Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

buildctl: Add configured TLS certificate to trust store when making calls to registry auth #4211

Merged
merged 1 commit into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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