diff --git a/cmd/buildkitd/config.go b/cmd/buildkitd/config.go new file mode 100644 index 000000000000..bdab9d712a14 --- /dev/null +++ b/cmd/buildkitd/config.go @@ -0,0 +1,31 @@ +package main + +import ( + "io" + "os" + + "github.com/BurntSushi/toml" + "github.com/moby/buildkit/cmd/buildkitd/config" + "github.com/pkg/errors" +) + +func Load(r io.Reader) (config.Config, *toml.MetaData, error) { + var c config.Config + md, err := toml.DecodeReader(r, &c) + if err != nil { + return c, nil, errors.Wrap(err, "failed to parse config") + } + return c, &md, nil +} + +func LoadFile(fp string) (config.Config, *toml.MetaData, error) { + f, err := os.Open(fp) + if err != nil { + if os.IsNotExist(err) { + return config.Config{}, nil, nil + } + return config.Config{}, nil, errors.Wrapf(err, "failed to load config from %s", fp) + } + defer f.Close() + return Load(f) +} diff --git a/cmd/buildkitd/config/config.go b/cmd/buildkitd/config/config.go index 6576ad257746..fc2da18fcc2f 100644 --- a/cmd/buildkitd/config/config.go +++ b/cmd/buildkitd/config/config.go @@ -1,13 +1,5 @@ package config -import ( - "io" - "os" - - "github.com/BurntSushi/toml" - "github.com/pkg/errors" -) - // Config provides containerd configuration data for the server type Config struct { Debug bool `toml:"debug"` @@ -42,9 +34,17 @@ type GRPCConfig struct { } type RegistryConfig struct { - Mirrors []string `toml:"mirrors"` - PlainHTTP *bool `toml:"http"` - Insecure *bool `toml:"insecure"` + Mirrors []string `toml:"mirrors"` + PlainHTTP *bool `toml:"http"` + Insecure *bool `toml:"insecure"` + RootCAs []string `toml:"ca"` + KeyPairs []TLSKeyPair `toml:"keypair"` + TLSConfigDir []string `toml:"tlsconfigdir"` +} + +type TLSKeyPair struct { + Key string `toml:"key"` + Certificate string `toml:"cert"` } type TLSConfig struct { @@ -103,24 +103,3 @@ type DNSConfig struct { Options []string `toml:"options"` SearchDomains []string `toml:"searchDomains"` } - -func Load(r io.Reader) (Config, *toml.MetaData, error) { - var c Config - md, err := toml.DecodeReader(r, &c) - if err != nil { - return c, nil, errors.Wrap(err, "failed to parse config") - } - return c, &md, nil -} - -func LoadFile(fp string) (Config, *toml.MetaData, error) { - f, err := os.Open(fp) - if err != nil { - if os.IsNotExist(err) { - return Config{}, nil, nil - } - return Config{}, nil, errors.Wrapf(err, "failed to load config from %s", fp) - } - defer f.Close() - return Load(f) -} diff --git a/cmd/buildkitd/config/config_test.go b/cmd/buildkitd/config_test.go similarity index 85% rename from cmd/buildkitd/config/config_test.go rename to cmd/buildkitd/config_test.go index 44c992281eec..12fd82de0fdb 100644 --- a/cmd/buildkitd/config/config_test.go +++ b/cmd/buildkitd/config_test.go @@ -1,4 +1,4 @@ -package config +package main import ( "bytes" @@ -51,6 +51,12 @@ keepDuration=7200 [registry."docker.io"] mirrors=["hub.docker.io"] http=true +insecure=true +ca=["myca.pem"] +tlsconfigdir=["/etc/buildkitd/myregistry"] +[[registry."docker.io".keypair]] +key="key.pem" +cert="cert.pem" [dns] nameservers=["1.1.1.1","8.8.8.8"] @@ -102,7 +108,12 @@ searchDomains=["example.com"] require.Equal(t, 0, len(cfg.Workers.Containerd.GCPolicy[1].Filters)) require.Equal(t, *cfg.Registries["docker.io"].PlainHTTP, true) + require.Equal(t, *cfg.Registries["docker.io"].Insecure, true) require.Equal(t, cfg.Registries["docker.io"].Mirrors[0], "hub.docker.io") + require.Equal(t, cfg.Registries["docker.io"].RootCAs, []string{"myca.pem"}) + require.Equal(t, cfg.Registries["docker.io"].TLSConfigDir, []string{"/etc/buildkitd/myregistry"}) + require.Equal(t, cfg.Registries["docker.io"].KeyPairs[0].Key, "key.pem") + require.Equal(t, cfg.Registries["docker.io"].KeyPairs[0].Certificate, "cert.pem") require.NotNil(t, cfg.DNS) require.Equal(t, cfg.DNS.Nameservers, []string{"1.1.1.1", "8.8.8.8"}) diff --git a/cmd/buildkitd/main.go b/cmd/buildkitd/main.go index da7cbcd5f5bf..42392da3e99b 100644 --- a/cmd/buildkitd/main.go +++ b/cmd/buildkitd/main.go @@ -182,7 +182,7 @@ func main() { ctx, cancel := context.WithCancel(appcontext.Context()) defer cancel() - cfg, md, err := config.LoadFile(c.GlobalString("config")) + cfg, md, err := LoadFile(c.GlobalString("config")) if err != nil { return err } @@ -337,7 +337,7 @@ func defaultConfigPath() string { } func defaultConf() (config.Config, *toml.MetaData, error) { - cfg, md, err := config.LoadFile(defaultConfigPath()) + cfg, md, err := LoadFile(defaultConfigPath()) if err != nil { if _, ok := errors.Cause(err).(*os.PathError); !ok { return config.Config{}, nil, err @@ -621,15 +621,7 @@ func newController(c *cli.Context, cfg *config.Config) (*control.Controller, err } func resolverFunc(cfg *config.Config) docker.RegistryHosts { - m := map[string]resolver.RegistryConf{} - for k, v := range cfg.Registries { - m[k] = resolver.RegistryConf{ - Mirrors: v.Mirrors, - PlainHTTP: v.PlainHTTP, - Insecure: v.Insecure, - } - } - return resolver.NewRegistryConfig(m) + return resolver.NewRegistryConfig(cfg.Registries) } func newWorkerController(c *cli.Context, wiOpt workerInitializerOpt) (*worker.Controller, error) { diff --git a/docs/buildkitd.toml.md b/docs/buildkitd.toml.md index bdf2cc7e575f..b3afc08b8741 100644 --- a/docs/buildkitd.toml.md +++ b/docs/buildkitd.toml.md @@ -83,4 +83,9 @@ insecure-entitlements = [ "network.host", "security.insecure" ] [registry."docker.io"] mirrors = ["hub.docker.io"] http = true + insecure = true + ca=["/etc/config/myca.pem"] + [[registry."docker.io".keypair]] + key="/etc/config/key.pem" + cert="/etc/config/cert.pem" ``` diff --git a/util/resolver/resolver.go b/util/resolver/resolver.go index 8779975cccbc..39134c2a9d9d 100644 --- a/util/resolver/resolver.go +++ b/util/resolver/resolver.go @@ -3,37 +3,109 @@ package resolver import ( "context" "crypto/tls" + "crypto/x509" + "io/ioutil" "net/http" + "os" + "path/filepath" + "runtime" + "strings" "time" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" + "github.com/moby/buildkit/cmd/buildkitd/config" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/auth" "github.com/moby/buildkit/util/tracing" + "github.com/pkg/errors" ) -type RegistryConf struct { - Mirrors []string - PlainHTTP *bool - Insecure *bool -} +func fillInsecureOpts(host string, c config.RegistryConfig, h *docker.RegistryHost) error { + tc, err := loadTLSConfig(c) + if err != nil { + return err + } -func fillInsecureOpts(host string, c RegistryConf, h *docker.RegistryHost) { if c.PlainHTTP != nil && *c.PlainHTTP { h.Scheme = "http" } else if c.Insecure != nil && *c.Insecure { - h.Client = &http.Client{ - Transport: tracing.NewTransport(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}), + if tc == nil { + tc = &tls.Config{} } + tc.InsecureSkipVerify = true } else if c.PlainHTTP == nil { if ok, _ := docker.MatchLocalhost(host); ok { h.Scheme = "http" } } + + if tc != nil && h.Scheme != "http" { + h.Client = &http.Client{ + Transport: tracing.NewTransport(&http.Transport{TLSClientConfig: tc}), + } + } + + return nil } -func NewRegistryConfig(m map[string]RegistryConf) docker.RegistryHosts { +func loadTLSConfig(c config.RegistryConfig) (*tls.Config, error) { + for _, d := range c.TLSConfigDir { + fs, err := ioutil.ReadDir(d) + if err != nil && !os.IsNotExist(err) && !os.IsPermission(err) { + return nil, errors.WithStack(err) + } + for _, f := range fs { + if strings.HasSuffix(f.Name(), ".crt") { + c.RootCAs = append(c.RootCAs, filepath.Join(d, f.Name())) + } + if strings.HasSuffix(f.Name(), ".cert") { + c.KeyPairs = append(c.KeyPairs, config.TLSKeyPair{ + Certificate: filepath.Join(d, f.Name()), + Key: filepath.Join(d, strings.TrimSuffix(f.Name(), ".cert")+".key"), + }) + } + } + } + + var tc *tls.Config + + if len(c.RootCAs) > 0 { + tc = &tls.Config{} + systemPool, err := x509.SystemCertPool() + if err != nil { + if runtime.GOOS == "windows" { + systemPool = x509.NewCertPool() + } else { + return nil, errors.Wrapf(err, "unable to get system cert pool") + } + } + tc.RootCAs = systemPool + } + + for _, p := range c.RootCAs { + dt, err := ioutil.ReadFile(p) + if err != nil { + return nil, errors.Wrapf(err, "failed to read %s", p) + } + tc.RootCAs.AppendCertsFromPEM(dt) + } + + for _, kp := range c.KeyPairs { + cert, err := tls.LoadX509KeyPair(kp.Certificate, kp.Key) + if err != nil { + return nil, errors.Wrapf(err, "failed to load keypair for %s", kp.Certificate) + } + if tc == nil { + tc = &tls.Config{} + } + tc.Certificates = append(tc.Certificates, cert) + } + + return tc, nil +} + +func NewRegistryConfig(m map[string]config.RegistryConfig) docker.RegistryHosts { return docker.Registries( func(host string) ([]docker.RegistryHost, error) { c, ok := m[host] @@ -51,7 +123,10 @@ func NewRegistryConfig(m map[string]RegistryConf) docker.RegistryHosts { Path: "/v2", Capabilities: docker.HostCapabilityPull | docker.HostCapabilityResolve, } - fillInsecureOpts(mirror, m[mirror], &h) + + if err := fillInsecureOpts(mirror, m[mirror], &h); err != nil { + return nil, err + } out = append(out, h) } @@ -67,7 +142,10 @@ func NewRegistryConfig(m map[string]RegistryConf) docker.RegistryHosts { Path: "/v2", Capabilities: docker.HostCapabilityPush | docker.HostCapabilityPull | docker.HostCapabilityResolve, } - fillInsecureOpts(host, c, &h) + + if err := fillInsecureOpts(host, c, &h); err != nil { + return nil, err + } out = append(out, h) return out, nil