Skip to content

Commit

Permalink
Copy CA and user TLS registries certs to the container
Browse files Browse the repository at this point in the history
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
  • Loading branch information
crazy-max committed Sep 29, 2021
1 parent c05a6eb commit 5a42259
Show file tree
Hide file tree
Showing 71 changed files with 11,600 additions and 27 deletions.
207 changes: 180 additions & 27 deletions driver/docker-container/driver.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
package docker

import (
"archive/tar"
"bytes"
"context"
"io"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"time"

"github.com/docker/buildx/driver"
"github.com/docker/buildx/driver/bkimage"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
dockerclient "github.com/docker/docker/client"
dockerarchive "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/system"
"github.com/moby/buildkit/client"
buildkitconfig "github.com/moby/buildkit/cmd/buildkitd/config"
"github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -130,11 +135,10 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
return err
}
if f := d.InitConfig.ConfigFile; f != "" {
buf, err := readFileToTar(f)
if err != nil {
if err := d.copyToContainer(ctx, f, "/"); err != nil {
return err
}
if err := d.DockerAPI.CopyToContainer(ctx, d.Name, "/", buf, dockertypes.CopyToContainerOptions{}); err != nil {
if err := d.copyRegCertsToContainer(ctx, f); err != nil {
return err
}
}
Expand Down Expand Up @@ -196,6 +200,128 @@ func (d *Driver) copyLogs(ctx context.Context, l progress.SubLogger) error {
return rc.Close()
}

// copyToContainer is based on the implementation from docker/cli
// https://github.com/docker/cli/blob/master/cli/command/container/cp.go
func (d *Driver) copyToContainer(ctx context.Context, srcPath string, dstPath string) error {
var err error

// Get an absolute source path.
srcPath, err = resolveLocalPath(srcPath)
if err != nil {
return err
}

// Prepare the destination.
dstInfo := dockerarchive.CopyInfo{Path: dstPath}
dstStat, err := d.DockerAPI.ContainerStatPath(ctx, d.Name, dstPath)

// If the destination is a symbolic link, we should evaluate it.
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
linkTarget := dstStat.LinkTarget
if !system.IsAbs(linkTarget) {
dstParent, _ := dockerarchive.SplitPathDirEntry(dstPath)
linkTarget = filepath.Join(dstParent, linkTarget)
}
dstInfo.Path = linkTarget
if dstStat, err = d.DockerAPI.ContainerStatPath(ctx, d.Name, linkTarget); err != nil {
return err
}
}

// Validate the destination path.
if err := command.ValidateOutputPathFileMode(dstStat.Mode); err != nil {
return errors.Wrapf(err, `destination "%s:%s" must be a directory or a regular file`, d.Name, dstPath)
}

// Ignore any error and assume that the parent directory of the destination
// path exists, in which case the copy may still succeed. If there is any
// type of conflict (e.g., non-directory overwriting an existing directory
// or vice versa) the extraction will fail. If the destination simply did
// not exist, but the parent directory does, the extraction will still
// succeed.
dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()

// Prepare source copy info.
srcInfo, err := dockerarchive.CopyInfoSourcePath(srcPath, true)
if err != nil {
return err
}

srcArchive, err := dockerarchive.TarResource(srcInfo)
if err != nil {
return err
}
defer srcArchive.Close()

// With the stat info about the local source as well as the
// destination, we have enough information to know whether we need to
// alter the archive that we upload so that when the server extracts
// it to the specified directory in the container we get the desired
// copy behavior.

// See comments in the implementation of `dockerarchive.PrepareArchiveCopy`
// for exactly what goes into deciding how and whether the source
// archive needs to be altered for the correct copy behavior when it is
// extracted. This function also infers from the source and destination
// info which directory to extract to, which may be the parent of the
// destination that the user specified.
dstDir, preparedArchive, err := dockerarchive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo)
if err != nil {
return err
}
defer preparedArchive.Close()

return d.DockerAPI.CopyToContainer(ctx, d.Name, dstDir, preparedArchive, dockertypes.CopyToContainerOptions{
AllowOverwriteDirWithFile: false,
})
}

func resolveLocalPath(localPath string) (absPath string, err error) {
if absPath, err = filepath.Abs(localPath); err != nil {
return
}
return dockerarchive.PreserveTrailingDotOrSeparator(absPath, localPath, filepath.Separator), nil
}

func (d *Driver) copyRegCertsToContainer(ctx context.Context, bkconfig string) error {
// Load BuildKit config.
bcfg, err := buildkitconfig.LoadFile(bkconfig)
if err != nil {
return err
}

// Create a temp folder and copy all certs to it with copyFileParents
// to keep the exact same folder structure as defined in BuildKit config.
// It's required to do that here because we cannot copy a file in a folder
// that does not exist in the container with the CopyToContainer API.
tmpRegCerts, err := os.MkdirTemp("", "basedir-certs")
if err != nil {
return err
}
defer os.RemoveAll(tmpRegCerts)
for _, reg := range bcfg.Registries {
for _, rootCA := range reg.RootCAs {
if err := copyFileParents(rootCA, tmpRegCerts); err != nil {
return err
}
}
for _, keyPair := range reg.KeyPairs {
if len(keyPair.Key) > 0 {
if err := copyFileParents(keyPair.Key, tmpRegCerts); err != nil {
return err
}
}
if len(keyPair.Certificate) > 0 {
if err := copyFileParents(keyPair.Certificate, tmpRegCerts); err != nil {
return err
}
}
}
}

return d.copyToContainer(ctx, tmpRegCerts+"/.", "/")
}

func (d *Driver) exec(ctx context.Context, cmd []string) (string, net.Conn, error) {
execConfig := types.ExecConfig{
Cmd: cmd,
Expand Down Expand Up @@ -357,29 +483,6 @@ func (d *demux) Read(dt []byte) (int, error) {
return d.Reader.Read(dt)
}

func readFileToTar(fn string) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil)
tw := tar.NewWriter(buf)
dt, err := ioutil.ReadFile(fn)
if err != nil {
return nil, err
}
if err := tw.WriteHeader(&tar.Header{
Name: "/etc/buildkit/buildkitd.toml",
Size: int64(len(dt)),
Mode: 0644,
}); err != nil {
return nil, err
}
if _, err := tw.Write(dt); err != nil {
return nil, err
}
if err := tw.Close(); err != nil {
return nil, err
}
return buf, nil
}

type logWriter struct {
logger progress.SubLogger
stream int
Expand All @@ -389,3 +492,53 @@ func (l *logWriter) Write(dt []byte) (int, error) {
l.logger.Log(l.stream, dt)
return len(dt), nil
}

// copyFileParents copies a file using the full source file name under a
// folder. For example, copying /bar/foo.txt to /tmp will result in
// the file being placed in /tmp/bar/foo.txt.
func copyFileParents(srcFile string, destFolder string) error {
si, err := os.Stat(srcFile)
if err != nil {
return err
}

pi, err := os.Stat(path.Dir(srcFile))
if err != nil {
return err
}

tdd := path.Join(destFolder, path.Dir(srcFile))
if err := os.MkdirAll(tdd, pi.Mode()); err != nil {
return err
}

if si.Mode()&os.ModeSymlink != 0 {
if srcFile, err = os.Readlink(srcFile); err != nil {
return err
}
si, err = os.Stat(srcFile)
if err != nil {
return err
}
}

sf, err := os.Open(srcFile)
if err != nil {
return err
}
defer sf.Close()

tdf := path.Join(tdd, path.Base(srcFile))
df, err := os.Create(tdf)
if err != nil {
return err
}
defer df.Close()

_, err = io.Copy(df, sf)
if err != nil {
return err
}

return os.Chmod(tdf, si.Mode())
}
62 changes: 62 additions & 0 deletions vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/docker/docker/pkg/archive/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5a42259

Please sign in to comment.