summaryrefslogtreecommitdiff
path: root/docker
diff options
context:
space:
mode:
authorDan Duvall <dduvall@wikimedia.org>2018-02-21 16:18:47 -0800
committerDan Duvall <dduvall@wikimedia.org>2018-03-05 13:22:10 -0800
commit47526283fea7df1734ef5b9a5da5c810bf76a29a (patch)
tree7d6ed2530acdaded093f410939aa9df250130c0a /docker
parentf606d212fd94769294b1ebdaa6ec224458281d22 (diff)
downloadblubber-47526283fea7df1734ef5b9a5da5c810bf76a29a.tar.gz
Fix application files/runtime permissions scheme
Summary: Introduces new `lives` configuration that provides the name/UID/GID of the user that will own application files and installed dependencies. This new configuration is distinct from `runs` in that the former determines application file location ownership and the latter now only determines runtime process ownership. Default configuration has also been introduced for both config sections. In addition to the new configuration, a new `build.CopyAs` instruction has been introduced that ensures correct UID/GID ownership of files copied into the container image, and all unqualified `build.Copy` instructions are wrapped by the new `build.CopyAs` instruction using the UID/GID appropriate for the current build phase. A new `build.User` instruction is also introduced and injected into the build at the start of certain phases to enforce ownership of `build.Run` processes. This effective process/file ownership model is: PhasePrivileged - "root" PhasePrivilegedDropped - lives.as PhasePreInstall - lives.as PhaseInstall - lives.as PhasePostInstall - runs.as Fixes T187372 Test Plan: Run `go test ./...`. Reviewers: thcipriani, hashar, demon, #release-engineering-team Reviewed By: thcipriani, #release-engineering-team Subscribers: mmodell Tags: #release-engineering-team Maniphest Tasks: T187372 Differential Revision: https://phabricator.wikimedia.org/D984
Diffstat (limited to 'docker')
-rw-r--r--docker/compiler.go11
-rw-r--r--docker/instructions.go33
-rw-r--r--docker/instructions_test.go24
3 files changed, 60 insertions, 8 deletions
diff --git a/docker/compiler.go b/docker/compiler.go
index 68f9384..5929692 100644
--- a/docker/compiler.go
+++ b/docker/compiler.go
@@ -36,6 +36,7 @@ func Compile(cfg *config.Config, variant string) (*bytes.Buffer, error) {
if err != nil {
return nil, err
}
+
compileStage(buffer, stage, dependency)
mainStage = variant
}
@@ -60,18 +61,12 @@ func compileStage(buffer *bytes.Buffer, stage string, vcfg *config.VariantConfig
writeln(buffer, "FROM ", baseAndStage)
- writeln(buffer, "USER root")
-
compilePhase(buffer, vcfg, build.PhasePrivileged)
- if vcfg.Runs.As != "" {
- writeln(buffer, "USER ", vcfg.Runs.As)
- }
-
compilePhase(buffer, vcfg, build.PhasePrivilegeDropped)
- if vcfg.Runs.In != "" {
- writeln(buffer, "WORKDIR ", vcfg.Runs.In)
+ if vcfg.Lives.In != "" {
+ writeln(buffer, "WORKDIR ", vcfg.Lives.In)
}
compilePhase(buffer, vcfg, build.PhasePreInstall)
diff --git a/docker/instructions.go b/docker/instructions.go
index 52c281a..d6e560f 100644
--- a/docker/instructions.go
+++ b/docker/instructions.go
@@ -23,6 +23,10 @@ func NewInstruction(instruction build.Instruction) (Instruction, error) {
var dockerInstruction Copy
dockerInstruction.arguments = instruction.Compile()
return dockerInstruction, nil
+ case build.CopyAs:
+ var dockerInstruction CopyAs
+ dockerInstruction.arguments = instruction.Compile()
+ return dockerInstruction, nil
case build.CopyFrom:
var dockerInstruction CopyFrom
dockerInstruction.arguments = instruction.Compile()
@@ -35,6 +39,10 @@ func NewInstruction(instruction build.Instruction) (Instruction, error) {
var dockerInstruction Label
dockerInstruction.arguments = instruction.Compile()
return dockerInstruction, nil
+ case build.User:
+ var dockerInstruction User
+ dockerInstruction.arguments = instruction.Compile()
+ return dockerInstruction, nil
case build.Volume:
var dockerInstruction Volume
dockerInstruction.arguments = instruction.Compile()
@@ -83,6 +91,19 @@ func (dc Copy) Compile() string {
join(dc.arguments, ", "))
}
+// CopyAs compiles into a COPY --chown instruction.
+//
+type CopyAs struct{ abstractInstruction }
+
+// Compile compiles COPY --chown instructions.
+//
+func (dca CopyAs) Compile() string {
+ return fmt.Sprintf(
+ "COPY --chown=%s [%s]\n",
+ dca.arguments[0],
+ join(dca.arguments[1:], ", "))
+}
+
// CopyFrom compiles into a COPY --from instruction.
//
type CopyFrom struct{ abstractInstruction }
@@ -121,6 +142,18 @@ func (dl Label) Compile() string {
join(dl.arguments, " "))
}
+// User compiles into a USER instruction.
+//
+type User struct{ abstractInstruction }
+
+// Compile compiles USER instructions.
+//
+func (du User) Compile() string {
+ return fmt.Sprintf(
+ "USER %s\n",
+ join(du.arguments, ", "))
+}
+
// Volume compiles into a VOLUME instruction.
//
type Volume struct{ abstractInstruction }
diff --git a/docker/instructions_test.go b/docker/instructions_test.go
index 181ab71..651e0bd 100644
--- a/docker/instructions_test.go
+++ b/docker/instructions_test.go
@@ -47,6 +47,18 @@ func TestCopy(t *testing.T) {
assert.Equal(t, "COPY [\"foo1\", \"foo2\", \"bar\"]\n", di.Compile())
}
+func TestCopyAs(t *testing.T) {
+ i := build.CopyAs{123, 124, build.Copy{[]string{"foo1", "foo2"}, "bar"}}
+
+ di, err := docker.NewInstruction(i)
+
+ var dockerCopyAs docker.CopyAs
+
+ assert.Nil(t, err)
+ assert.IsType(t, dockerCopyAs, di)
+ assert.Equal(t, "COPY --chown=123:124 [\"foo1\", \"foo2\", \"bar\"]\n", di.Compile())
+}
+
func TestCopyFrom(t *testing.T) {
i := build.CopyFrom{"foo", build.Copy{[]string{"foo1", "foo2"}, "bar"}}
@@ -83,6 +95,18 @@ func TestLabel(t *testing.T) {
assert.Equal(t, "LABEL bar=\"foo\" foo=\"bar\"\n", di.Compile())
}
+func TestUser(t *testing.T) {
+ i := build.User{"foo"}
+
+ di, err := docker.NewInstruction(i)
+
+ var dockerUser docker.User
+
+ assert.Nil(t, err)
+ assert.IsType(t, dockerUser, di)
+ assert.Equal(t, "USER \"foo\"\n", di.Compile())
+}
+
func TestVolume(t *testing.T) {
i := build.Volume{"/foo/dir"}