This repository has been archived by the owner on Nov 3, 2023. It is now read-only.
/
authprovider.go
151 lines (135 loc) · 5.15 KB
/
authprovider.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (C) 2020 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0
package kubernetes
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/vmware-tanzu/buildkit-cli-for-kubectl/pkg/imagetools"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (d *Driver) GetAuthProvider(secretName string, stderr io.Writer) session.Attachable {
if secretName == "" {
secretName = buildxNameToDeploymentName(d.InitConfig.Name)
}
return &authProvider{
driver: d,
name: secretName,
}
}
func (d *Driver) GetAuthHintMessage() string {
return d.authHintMessage
}
type authProvider struct {
driver *Driver
name string
secret *corev1.Secret
// softFailure bool
}
func (ap *authProvider) GetAuthConfig(registryHostname string) (imagetools.AuthConfig, error) {
return imagetools.AuthConfig{}, fmt.Errorf("GetAuthConfig not yet implemented for kube secrets")
}
func (ap *authProvider) Register(server *grpc.Server) {
auth.RegisterAuthServer(server, ap)
}
type creds struct {
Username string `json:"username"`
Password string `json:"password"`
Auth string `json:"auth"`
IdentityToken string `json:"identitytoken"`
}
type credStore struct {
Auths map[string]creds `json:"auths"`
}
func (ap *authProvider) Credentials(ctx context.Context, req *auth.CredentialsRequest) (*auth.CredentialsResponse, error) {
registries := credStore{}
res := &auth.CredentialsResponse{}
if req.Host == "registry-1.docker.io" {
req.Host = "https://index.docker.io/v1/"
}
if ap.secret == nil {
// For secret lookup calls, we don't hard-fail, but record a hint message in case the entire build fails
// This avoids causing problems for local builds (non-push) based on public images (allowing anonymous operation)
secret, err := ap.driver.secretClient.Get(ctx, ap.name, metav1.GetOptions{})
if err != nil {
if kubeerrors.IsNotFound(err) {
ap.driver.authHintMessage = fmt.Sprintf("unable to find secret \"%s\" - if you used a different name specify with --registry-secret - if you haven't created a secret yet follow these instructions https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/", ap.name)
return res, nil
}
ap.driver.authHintMessage = fmt.Sprintf("failed to lookup secret \"%s\": %s", ap.name, err)
return res, nil
}
ap.secret = secret
}
// Make sure the secret is a properly formatted registry secret
data, ok := ap.secret.Data[".dockerconfigjson"]
if !ok {
return nil, fmt.Errorf("malformed kubernetes registry secret - missing '.dockerconfigjson' data key")
}
err := json.Unmarshal(data, ®istries)
if err != nil {
return nil, fmt.Errorf("malformed kubernetes registry secret - '.dockerconfigjson' didn't contain valid cred store: %w", err)
}
creds, found := registries.Auths[req.Host] // TODO this code needs work...
if found {
if (creds.Username == "" || creds.Password == "") && creds.Auth != "" {
creds.Username, creds.Password, err = decodeAuth(creds.Auth)
if err != nil {
return nil, fmt.Errorf("malformed kubernetes registry secret - failed to decode auth %w", err)
}
}
} else { // TODO remove this extra debugging once things are sorted out...
logrus.Infof("no credentials found for registry %s (proceeding with anonymous auth)", req.Host)
}
if creds.IdentityToken != "" {
res.Secret = creds.IdentityToken
} else {
res.Username = creds.Username
res.Secret = creds.Password
}
return res, nil
}
// TODO - to actually implement these properly, use buildkit/session/autrh/authprovider/authprovider.go for inspiration
func (ap *authProvider) FetchToken(context.Context, *auth.FetchTokenRequest) (*auth.FetchTokenResponse, error) {
return nil, status.Errorf(codes.Unavailable, "client side tokens not yet implemented")
}
func (ap *authProvider) GetTokenAuthority(context.Context, *auth.GetTokenAuthorityRequest) (*auth.GetTokenAuthorityResponse, error) {
return nil, status.Errorf(codes.Unavailable, "client side tokens not yet implemented")
}
func (ap *authProvider) VerifyTokenAuthority(context.Context, *auth.VerifyTokenAuthorityRequest) (*auth.VerifyTokenAuthorityResponse, error) {
return nil, status.Errorf(codes.Unavailable, "client side tokens not yet implemented")
}
// decodeAuth decodes a base64 encoded string and returns username and password
func decodeAuth(authStr string) (string, string, error) {
if authStr == "" {
return "", "", nil
}
decLen := base64.StdEncoding.DecodedLen(len(authStr))
decoded := make([]byte, decLen)
authByte := []byte(authStr)
n, err := base64.StdEncoding.Decode(decoded, authByte)
if err != nil {
return "", "", err
}
if n > decLen {
return "", "", errors.Errorf("Something went wrong decoding auth config")
}
arr := strings.SplitN(string(decoded), ":", 2)
if len(arr) != 2 {
return "", "", errors.Errorf("Invalid auth configuration file")
}
password := strings.Trim(arr[1], "\x00")
return arr[0], password, nil
}