package auth import ( "context" "crypto/subtle" "math/rand" "sync" "github.com/moby/buildkit/session" "github.com/moby/buildkit/util/grpcerrors" "github.com/pkg/errors" "golang.org/x/crypto/nacl/sign" "google.golang.org/grpc/codes" ) var salt []byte var saltOnce sync.Once // getSalt returns unique component per daemon restart to avoid persistent keys func getSalt() []byte { saltOnce.Do(func() { salt = make([]byte, 32) rand.Read(salt) }) return salt } func CredentialsFunc(sm *session.Manager, g session.Group) func(string) (session, username, secret string, err error) { return func(host string) (string, string, string, error) { var sessionID, user, secret string err := sm.Any(context.TODO(), g, func(ctx context.Context, id string, c session.Caller) error { client := NewAuthClient(c.Conn()) resp, err := client.Credentials(ctx, &CredentialsRequest{ Host: host, }) if err != nil { if grpcerrors.Code(err) == codes.Unimplemented { return nil } return err } sessionID = id user = resp.Username secret = resp.Secret return nil }) if err != nil { return "", "", "", err } return sessionID, user, secret, nil } } func FetchToken(req *FetchTokenRequest, sm *session.Manager, g session.Group) (resp *FetchTokenResponse, err error) { err = sm.Any(context.TODO(), g, func(ctx context.Context, id string, c session.Caller) error { client := NewAuthClient(c.Conn()) resp, err = client.FetchToken(ctx, req) if err != nil { return err } return nil }) if err != nil { return nil, err } return resp, nil } func VerifyTokenAuthority(host string, pubKey *[32]byte, sm *session.Manager, g session.Group) (sessionID string, ok bool, err error) { var verified bool err = sm.Any(context.TODO(), g, func(ctx context.Context, id string, c session.Caller) error { client := NewAuthClient(c.Conn()) payload := make([]byte, 32) rand.Read(payload) resp, err := client.VerifyTokenAuthority(ctx, &VerifyTokenAuthorityRequest{ Host: host, Salt: getSalt(), Payload: payload, }) if err != nil { if grpcerrors.Code(err) == codes.Unimplemented { return nil } return err } var dt []byte dt, ok = sign.Open(nil, resp.Signed, pubKey) if ok && subtle.ConstantTimeCompare(dt, payload) == 1 { verified = true } sessionID = id return nil }) if err != nil { return "", false, err } return sessionID, verified, nil } func GetTokenAuthority(host string, sm *session.Manager, g session.Group) (sessionID string, pubKey *[32]byte, err error) { err = sm.Any(context.TODO(), g, func(ctx context.Context, id string, c session.Caller) error { client := NewAuthClient(c.Conn()) resp, err := client.GetTokenAuthority(ctx, &GetTokenAuthorityRequest{ Host: host, Salt: getSalt(), }) if err != nil { if grpcerrors.Code(err) == codes.Unimplemented || grpcerrors.Code(err) == codes.Unavailable { return nil } return err } if len(resp.PublicKey) != 32 { return errors.Errorf("invalid pubkey length %d", len(pubKey)) } sessionID = id pubKey = new([32]byte) copy((*pubKey)[:], resp.PublicKey) return nil }) if err != nil { return "", nil, err } return sessionID, pubKey, nil }