Skip to content

Commit

Permalink
Merge pull request moby#4211 from njucjc/support-auth-tls
Browse files Browse the repository at this point in the history
buildctl: Add configured TLS certificate to trust store when making calls to registry auth
  • Loading branch information
tonistiigi authored Sep 16, 2023
2 parents 3551ef6 + 2bd8d76 commit 7589637
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 24 deletions.
10 changes: 9 additions & 1 deletion cmd/buildctl/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ var buildCommand = cli.Command{
Name: "ref-file",
Usage: "Write build ref to a file",
},
cli.StringSliceFlag{
Name: "registry-auth-tlscontext",
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",
},
},
}

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

dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig)}
tlsConfigs, err := build.ParseRegistryAuthTLSContext(clicontext.StringSlice("registry-auth-tlscontext"))
if err != nil {
return err
}
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig, tlsConfigs)}

if ssh := clicontext.StringSlice("ssh"); len(ssh) > 0 {
configs, err := build.ParseSSH(ssh)
Expand Down
84 changes: 84 additions & 0 deletions cmd/buildctl/build/registryauthtlscontext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package build

import (
"encoding/csv"
"strings"

"github.com/moby/buildkit/session/auth/authprovider"
"github.com/pkg/errors"
)

type authTLSContextEntry struct {
Host string
CA string
Cert string
Key string
}

func parseRegistryAuthTLSContextCSV(s string) (authTLSContextEntry, error) {
authTLSContext := authTLSContextEntry{}
csvReader := csv.NewReader(strings.NewReader(s))
fields, err := csvReader.Read()
if err != nil {
return authTLSContext, err
}
for _, field := range fields {
key, value, ok := strings.Cut(field, "=")
if !ok {
return authTLSContext, errors.Errorf("invalid value %s", field)
}
key = strings.ToLower(key)
switch key {
case "host":
authTLSContext.Host = value
case "ca":
authTLSContext.CA = value
case "cert":
authTLSContext.Cert = value
case "key":
authTLSContext.Key = value
}
}
if authTLSContext.Host == "" {
return authTLSContext, errors.New("--registry-auth-tlscontext requires host=<host>")
}
if authTLSContext.CA == "" {
if authTLSContext.Cert == "" || authTLSContext.Key == "" {
return authTLSContext, errors.New("--registry-auth-tlscontext requires ca=<ca> or cert=<cert>,key=<key>")
}
} else {
if (authTLSContext.Cert != "" && authTLSContext.Key == "") || (authTLSContext.Cert == "" && authTLSContext.Key != "") {
return authTLSContext, errors.New("--registry-auth-tlscontext requires cert=<cert>,key=<key>")
}
}
return authTLSContext, nil
}

func ParseRegistryAuthTLSContext(registryAuthTLSContext []string) (map[string]*authprovider.AuthTLSConfig, error) {
var tlsContexts []authTLSContextEntry
for _, c := range registryAuthTLSContext {
authTLSContext, err := parseRegistryAuthTLSContextCSV(c)
if err != nil {
return nil, err
}
tlsContexts = append(tlsContexts, authTLSContext)
}

authConfigs := make(map[string]*authprovider.AuthTLSConfig)
for _, c := range tlsContexts {
_, ok := authConfigs[c.Host]
if !ok {
authConfigs[c.Host] = &authprovider.AuthTLSConfig{}
}
if c.CA != "" {
authConfigs[c.Host].RootCAs = append(authConfigs[c.Host].RootCAs, c.CA)
}
if c.Cert != "" && c.Key != "" {
authConfigs[c.Host].KeyPairs = append(authConfigs[c.Host].KeyPairs, authprovider.TLSKeyPair{
Key: c.Key,
Certificate: c.Cert,
})
}
}
return authConfigs, nil
}
109 changes: 109 additions & 0 deletions cmd/buildctl/build/registryauthtlscontext_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package build

import (
"testing"

"github.com/moby/buildkit/session/auth/authprovider"
"github.com/stretchr/testify/require"
)

func TestParseRegistryAuthTLSContext(t *testing.T) {
type testCase struct {
registryAuthTLSContext []string //--registry-auth-tlscontext
expected map[string]*authprovider.AuthTLSConfig
expectedErr string
}
testCases := []testCase{
{
registryAuthTLSContext: []string{
"host=tcp://myserver:2376,ca=/home/admin/ca-file,cert=/home/admin/cert-file,key=/home/admin/key-file",
},
expected: map[string]*authprovider.AuthTLSConfig{
"tcp://myserver:2376": {
RootCAs: []string{
"/home/admin/ca-file",
},
KeyPairs: []authprovider.TLSKeyPair{
{
Key: "/home/admin/key-file",
Certificate: "/home/admin/cert-file",
},
},
},
},
},
{
registryAuthTLSContext: []string{
"host=tcp://myserver:2376,cert=/home/admin/cert-file,key=/home/admin/key-file",
},
expected: map[string]*authprovider.AuthTLSConfig{
"tcp://myserver:2376": {
KeyPairs: []authprovider.TLSKeyPair{
{
Key: "/home/admin/key-file",
Certificate: "/home/admin/cert-file",
},
},
},
},
},
{
registryAuthTLSContext: []string{
"host=tcp://myserver:2376,ca=/home/admin/ca-file",
},
expected: map[string]*authprovider.AuthTLSConfig{
"tcp://myserver:2376": {
RootCAs: []string{
"/home/admin/ca-file",
},
},
},
},
{
registryAuthTLSContext: []string{
"host=tcp://myserver:2376,ca=/home/admin/ca-file,key=/home/admin/key-file",
},
expectedErr: "--registry-auth-tlscontext requires cert=<cert>,key=<key>",
},
{
registryAuthTLSContext: []string{
"host=tcp://myserver:2376,ca=/home/admin/ca-file,cert=/home/admin/cert-file,key=/home/admin/key-file",
"host=https://myserver:2376,ca=/path/to/my/ca.crt,cert=/path/to/my/cert.crt,key=/path/to/my/key.crt",
},
expected: map[string]*authprovider.AuthTLSConfig{
"tcp://myserver:2376": {
RootCAs: []string{
"/home/admin/ca-file",
},
KeyPairs: []authprovider.TLSKeyPair{
{
Key: "/home/admin/key-file",
Certificate: "/home/admin/cert-file",
},
},
},
"https://myserver:2376": {
RootCAs: []string{
"/path/to/my/ca.crt",
},
KeyPairs: []authprovider.TLSKeyPair{
{
Key: "/path/to/my/key.crt",
Certificate: "/path/to/my/cert.crt",
},
},
},
},
},
}

for _, tc := range testCases {
im, err := ParseRegistryAuthTLSContext(tc.registryAuthTLSContext)
if tc.expectedErr == "" {
require.EqualValues(t, tc.expected, im)
} else {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
}
}
}
33 changes: 17 additions & 16 deletions docs/reference/buildctl.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,23 @@ USAGE:
OPTIONS:
--output value, -o value Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true
--progress value Set type of progress (auto, plain, tty, rawjson). Use plain to show container output (default: "auto")
--trace value Path to trace file. Defaults to no tracing.
--local value Allow build access to the local directory
--oci-layout value Allow build access to the local OCI layout
--frontend value Define frontend used for build
--opt value Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar
--no-cache Disable cache for all the vertices
--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
--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
--secret value Secret value exposed to the build. Format id=secretname,src=filepath
--allow value Allow extra privileged entitlement, e.g. network.host, security.insecure
--ssh value Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]
--metadata-file value Output build metadata (e.g., image digest) to a file as JSON
--source-policy-file value Read source policy file from a JSON file
--ref-file value Write build ref to a file
--output value, -o value Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true
--progress value Set type of progress (auto, plain, tty, rawjson). Use plain to show container output (default: "auto")
--trace value Path to trace file. Defaults to no tracing.
--local value Allow build access to the local directory
--oci-layout value Allow build access to the local OCI layout
--frontend value Define frontend used for build
--opt value Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar
--no-cache Disable cache for all the vertices
--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
--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
--secret value Secret value exposed to the build. Format id=secretname,src=filepath
--allow value Allow extra privileged entitlement, e.g. network.host, security.insecure
--ssh value Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]
--metadata-file value Output build metadata (e.g., image digest) to a file as JSON
--source-policy-file value Read source policy file from a JSON file
--ref-file value Write build ref to a file
--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
```
<!---GENERATE_END-->
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ require (
github.com/google/go-cmp v0.5.9
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-immutable-radix v1.3.1
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/golang-lru v0.5.4
Expand Down Expand Up @@ -136,7 +137,6 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect
github.com/hanwen/go-fuse/v2 v2.2.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
Expand Down
11 changes: 11 additions & 0 deletions session/auth/authprovider/authconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package authprovider

type AuthTLSConfig struct {
RootCAs []string
KeyPairs []TLSKeyPair
}

type TLSKeyPair struct {
Key string
Certificate string
}
Loading

0 comments on commit 7589637

Please sign in to comment.