Skip to content
This repository was archived by the owner on Apr 28, 2020. It is now read-only.

Commit 1576a6a

Browse files
author
Nathan Potter
committed
Use fixuid for mapping host uid:gid to docker uid:gid
1 parent 576aa9e commit 1576a6a

File tree

7 files changed

+61
-40
lines changed

7 files changed

+61
-40
lines changed

images/Dockerfile

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,22 @@ LABEL share.ssh="~/.ssh:~/.ssh"
2828
RUN wget -O /usr/bin/code-server https://codesrv-ci.cdr.sh/latest-linux && \
2929
chmod +x /usr/bin/code-server
3030

31-
RUN adduser --gecos '' --disabled-password user && \
31+
32+
# Use fixuid so that we can dynamically change the uid:gid to match what is being used by the host user.
33+
# Note: If the project is large, the `chown -R` call from fixuid can take a long time.
34+
# TODO: optimize the chown for larger projects.
35+
RUN addgroup --gid 1000 user && \
36+
adduser --uid 1000 --ingroup user --home /home/user --shell /bin/sh --disabled-password --gecos "" user && \
3237
echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
33-
USER user
38+
39+
RUN USER=user && \
40+
GROUP=user && \
41+
curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.4/fixuid-0.4-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \
42+
chown root:root /usr/local/bin/fixuid && \
43+
chmod 4755 /usr/local/bin/fixuid && \
44+
mkdir -p /etc/fixuid && \
45+
printf "user: $USER\ngroup: $GROUP\n" > /etc/fixuid/config.yml
46+
47+
USER user:user
48+
49+
ENTRYPOINT ["fixuid"]

internal/codeserver/proc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func Port(containerName string) (string, error) {
3434
// tcp 0 0 127.0.0.53:domain 0.0.0.0:* LISTEN -
3535
out, err := dockutil.Exec(containerName, "netstat", "-tpl").CombinedOutput()
3636
if err != nil {
37-
return "", xerrors.Errorf("failed to netstat: %s, %v", out, err)
37+
return "", xerrors.Errorf("failed to netstat %s: %w", out, err)
3838
}
3939

4040
lines := strings.Split(string(out), "\n")

project.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ func (p *project) waitOnline() error {
250250
cli := dockerClient()
251251
defer cli.Close()
252252

253-
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
253+
// Give a pretty significant amount of time since the `chown -R` call from
254+
// fixuid can take a long time if the project codebase is large.
255+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
254256
defer cancel()
255257

256258
for ctx.Err() == nil {

proxycmd.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212
"net/http"
1313
"net/http/httputil"
1414
"net/url"
15-
"nhooyr.io/websocket"
1615
"strconv"
1716
"sync"
1817
"sync/atomic"
1918
"time"
2019

20+
"nhooyr.io/websocket"
21+
2122
"go.coder.com/cli"
2223
"go.coder.com/flog"
2324
"golang.org/x/xerrors"
@@ -87,7 +88,10 @@ func (p *proxy) getCodeServerPort() (string, error) {
8788
}
8889

8990
func (p *proxy) refreshPort() {
90-
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
91+
// Give a longer period of time since the chown process in fixuid can take a bit
92+
// to complete. This only happens if the uid:gid on the host isn't already 1000:1000.
93+
// TODO: optimize fixuid and chown.
94+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
9195
defer cancel()
9296

9397
atomic.StoreInt64(&p.refreshing, 1)

runner.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/url"
88
"os"
99
"os/exec"
10+
"os/user"
1011
"path/filepath"
1112
"runtime"
1213
"strings"
@@ -80,6 +81,11 @@ func (r *runner) runContainer(image string) error {
8081
return err
8182
}
8283

84+
u, err := user.Current()
85+
if err != nil {
86+
return err
87+
}
88+
8389
var envs []string
8490
envs = r.environment(envs)
8591

@@ -97,10 +103,7 @@ func (r *runner) runContainer(image string) error {
97103
projectNameLabel: r.projectName,
98104
proxyURLLabel: r.proxyURL,
99105
},
100-
// The user inside has uid 1000. This works even on macOS where the default user has uid 501.
101-
// See https://stackoverflow.com/questions/43097341/docker-on-macosx-does-not-translate-file-ownership-correctly-in-volumes
102-
// The docker image runs it as uid 1000 so we don't need to set anything.
103-
User: "",
106+
User: fmt.Sprintf("%s:%s", u.Uid, u.Gid),
104107
}
105108

106109
err = r.addImageDefinedLabels(image, containerConfig.Labels)

runner_test.go

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"testing"
55

6+
"github.com/stretchr/testify/assert"
67
"github.com/stretchr/testify/require"
78
)
89

@@ -39,59 +40,59 @@ func Test_runner(t *testing.T) {
3940
})
4041
}
4142

43+
// codeServerStarts ensures that the code server process
44+
// starts up inside the container.
45+
codeServerStarts := func(t *testing.T, p *params) {
46+
t.Run("CodeServerStarts", func(t *testing.T) {
47+
err := p.proj.waitOnline()
48+
require.NoError(t, err)
49+
})
50+
}
51+
4252
// loadFromContainer ensures that our state is properly stored
4353
// on the container and can rebuild our in memory structures
4454
// correctly.
4555
loadFromContainer := func(t *testing.T, p *params) {
4656
t.Run("FromContainer", func(t *testing.T) {
47-
// bldr, err := hatBuilderFromContainer(p.proj.cntName())
48-
// require.NoError(t, err)
49-
50-
// assert.Equal(t, p.bldr.hatPath, bldr.hatPath)
51-
// assert.Equal(t, p.bldr.baseImage, bldr.baseImage)
52-
53-
// runner, err := runnerFromContainer(p.proj.cntName())
54-
// require.NoError(t, err)
57+
bldr, err := hatBuilderFromContainer(p.proj.cntName())
58+
require.NoError(t, err)
5559

56-
// assert.Equal(t, p.runner.cntName, runner.cntName)
57-
// assert.Equal(t, p.runner.hostname, runner.hostname)
58-
// assert.Equal(t, p.runner.port, runner.port)
59-
// assert.Equal(t, p.runner.projectLocalDir, runner.projectLocalDir)
60-
// assert.Equal(t, p.runner.projectName, runner.projectName)
61-
// assert.Equal(t, p.runner.testCmd, runner.testCmd)
62-
})
63-
}
60+
assert.Equal(t, p.bldr.hatPath, bldr.hatPath)
61+
assert.Equal(t, p.bldr.baseImage, bldr.baseImage)
6462

65-
// codeServerStarts ensures that the code server process
66-
// starts up inside the container.
67-
codeServerStarts := func(t *testing.T, p *params) {
68-
t.Run("CodeServerStarts", func(t *testing.T) {
69-
err := p.proj.waitOnline()
63+
runner, err := runnerFromContainer(p.proj.cntName())
7064
require.NoError(t, err)
65+
66+
assert.Equal(t, p.runner.cntName, runner.cntName)
67+
assert.Equal(t, p.runner.hostname, runner.hostname)
68+
assert.Equal(t, p.runner.port, runner.port)
69+
assert.Equal(t, p.runner.projectLocalDir, runner.projectLocalDir)
70+
assert.Equal(t, p.runner.projectName, runner.projectName)
71+
assert.Equal(t, p.runner.testCmd, runner.testCmd)
7172
})
7273
}
7374

7475
run(t, "BaseImageNoHat", "https://github.com/cdr/nbin", "",
7576
labelChecker,
76-
loadFromContainer,
7777
codeServerStarts,
78+
loadFromContainer,
7879
)
7980

8081
run(t, "BaseImageHat", "https://github.com/cdr/flog", "./hat-examples/fish",
8182
labelChecker,
82-
loadFromContainer,
8383
codeServerStarts,
84+
loadFromContainer,
8485
)
8586

8687
run(t, "ProjImageNoHat", "https://github.com/cdr/bigdur", "",
8788
labelChecker,
88-
loadFromContainer,
8989
codeServerStarts,
90+
loadFromContainer,
9091
)
9192

9293
run(t, "ProjImageHat", "https://github.com/cdr/sshcode", "./hat-examples/net",
9394
labelChecker,
94-
loadFromContainer,
9595
codeServerStarts,
96+
loadFromContainer,
9697
)
9798
}

ubuntu-dev/buildpush.sh

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)