Skip to content

Commit 2319c65

Browse files
njucjciQQBot
authored andcommitted
buildctl: Add configured TLS certificate to trust store when making calls to registry auth
Signed-off-by: njucjc <[email protected]>
1 parent aecbdee commit 2319c65

File tree

8 files changed

+288
-24
lines changed

8 files changed

+288
-24
lines changed

cmd/buildctl/build.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ var buildCommand = cli.Command{
105105
Name: "ref-file",
106106
Usage: "Write build ref to a file",
107107
},
108+
cli.StringSliceFlag{
109+
Name: "registry-auth-tlscontext",
110+
Usage: "Overwrite TLS configuration when authenticating with registries, e.g. --registry-auth-tlscontext host=https://myserver:2376,ca=/path/to/my/ca.crt,cert=/path/to/my/cert.crt,key=/path/to/my/key.crt",
111+
},
108112
},
109113
}
110114

@@ -158,7 +162,11 @@ func buildAction(clicontext *cli.Context) error {
158162
}
159163

160164
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
161-
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig)}
165+
tlsConfigs, err := build.ParseRegistryAuthTLSContext(clicontext.StringSlice("registry-auth-tlscontext"))
166+
if err != nil {
167+
return err
168+
}
169+
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig, tlsConfigs)}
162170

163171
if ssh := clicontext.StringSlice("ssh"); len(ssh) > 0 {
164172
configs, err := build.ParseSSH(ssh)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package build
2+
3+
import (
4+
"encoding/csv"
5+
"strings"
6+
7+
"github.com/moby/buildkit/session/auth/authprovider"
8+
"github.com/pkg/errors"
9+
)
10+
11+
type authTLSContextEntry struct {
12+
Host string
13+
CA string
14+
Cert string
15+
Key string
16+
}
17+
18+
func parseRegistryAuthTLSContextCSV(s string) (authTLSContextEntry, error) {
19+
authTLSContext := authTLSContextEntry{}
20+
csvReader := csv.NewReader(strings.NewReader(s))
21+
fields, err := csvReader.Read()
22+
if err != nil {
23+
return authTLSContext, err
24+
}
25+
for _, field := range fields {
26+
key, value, ok := strings.Cut(field, "=")
27+
if !ok {
28+
return authTLSContext, errors.Errorf("invalid value %s", field)
29+
}
30+
key = strings.ToLower(key)
31+
switch key {
32+
case "host":
33+
authTLSContext.Host = value
34+
case "ca":
35+
authTLSContext.CA = value
36+
case "cert":
37+
authTLSContext.Cert = value
38+
case "key":
39+
authTLSContext.Key = value
40+
}
41+
}
42+
if authTLSContext.Host == "" {
43+
return authTLSContext, errors.New("--registry-auth-tlscontext requires host=<host>")
44+
}
45+
if authTLSContext.CA == "" {
46+
if authTLSContext.Cert == "" || authTLSContext.Key == "" {
47+
return authTLSContext, errors.New("--registry-auth-tlscontext requires ca=<ca> or cert=<cert>,key=<key>")
48+
}
49+
} else {
50+
if (authTLSContext.Cert != "" && authTLSContext.Key == "") || (authTLSContext.Cert == "" && authTLSContext.Key != "") {
51+
return authTLSContext, errors.New("--registry-auth-tlscontext requires cert=<cert>,key=<key>")
52+
}
53+
}
54+
return authTLSContext, nil
55+
}
56+
57+
func ParseRegistryAuthTLSContext(registryAuthTLSContext []string) (map[string]*authprovider.AuthTLSConfig, error) {
58+
var tlsContexts []authTLSContextEntry
59+
for _, c := range registryAuthTLSContext {
60+
authTLSContext, err := parseRegistryAuthTLSContextCSV(c)
61+
if err != nil {
62+
return nil, err
63+
}
64+
tlsContexts = append(tlsContexts, authTLSContext)
65+
}
66+
67+
authConfigs := make(map[string]*authprovider.AuthTLSConfig)
68+
for _, c := range tlsContexts {
69+
_, ok := authConfigs[c.Host]
70+
if !ok {
71+
authConfigs[c.Host] = &authprovider.AuthTLSConfig{}
72+
}
73+
if c.CA != "" {
74+
authConfigs[c.Host].RootCAs = append(authConfigs[c.Host].RootCAs, c.CA)
75+
}
76+
if c.Cert != "" && c.Key != "" {
77+
authConfigs[c.Host].KeyPairs = append(authConfigs[c.Host].KeyPairs, authprovider.TLSKeyPair{
78+
Key: c.Key,
79+
Certificate: c.Cert,
80+
})
81+
}
82+
}
83+
return authConfigs, nil
84+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package build
2+
3+
import (
4+
"testing"
5+
6+
"github.com/moby/buildkit/session/auth/authprovider"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestParseRegistryAuthTLSContext(t *testing.T) {
11+
type testCase struct {
12+
registryAuthTLSContext []string //--registry-auth-tlscontext
13+
expected map[string]*authprovider.AuthTLSConfig
14+
expectedErr string
15+
}
16+
testCases := []testCase{
17+
{
18+
registryAuthTLSContext: []string{
19+
"host=tcp://myserver:2376,ca=/home/admin/ca-file,cert=/home/admin/cert-file,key=/home/admin/key-file",
20+
},
21+
expected: map[string]*authprovider.AuthTLSConfig{
22+
"tcp://myserver:2376": {
23+
RootCAs: []string{
24+
"/home/admin/ca-file",
25+
},
26+
KeyPairs: []authprovider.TLSKeyPair{
27+
{
28+
Key: "/home/admin/key-file",
29+
Certificate: "/home/admin/cert-file",
30+
},
31+
},
32+
},
33+
},
34+
},
35+
{
36+
registryAuthTLSContext: []string{
37+
"host=tcp://myserver:2376,cert=/home/admin/cert-file,key=/home/admin/key-file",
38+
},
39+
expected: map[string]*authprovider.AuthTLSConfig{
40+
"tcp://myserver:2376": {
41+
KeyPairs: []authprovider.TLSKeyPair{
42+
{
43+
Key: "/home/admin/key-file",
44+
Certificate: "/home/admin/cert-file",
45+
},
46+
},
47+
},
48+
},
49+
},
50+
{
51+
registryAuthTLSContext: []string{
52+
"host=tcp://myserver:2376,ca=/home/admin/ca-file",
53+
},
54+
expected: map[string]*authprovider.AuthTLSConfig{
55+
"tcp://myserver:2376": {
56+
RootCAs: []string{
57+
"/home/admin/ca-file",
58+
},
59+
},
60+
},
61+
},
62+
{
63+
registryAuthTLSContext: []string{
64+
"host=tcp://myserver:2376,ca=/home/admin/ca-file,key=/home/admin/key-file",
65+
},
66+
expectedErr: "--registry-auth-tlscontext requires cert=<cert>,key=<key>",
67+
},
68+
{
69+
registryAuthTLSContext: []string{
70+
"host=tcp://myserver:2376,ca=/home/admin/ca-file,cert=/home/admin/cert-file,key=/home/admin/key-file",
71+
"host=https://myserver:2376,ca=/path/to/my/ca.crt,cert=/path/to/my/cert.crt,key=/path/to/my/key.crt",
72+
},
73+
expected: map[string]*authprovider.AuthTLSConfig{
74+
"tcp://myserver:2376": {
75+
RootCAs: []string{
76+
"/home/admin/ca-file",
77+
},
78+
KeyPairs: []authprovider.TLSKeyPair{
79+
{
80+
Key: "/home/admin/key-file",
81+
Certificate: "/home/admin/cert-file",
82+
},
83+
},
84+
},
85+
"https://myserver:2376": {
86+
RootCAs: []string{
87+
"/path/to/my/ca.crt",
88+
},
89+
KeyPairs: []authprovider.TLSKeyPair{
90+
{
91+
Key: "/path/to/my/key.crt",
92+
Certificate: "/path/to/my/cert.crt",
93+
},
94+
},
95+
},
96+
},
97+
},
98+
}
99+
100+
for _, tc := range testCases {
101+
im, err := ParseRegistryAuthTLSContext(tc.registryAuthTLSContext)
102+
if tc.expectedErr == "" {
103+
require.EqualValues(t, tc.expected, im)
104+
} else {
105+
require.Error(t, err)
106+
require.Contains(t, err.Error(), tc.expectedErr)
107+
}
108+
}
109+
}

docs/reference/buildctl.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,23 @@ USAGE:
6262
6363
6464
OPTIONS:
65-
--output value, -o value Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true
66-
--progress value Set type of progress (auto, plain, tty, rawjson). Use plain to show container output (default: "auto")
67-
--trace value Path to trace file. Defaults to no tracing.
68-
--local value Allow build access to the local directory
69-
--oci-layout value Allow build access to the local OCI layout
70-
--frontend value Define frontend used for build
71-
--opt value Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar
72-
--no-cache Disable cache for all the vertices
73-
--export-cache value Export build cache, e.g. --export-cache type=registry,ref=example.com/foo/bar, or --export-cache type=local,dest=path/to/dir
74-
--import-cache value Import build cache, e.g. --import-cache type=registry,ref=example.com/foo/bar, or --import-cache type=local,src=path/to/dir
75-
--secret value Secret value exposed to the build. Format id=secretname,src=filepath
76-
--allow value Allow extra privileged entitlement, e.g. network.host, security.insecure
77-
--ssh value Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]
78-
--metadata-file value Output build metadata (e.g., image digest) to a file as JSON
79-
--source-policy-file value Read source policy file from a JSON file
80-
--ref-file value Write build ref to a file
65+
--output value, -o value Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true
66+
--progress value Set type of progress (auto, plain, tty, rawjson). Use plain to show container output (default: "auto")
67+
--trace value Path to trace file. Defaults to no tracing.
68+
--local value Allow build access to the local directory
69+
--oci-layout value Allow build access to the local OCI layout
70+
--frontend value Define frontend used for build
71+
--opt value Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar
72+
--no-cache Disable cache for all the vertices
73+
--export-cache value Export build cache, e.g. --export-cache type=registry,ref=example.com/foo/bar, or --export-cache type=local,dest=path/to/dir
74+
--import-cache value Import build cache, e.g. --import-cache type=registry,ref=example.com/foo/bar, or --import-cache type=local,src=path/to/dir
75+
--secret value Secret value exposed to the build. Format id=secretname,src=filepath
76+
--allow value Allow extra privileged entitlement, e.g. network.host, security.insecure
77+
--ssh value Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]
78+
--metadata-file value Output build metadata (e.g., image digest) to a file as JSON
79+
--source-policy-file value Read source policy file from a JSON file
80+
--ref-file value Write build ref to a file
81+
--registry-auth-tlscontext value Overwrite TLS configuration when authenticating with registries, e.g. --registry-auth-tlscontext host=https://myserver:2376,ca=/path/to/my/ca.crt,cert=/path/to/my/cert.crt,key=/path/to/my/key.crt
8182
8283
```
8384
<!---GENERATE_END-->

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ require (
3838
github.com/google/go-cmp v0.5.9
3939
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
4040
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
41+
github.com/hashicorp/go-cleanhttp v0.5.2
4142
github.com/hashicorp/go-immutable-radix v1.3.1
4243
github.com/hashicorp/go-multierror v1.1.1
4344
github.com/hashicorp/golang-lru v0.5.4
@@ -136,7 +137,6 @@ require (
136137
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect
137138
github.com/hanwen/go-fuse/v2 v2.2.0 // indirect
138139
github.com/hashicorp/errwrap v1.1.0 // indirect
139-
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
140140
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
141141
github.com/jmespath/go-jmespath v0.4.0 // indirect
142142
github.com/kylelemons/godebug v1.1.0 // indirect
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package authprovider
2+
3+
type AuthTLSConfig struct {
4+
RootCAs []string
5+
KeyPairs []TLSKeyPair
6+
}
7+
8+
type TLSKeyPair struct {
9+
Key string
10+
Certificate string
11+
}

0 commit comments

Comments
 (0)