support loading certs from a directory

Add `remotes/certutil` functions for loading `ca.crt`, `client.cert`, and `client.key` into `tls.Config` from a directory like `/etc/docker/certs.d/<hostname>.

See https://docs.docker.com/engine/security/certificates/ .

Client applications including CRI plugin are expected to configure the resolver using these functions.

As an example, the `ctr` tool is extended to support `ctr images pull --certs-dir=/etc/docker/certs.d example.com/foo/bar:baz`.

Tested with Harbor 1.8.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
Akihiro Suda
2019-08-10 17:07:58 +09:00
committed by Derek McGowan
parent e852da5855
commit dc131aa862
3 changed files with 164 additions and 0 deletions

View File

@@ -61,6 +61,11 @@ var (
Name: "refresh",
Usage: "refresh token for authorization server",
},
cli.StringFlag{
Name: "certs-dir",
// compatible with "/etc/docker/certs.d"
Usage: "custom certificates directory that contains \"<hostname>/{ca.crt, client.cert, client.key}\"",
},
}
// ContainerFlags are cli flags specifying container options

View File

@@ -21,15 +21,20 @@ import (
gocontext "context"
"crypto/tls"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/containerd/console"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/certutil"
"github.com/containerd/containerd/remotes/docker"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -94,6 +99,7 @@ func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolv
},
ExpectContinueTimeout: 5 * time.Second,
}
loadCertsDir(tr.TLSClientConfig, clicontext.String("certs-dir"))
options.Client = &http.Client{
Transport: tr,
@@ -108,3 +114,30 @@ func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolv
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)
}
}
}