package main import ( "context" "crypto/tls" "fmt" "regexp" "strings" "github.com/golang/glog" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ldap "gopkg.in/ldap.v3" pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto" ) var ( reUsername = regexp.MustCompile(`^[a-zA-Z0-9_\.]+$`) ) func (p *prodvider) Authenticate(ctx context.Context, req *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error) { username := strings.TrimSpace(req.Username) if username == "" || !reUsername.MatchString(username) { return nil, status.Error(codes.InvalidArgument, "invalid username") } password := req.Password if password == "" { return &pb.AuthenticateResponse{ Result: pb.AuthenticateResponse_RESULT_INVALID_CREDENTIALS, }, nil } tlsConfig := &tls.Config{} lconn, err := ldap.DialTLS("tcp", flagLDAPServer, tlsConfig) if err != nil { glog.Errorf("ldap.DialTLS: %v", err) return nil, status.Error(codes.Unavailable, "could not context LDAP") } defer lconn.Close() dn := fmt.Sprintf(flagLDAPBindDN, username) err = lconn.Bind(dn, password) if err != nil { if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) { return &pb.AuthenticateResponse{ Result: pb.AuthenticateResponse_RESULT_INVALID_CREDENTIALS, }, nil } glog.Errorf("ldap.Bind: %v", err) return nil, status.Error(codes.Unavailable, "could not query LDAP") } groups, err := p.groupMemberships(lconn, username) if err != nil { return nil, err } if !groups["kubernetes-users"] && !groups["staff"] { return nil, status.Error(codes.PermissionDenied, "not part of staff or kubernetes-users") } err = p.kubernetesSetupUser(ctx, username) if err != nil { glog.Errorf("kubernetesSetupUser(%v): %v", username, err) return nil, status.Error(codes.Unavailable, "could not set up objects in Kubernetes") } kubernetesKeys, err := p.kubernetesCreds(username) if err != nil { glog.Errorf("kubernetesCreds(%q): %v", username, err) return nil, status.Error(codes.Unavailable, "could not generate k8s keys") } hspkiKeys, err := p.hspkiCreds(ctx, username) if err != nil { glog.Errorf("hspkiCreds(%q): %v", username, err) return nil, status.Error(codes.Unavailable, "could not generate hspki keys") } crdbWaw1Keys, err := p.crdbCreds(ctx, username, "waw1") if err != nil { glog.Errorf("crdbCreds(%q): %v", username, err) return nil, status.Error(codes.Unavailable, "could not generate crdb keys") } return &pb.AuthenticateResponse{ Result: pb.AuthenticateResponse_RESULT_AUTHENTICATED, KubernetesKeys: kubernetesKeys, HspkiKeys: hspkiKeys, CrdbKeys: &pb.CockroachDBKeys{ Clusters: []*pb.CockroachDBKeys_Cluster{crdbWaw1Keys}, }, }, nil } func (p *prodvider) groupMemberships(lconn *ldap.Conn, username string) (map[string]bool, error) { searchRequest := ldap.NewSearchRequest( flagLDAPGroupSearchBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, fmt.Sprintf("(uniqueMember=%s)", fmt.Sprintf(flagLDAPBindDN, username)), []string{"dn", "cn"}, nil, ) sr, err := lconn.Search(searchRequest) if err != nil { glog.Errorf("ldap.Search: %v", err) return nil, status.Error(codes.Unavailable, "could not query LDAP for group") } res := make(map[string]bool) for _, entry := range sr.Entries { cn := entry.GetAttributeValue("cn") res[cn] = true } return res, nil }