Skip to content

Commit

Permalink
Merge pull request containerd#1144 from ungureanuvladvictor/vladu/tls…
Browse files Browse the repository at this point in the history
…-auth

Add TLS auth registry support
  • Loading branch information
Random-Liu authored Jun 6, 2019
2 parents 0ab79ff + 60a58af commit 0a89a04
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 2 deletions.
16 changes: 16 additions & 0 deletions docs/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ from a registry, containerd will try these endpoint URLs one by one, and use the

After modify this config, you need restart the `containerd` service.

## Configure Registry TLS Communication
`cri` plugin also supports configuring TLS settings when communicating with a registry.

To configure the TLS settings for a specific registry, create/modify the `/ec/containerd/config.toml` as follows:
```toml
[plugins.cri.registry.tls_configs]
[plugins.cri.registry.tls_configs."my.custom.registry"]
ca_file = "ca.pem"
cert_file = "cert.pem"
key_file = "key.pem"
```

In the config example shown above, TLS mutual authentication will be used for communications with the registry endpoint located at https://my.custom.registry.
`ca_file` is file name of the certificate authority (CA) certificate used to authenticate the x509 certificate/key pair specified by the files respectively pointed to by `cert_file` and `key_file`.


## Configure Registry Credentials

`cri` plugin also supports docker like registry credential config.
Expand Down
10 changes: 10 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,23 @@ type AuthConfig struct {
IdentityToken string `toml:"identitytoken" json:"identitytoken"`
}

// TLSConfig contains the CA/Cert/Key used for a registry
type TLSConfig struct {
CAFile string `toml:"ca_file" json:"caFile"`
CertFile string `toml:"cert_file" json:"certFile"`
KeyFile string `toml:"key_file" json:"keyFile"`
}

// Registry is registry settings configured
type Registry struct {
// Mirrors are namespace to mirror mapping for all namespaces.
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors"`
// Auths are registry endpoint to auth config mapping. The registry endpoint must
// be a valid url with host specified.
Auths map[string]AuthConfig `toml:"auths" json:"auths"`
// TLSConfigs are pairs of CA/Cert/Key which then are used when creating the transport
// that communicates with the registry.
TLSConfigs map[string]TLSConfig `toml:"tls_configs" json:"tlsConfigs"`
}

// PluginConfig contains toml config related to CRI plugin,
Expand Down
60 changes: 58 additions & 2 deletions pkg/server/image_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ limitations under the License.
package server

import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"io/ioutil"
"net/http"
"net/url"
"strings"
Expand All @@ -28,6 +31,7 @@ import (
"github.com/containerd/containerd/reference"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/containerd/cri/pkg/config"
distribution "github.com/docker/distribution/reference"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
Expand Down Expand Up @@ -246,22 +250,61 @@ func (c *criService) credentials(auth *runtime.AuthConfig) func(string) (string,
}
}

// getRegistryTLSTransport returns a http.Transport configured with a CA/Cert/Key specified by registryTLSConfig
func (c *criService) getRegistryTLSTransport(ctx context.Context, registryTLSConfig config.TLSConfig) (*http.Transport, error) {
cert, err := tls.LoadX509KeyPair(registryTLSConfig.CertFile, registryTLSConfig.KeyFile)
if err != nil {
return nil, errors.Wrap(err, "failed to load cert file")
}

caCertPool, err := x509.SystemCertPool()
if err != nil {
return nil, errors.Wrap(err, "failed to get system cert pool")
}
caCert, err := ioutil.ReadFile(registryTLSConfig.CAFile)
if err != nil {
return nil, errors.Wrap(err, "failed to load CA file")
}
caCertPool.AppendCertsFromPEM(caCert)

tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
tlsConfig.BuildNameToCertificate()
transport := &http.Transport{TLSClientConfig: tlsConfig}

return transport, nil
}

// getResolver tries registry mirrors and the default registry, and returns the resolver and descriptor
// from the first working registry.
func (c *criService) getResolver(ctx context.Context, ref string, cred func(string) (string, string, error)) (remotes.Resolver, imagespec.Descriptor, error) {
refspec, err := reference.Parse(ref)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrap(err, "parse image reference")
}

httpClient := http.DefaultClient

// Try mirrors in order first, and then try default host name.
for _, e := range c.config.Registry.Mirrors[refspec.Hostname()].Endpoints {
u, err := url.Parse(e)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "parse registry endpoint %q", e)
}

if registryTLSConfig, ok := c.config.Registry.TLSConfigs[u.Host]; ok {
tlsTransport, err := c.getRegistryTLSTransport(ctx, registryTLSConfig)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "get tlsTransport for registry %q", refspec.Hostname())
}
httpClient = &http.Client{Transport: tlsTransport}
}

resolver := docker.NewResolver(docker.ResolverOptions{
Authorizer: docker.NewAuthorizer(http.DefaultClient, cred),
Client: http.DefaultClient,
Client: httpClient,
Host: func(string) (string, error) { return u.Host, nil },
// By default use "https".
PlainHTTP: u.Scheme == "http",
Expand All @@ -273,9 +316,22 @@ func (c *criService) getResolver(ctx context.Context, ref string, cred func(stri
logrus.WithError(err).Debugf("Tried registry mirror %q but failed", e)
// Continue to try next endpoint
}

hostname, err := docker.DefaultHost(refspec.Hostname())
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "get host for refspec %q", refspec.Hostname())
}
if registryTLSConfig, ok := c.config.Registry.TLSConfigs[hostname]; ok {
tlsTransport, err := c.getRegistryTLSTransport(ctx, registryTLSConfig)
if err != nil {
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "get tlsTransport for registry %q", refspec.Hostname())
}
httpClient = &http.Client{Transport: tlsTransport}
}

resolver := docker.NewResolver(docker.ResolverOptions{
Credentials: cred,
Client: http.DefaultClient,
Client: httpClient,
})
_, desc, err := resolver.Resolve(ctx, ref)
if err != nil {
Expand Down

0 comments on commit 0a89a04

Please sign in to comment.