blob: 21b5f5726cb30be805adda85dfd1b1af820e0524 [file] [log] [blame]
package main
import (
"context"
"crypto/tls"
"fmt"
"regexp"
"strings"
ldap "github.com/go-ldap/ldap/v3"
"github.com/golang/glog"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
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
}