Update ctr resolver to use new config package

Moved registry host configuration to the config package
and allows support of loading configurations from a
directory when the hosts are being resolved.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2020-03-31 21:46:57 -07:00
parent 17b6050d20
commit 547301cb0c
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
3 changed files with 21 additions and 189 deletions

View File

@ -62,9 +62,9 @@ var (
Usage: "refresh token for authorization server", Usage: "refresh token for authorization server",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "certs-dir", Name: "hosts-dir",
// compatible with "/etc/docker/certs.d" // compatible with "/etc/docker/certs.d"
Usage: "custom certificates directory that contains \"<hostname>/{ca.crt, client.cert, client.key}\"", Usage: "Custom hosts configuration directory",
}, },
} }

View File

@ -21,20 +21,13 @@ import (
gocontext "context" gocontext "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strings" "strings"
"time"
"github.com/containerd/console" "github.com/containerd/console"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/certutil"
"github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/remotes/docker"
"github.com/containerd/containerd/remotes/docker/config"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -65,7 +58,6 @@ func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolv
username = username[0:i] username = username[0:i]
} }
options := docker.ResolverOptions{ options := docker.ResolverOptions{
PlainHTTP: clicontext.Bool("plain-http"),
Tracker: PushTracker, Tracker: PushTracker,
} }
if username != "" { if username != "" {
@ -84,60 +76,26 @@ func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolv
secret = rt secret = rt
} }
tr := &http.Transport{ hostOptions := config.HostOptions{}
Proxy: http.ProxyFromEnvironment, hostOptions.Credentials = func(host string) (string, string, error) {
DialContext: (&net.Dialer{ // If host doesn't match...
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: clicontext.Bool("skip-verify"),
},
ExpectContinueTimeout: 5 * time.Second,
}
loadCertsDir(tr.TLSClientConfig, clicontext.String("certs-dir"))
options.Client = &http.Client{
Transport: tr,
}
credentials := func(host string) (string, string, error) {
// Only one host // Only one host
return username, secret, nil return username, secret, nil
} }
authOpts := []docker.AuthorizerOpt{docker.WithAuthClient(options.Client), docker.WithAuthCreds(credentials)} if clicontext.Bool("plain-http") {
options.Authorizer = docker.NewDockerAuthorizer(authOpts...) hostOptions.DefaultScheme = "http"
}
if clicontext.Bool("skip-verify") {
hostOptions.DefaultTLS = &tls.Config{
InsecureSkipVerify: true,
}
}
if hostDir := clicontext.String("hosts-dir"); hostDir != "" {
hostOptions.HostDir = config.HostDirFromRoot(hostDir)
}
options.Hosts = config.ConfigureHosts(ctx, hostOptions)
return docker.NewResolver(options), nil return docker.NewResolver(options), nil
} }
// loadCertsDir loads certs from certsDir like "/etc/docker/certs.d" .
func loadCertsDir(config *tls.Config, certsDir string) {
if certsDir == "" {
return
}
fs, err := ioutil.ReadDir(certsDir)
if err != nil && !os.IsNotExist(err) {
logrus.WithError(err).Errorf("cannot read certs directory %q", certsDir)
return
}
for _, f := range fs {
if !f.IsDir() {
continue
}
// TODO: skip loading if f.Name() is not valid FQDN/IP
hostDir := filepath.Join(certsDir, f.Name())
caCertGlob := filepath.Join(hostDir, "*.crt")
if _, err = certutil.LoadCACerts(config, caCertGlob); err != nil {
logrus.WithError(err).Errorf("cannot load certs from %q", caCertGlob)
}
keyGlob := filepath.Join(hostDir, "*.key")
if _, _, err = certutil.LoadKeyPairs(config, keyGlob, certutil.DockerKeyPairCertLocator); err != nil {
logrus.WithError(err).Errorf("cannot load key pairs from %q", keyGlob)
}
}
}

View File

@ -1,126 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package certutil
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
)
// SystemCertPool returns a copy of the system cert pool,
// returns an error if failed to load or empty pool on windows.
//
// SystemCertPool was ported from Docker 19.03
// https://github.com/docker/engine/blob/v19.03.1/vendor/github.com/docker/go-connections/tlsconfig/certpool_go17.go#L12
func SystemCertPool() (*x509.CertPool, error) {
certpool, err := x509.SystemCertPool()
if err != nil && runtime.GOOS == "windows" {
return x509.NewCertPool(), nil
}
return certpool, err
}
// LoadCACerts loads CA certificates into tlsConfig from glob.
// glob should be like "/etc/docker/certs.d/example.com/*.crt" .
// LoadCACerts returns the paths of the loaded certs.
func LoadCACerts(tlsConfig *tls.Config, glob string) ([]string, error) {
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
files, err := filepath.Glob(glob)
if err != nil {
return nil, err
}
sort.Strings(files)
if tlsConfig.RootCAs == nil {
systemPool, err := SystemCertPool()
if err != nil {
return nil, errors.Wrap(err, "unable to get system cert poolv")
}
tlsConfig.RootCAs = systemPool
}
var loaded []string
for _, f := range files {
data, err := ioutil.ReadFile(f)
if err != nil {
return loaded, errors.Wrapf(err, "unable to read CA cert %q", f)
}
if !tlsConfig.RootCAs.AppendCertsFromPEM(data) {
return loaded, errors.Errorf("unable to load CA cert %q", f)
}
loaded = append(loaded, f)
}
return loaded, nil
}
// KeyPairCertLocator is used to resolve the cert path from the key path.
type KeyPairCertLocator func(keyPath string) (certPath string, err error)
// DockerKeyPairCertLocator implements the Docker-style convention. ("*.key" -> "*.cert")
var DockerKeyPairCertLocator KeyPairCertLocator = func(keyPath string) (string, error) {
if !strings.HasSuffix(keyPath, ".key") {
return "", errors.Errorf("expected key path with \".key\" suffix, got %q", keyPath)
}
// Docker convention uses *.crt for CA certs, *.cert for keypair certs.
certPath := keyPath[:len(keyPath)-4] + ".cert"
return certPath, nil
}
// LoadKeyPairs loads key pairs into tlsConfig from keyGlob.
// keyGlob should be like "/etc/docker/certs.d/example.com/*.key" .
// certLocator is used to resolve the cert path from the key path.
// Use DockerKeyPairCertLocator for the Docker-style convention. ("*.key" -> "*.cert")
// LoadKeyParis returns the paths of the loaded certs and the loaded keys.
func LoadKeyPairs(tlsConfig *tls.Config, keyGlob string, certLocator KeyPairCertLocator) ([]string, []string, error) {
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
if certLocator == nil {
return nil, nil, errors.Wrap(errdefs.ErrInvalidArgument, "missing cert locator")
}
keyPaths, err := filepath.Glob(keyGlob)
if err != nil {
return nil, nil, err
}
sort.Strings(keyPaths)
var (
loadedCerts []string
loadedKeys []string
)
for _, keyPath := range keyPaths {
certPath, err := certLocator(keyPath)
if err != nil {
return loadedCerts, loadedKeys, err
}
keyPair, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return loadedCerts, loadedKeys, err
}
tlsConfig.Certificates = append(tlsConfig.Certificates, keyPair)
loadedCerts = append(loadedCerts, certPath)
loadedKeys = append(loadedKeys, keyPath)
}
return loadedCerts, loadedKeys, nil
}