feat: non root user container (#1202)
* feat: non root user container * Also chown WorkingDir * . * . * Update docker_run.go * Add Test * Update runner_test.go * Update docker_run.go * Apply suggestions from code review Co-authored-by: R <me@hackerc.at> * remove cruft from master merge Co-authored-by: R <me@hackerc.at> Co-authored-by: Casey Lee <cplee@nektos.com> Co-authored-by: Casey Lee <caseypl@amazon.com>
This commit is contained in:
parent
c30bc824b2
commit
b59e6dee6d
4 changed files with 71 additions and 6 deletions
|
@ -12,6 +12,7 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-billy/v5/helper/polyfill"
|
||||
|
@ -125,6 +126,13 @@ func (cr *containerReference) Start(attach bool) common.Executor {
|
|||
cr.attach().IfBool(attach),
|
||||
cr.start(),
|
||||
cr.wait().IfBool(attach),
|
||||
cr.tryReadUID(),
|
||||
cr.tryReadGID(),
|
||||
func(ctx context.Context) error {
|
||||
// If this fails, then folders have wrong permissions on non root container
|
||||
_ = cr.Exec([]string{"chown", "-R", fmt.Sprintf("%d:%d", cr.UID, cr.GID), cr.input.WorkingDir}, nil, "0", "")(ctx)
|
||||
return nil
|
||||
},
|
||||
).IfNot(common.Dryrun),
|
||||
)
|
||||
}
|
||||
|
@ -154,8 +162,12 @@ func (cr *containerReference) Copy(destPath string, files ...*FileEntry) common.
|
|||
func (cr *containerReference) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
|
||||
return common.NewPipelineExecutor(
|
||||
common.NewInfoExecutor("%sdocker cp src=%s dst=%s", logPrefix, srcPath, destPath),
|
||||
cr.Exec([]string{"mkdir", "-p", destPath}, nil, "", ""),
|
||||
cr.copyDir(destPath, srcPath, useGitIgnore),
|
||||
func(ctx context.Context) error {
|
||||
// If this fails, then folders have wrong permissions on non root container
|
||||
_ = cr.Exec([]string{"chown", "-R", fmt.Sprintf("%d:%d", cr.UID, cr.GID), destPath}, nil, "0", "")(ctx)
|
||||
return nil
|
||||
},
|
||||
).IfNot(common.Dryrun)
|
||||
}
|
||||
|
||||
|
@ -211,6 +223,8 @@ type containerReference struct {
|
|||
cli client.APIClient
|
||||
id string
|
||||
input *NewContainerInput
|
||||
UID int
|
||||
GID int
|
||||
}
|
||||
|
||||
func GetDockerClient(ctx context.Context) (cli client.APIClient, err error) {
|
||||
|
@ -581,6 +595,47 @@ func (cr *containerReference) exec(cmd []string, env map[string]string, user, wo
|
|||
}
|
||||
}
|
||||
|
||||
func (cr *containerReference) tryReadID(opt string, cbk func(id int)) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
idResp, err := cr.cli.ContainerExecCreate(ctx, cr.id, types.ExecConfig{
|
||||
Cmd: []string{"id", opt},
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := cr.cli.ContainerExecAttach(ctx, idResp.ID, types.ExecStartCheck{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
sid, err := resp.Reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
exp := regexp.MustCompile(`\d+\n`)
|
||||
found := exp.FindString(sid)
|
||||
id, err := strconv.ParseInt(found[:len(found)-1], 10, 32)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
cbk(int(id))
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cr *containerReference) tryReadUID() common.Executor {
|
||||
return cr.tryReadID("-u", func(id int) { cr.UID = id })
|
||||
}
|
||||
|
||||
func (cr *containerReference) tryReadGID() common.Executor {
|
||||
return cr.tryReadID("-g", func(id int) { cr.GID = id })
|
||||
}
|
||||
|
||||
func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal bool, resp types.HijackedResponse, idResp types.IDResponse, user string, workdir string) error {
|
||||
logger := common.Logger(ctx)
|
||||
|
||||
|
@ -670,6 +725,9 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
|
|||
SrcPrefix: srcPrefix,
|
||||
Handler: &tarCollector{
|
||||
TarWriter: tw,
|
||||
UID: cr.UID,
|
||||
GID: cr.GID,
|
||||
DstDir: dstPath[1:],
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -686,7 +744,7 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to seek tar archive: %w", err)
|
||||
}
|
||||
err = cr.cli.CopyToContainer(ctx, cr.id, dstPath, tarFile, types.CopyToContainerOptions{})
|
||||
err = cr.cli.CopyToContainer(ctx, cr.id, "/", tarFile, types.CopyToContainerOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy content to container: %w", err)
|
||||
}
|
||||
|
@ -705,6 +763,8 @@ func (cr *containerReference) copyContent(dstPath string, files ...*FileEntry) c
|
|||
Name: file.Name,
|
||||
Mode: file.Mode,
|
||||
Size: int64(len(file.Body)),
|
||||
Uid: cr.UID,
|
||||
Gid: cr.GID,
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
|
|
|
@ -23,9 +23,12 @@ type fileCollectorHandler interface {
|
|||
|
||||
type tarCollector struct {
|
||||
TarWriter *tar.Writer
|
||||
UID int
|
||||
GID int
|
||||
DstDir string
|
||||
}
|
||||
|
||||
func (tc tarCollector) WriteFile(path string, fi fs.FileInfo, linkName string, f io.Reader) error {
|
||||
func (tc tarCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error {
|
||||
// create a new dir/file header
|
||||
header, err := tar.FileInfoHeader(fi, linkName)
|
||||
if err != nil {
|
||||
|
@ -33,9 +36,11 @@ func (tc tarCollector) WriteFile(path string, fi fs.FileInfo, linkName string, f
|
|||
}
|
||||
|
||||
// update the name to correctly reflect the desired destination when untaring
|
||||
header.Name = path
|
||||
header.Name = path.Join(tc.DstDir, fpath)
|
||||
header.Mode = int64(fi.Mode())
|
||||
header.ModTime = fi.ModTime()
|
||||
header.Uid = tc.UID
|
||||
header.Gid = tc.GID
|
||||
|
||||
// write the header
|
||||
if err := tc.TarWriter.WriteHeader(header); err != nil {
|
||||
|
@ -138,7 +143,7 @@ func (fc *fileCollector) collectFiles(ctx context.Context, submodulePath []strin
|
|||
}
|
||||
}
|
||||
if err == nil && entry.Mode == filemode.Submodule {
|
||||
err = filepath.Walk(fi.Name(), fc.collectFiles(ctx, split))
|
||||
err = fc.Fs.Walk(fi.Name(), fc.collectFiles(ctx, split))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -180,7 +180,6 @@ func (rc *RunContext) startJobContainer() common.Executor {
|
|||
rc.JobContainer.Start(false),
|
||||
rc.JobContainer.UpdateFromImageEnv(&rc.Env),
|
||||
rc.JobContainer.UpdateFromEnv("/etc/environment", &rc.Env),
|
||||
rc.JobContainer.Exec([]string{"mkdir", "-m", "0777", "-p", ActPath}, rc.Env, "root", ""),
|
||||
rc.JobContainer.Copy(ActPath+"/", &container.FileEntry{
|
||||
Name: "workflow/event.json",
|
||||
Mode: 0644,
|
||||
|
|
|
@ -158,6 +158,7 @@ func TestRunEvent(t *testing.T) {
|
|||
{workdir, "container-hostname", "push", "", platforms},
|
||||
{workdir, "remote-action-docker", "push", "", platforms},
|
||||
{workdir, "remote-action-js", "push", "", platforms},
|
||||
{workdir, "remote-action-js", "push", "", map[string]string{"ubuntu-latest": "ghcr.io/catthehacker/ubuntu:runner-latest"}}, // Test if this works with non root container
|
||||
{workdir, "matrix", "push", "", platforms},
|
||||
{workdir, "matrix-include-exclude", "push", "", platforms},
|
||||
{workdir, "commands", "push", "", platforms},
|
||||
|
|
Loading…
Reference in a new issue