Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6813615

Browse files
Merge branch 'main' into dm-workspace-template-ttl
2 parents 66e80cf + 67553a7 commit 6813615

File tree

23 files changed

+190
-58
lines changed

23 files changed

+190
-58
lines changed

.github/workflows/ci.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ jobs:
188188
189189
# Check for any typos
190190
- name: Check for typos
191-
uses: crate-ci/typos@b74202f74b4346efdbce7801d187ec57b266bac8 # v1.27.3
191+
uses: crate-ci/typos@2872c382bb9668d4baa5eade234dcbc0048ca2cf # v1.28.2
192192
with:
193193
config: .github/workflows/typos.toml
194194

@@ -540,7 +540,7 @@ jobs:
540540
timeout-minutes: 25
541541
steps:
542542
- name: Harden Runner
543-
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
543+
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
544544
with:
545545
egress-policy: audit
546546

@@ -1146,7 +1146,7 @@ jobs:
11461146
version: "2.2.1"
11471147

11481148
- name: Get Cluster Credentials
1149-
uses: google-github-actions/get-gke-credentials@206d64b64b0eba0a6e2f25113d044c31776ca8d6 # v2.2.2
1149+
uses: google-github-actions/get-gke-credentials@9025e8f90f2d8e0c3dafc3128cc705a26d992a6a # v2.3.0
11501150
with:
11511151
cluster_name: dogfood-v2
11521152
location: us-central1-a

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ jobs:
4747

4848
# Upload the results to GitHub's code scanning dashboard.
4949
- name: "Upload to code-scanning"
50-
uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
50+
uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6
5151
with:
5252
sarif_file: results.sarif

.github/workflows/security.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
uses: ./.github/actions/setup-go
3939

4040
- name: Initialize CodeQL
41-
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
41+
uses: github/codeql-action/init@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6
4242
with:
4343
languages: go, javascript
4444

@@ -48,7 +48,7 @@ jobs:
4848
rm Makefile
4949
5050
- name: Perform CodeQL Analysis
51-
uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
51+
uses: github/codeql-action/analyze@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6
5252

5353
- name: Send Slack notification on failure
5454
if: ${{ failure() }}
@@ -144,7 +144,7 @@ jobs:
144144
severity: "CRITICAL,HIGH"
145145

146146
- name: Upload Trivy scan results to GitHub Security tab
147-
uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
147+
uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6
148148
with:
149149
sarif_file: trivy-results.sarif
150150
category: "Trivy"

agent/agentexec/cli_linux.go

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import (
99
"os"
1010
"os/exec"
1111
"runtime"
12+
"slices"
1213
"strconv"
1314
"strings"
1415
"syscall"
1516

1617
"golang.org/x/sys/unix"
1718
"golang.org/x/xerrors"
19+
"kernel.org/pub/linux/libs/security/libcap/cap"
1820
)
1921

2022
// CLI runs the agent-exec command. It should only be called by the cli package.
@@ -48,6 +50,14 @@ func CLI() error {
4850
return xerrors.Errorf("no exec command provided %+v", os.Args)
4951
}
5052

53+
if *oom == unset {
54+
// If an explicit oom score isn't set, we use the default.
55+
*oom, err = defaultOOMScore()
56+
if err != nil {
57+
return xerrors.Errorf("get default oom score: %w", err)
58+
}
59+
}
60+
5161
if *nice == unset {
5262
// If an explicit nice score isn't set, we use the default.
5363
*nice, err = defaultNiceScore()
@@ -56,36 +66,62 @@ func CLI() error {
5666
}
5767
}
5868

59-
if *oom == unset {
60-
// If an explicit oom score isn't set, we use the default.
61-
*oom, err = defaultOOMScore()
62-
if err != nil {
63-
return xerrors.Errorf("get default oom score: %w", err)
64-
}
69+
// We drop effective caps prior to setting dumpable so that we limit the
70+
// impact of someone attempting to hijack the process (i.e. with a debugger)
71+
// to take advantage of the capabilities of the agent process. We encourage
72+
// users to set cap_net_admin on the agent binary for improved networking
73+
// performance and doing so results in the process having its SET_DUMPABLE
74+
// attribute disabled (meaning we cannot adjust the oom score).
75+
err = dropEffectiveCaps()
76+
if err != nil {
77+
printfStdErr("failed to drop effective caps: %v", err)
6578
}
6679

67-
err = unix.Setpriority(unix.PRIO_PROCESS, 0, *nice)
80+
// Set dumpable to 1 so that we can adjust the oom score. If the process
81+
// doesn't have capabilities or has an suid/sgid bit set, this is already
82+
// set.
83+
err = unix.Prctl(unix.PR_SET_DUMPABLE, 1, 0, 0, 0)
6884
if err != nil {
69-
// We alert the user instead of failing the command since it can be difficult to debug
70-
// for a template admin otherwise. It's quite possible (and easy) to set an
71-
// inappriopriate value for niceness.
72-
printfStdErr("failed to adjust niceness to %d for cmd %+v: %v", *nice, args, err)
85+
printfStdErr("failed to set dumpable: %v", err)
7386
}
7487

7588
err = writeOOMScoreAdj(*oom)
7689
if err != nil {
7790
// We alert the user instead of failing the command since it can be difficult to debug
7891
// for a template admin otherwise. It's quite possible (and easy) to set an
7992
// inappriopriate value for oom_score_adj.
80-
printfStdErr("failed to adjust oom score to %d for cmd %+v: %v", *oom, args, err)
93+
printfStdErr("failed to adjust oom score to %d for cmd %+v: %v", *oom, execArgs(os.Args), err)
94+
}
95+
96+
// Set dumpable back to 0 just to be safe. It's not inherited for execve anyways.
97+
err = unix.Prctl(unix.PR_SET_DUMPABLE, 0, 0, 0, 0)
98+
if err != nil {
99+
printfStdErr("failed to unset dumpable: %v", err)
100+
}
101+
102+
err = unix.Setpriority(unix.PRIO_PROCESS, 0, *nice)
103+
if err != nil {
104+
// We alert the user instead of failing the command since it can be difficult to debug
105+
// for a template admin otherwise. It's quite possible (and easy) to set an
106+
// inappriopriate value for niceness.
107+
printfStdErr("failed to adjust niceness to %d for cmd %+v: %v", *nice, args, err)
81108
}
82109

83110
path, err := exec.LookPath(args[0])
84111
if err != nil {
85112
return xerrors.Errorf("look path: %w", err)
86113
}
87114

88-
return syscall.Exec(path, args, os.Environ())
115+
// Remove environment variables specific to the agentexec command. This is
116+
// especially important for environments that are attempting to develop Coder in Coder.
117+
env := os.Environ()
118+
env = slices.DeleteFunc(env, func(e string) bool {
119+
return strings.HasPrefix(e, EnvProcPrioMgmt) ||
120+
strings.HasPrefix(e, EnvProcOOMScore) ||
121+
strings.HasPrefix(e, EnvProcNiceScore)
122+
})
123+
124+
return syscall.Exec(path, args, env)
89125
}
90126

91127
func defaultNiceScore() (int, error) {
@@ -151,3 +187,16 @@ func execArgs(args []string) []string {
151187
func printfStdErr(format string, a ...any) {
152188
_, _ = fmt.Fprintf(os.Stderr, "coder-agent: %s\n", fmt.Sprintf(format, a...))
153189
}
190+
191+
func dropEffectiveCaps() error {
192+
proc := cap.GetProc()
193+
err := proc.ClearFlag(cap.Effective)
194+
if err != nil {
195+
return xerrors.Errorf("clear effective caps: %w", err)
196+
}
197+
err = proc.SetProc()
198+
if err != nil {
199+
return xerrors.Errorf("set proc: %w", err)
200+
}
201+
return nil
202+
}

agent/agentexec/cli_linux_test.go

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os"
1111
"os/exec"
1212
"path/filepath"
13+
"slices"
1314
"strconv"
1415
"strings"
1516
"syscall"
@@ -18,7 +19,9 @@ import (
1819

1920
"github.com/stretchr/testify/require"
2021
"golang.org/x/sys/unix"
22+
"golang.org/x/xerrors"
2123

24+
"github.com/coder/coder/v2/agent/agentexec"
2225
"github.com/coder/coder/v2/testutil"
2326
)
2427

@@ -36,6 +39,32 @@ func TestCLI(t *testing.T) {
3639
requireNiceScore(t, cmd.Process.Pid, 12)
3740
})
3841

42+
t.Run("FiltersEnv", func(t *testing.T) {
43+
ctx := testutil.Context(t, testutil.WaitMedium)
44+
cmd, path := cmd(ctx, t, 123, 12)
45+
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=true", agentexec.EnvProcPrioMgmt))
46+
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=123", agentexec.EnvProcOOMScore))
47+
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=12", agentexec.EnvProcNiceScore))
48+
// Ensure unrelated environment variables are preserved.
49+
cmd.Env = append(cmd.Env, "CODER_TEST_ME_AGENTEXEC=true")
50+
err := cmd.Start()
51+
require.NoError(t, err)
52+
go cmd.Wait()
53+
waitForSentinel(ctx, t, cmd, path)
54+
55+
env := procEnv(t, cmd.Process.Pid)
56+
hasExecEnvs := slices.ContainsFunc(
57+
env,
58+
func(e string) bool {
59+
return strings.HasPrefix(e, agentexec.EnvProcPrioMgmt) ||
60+
strings.HasPrefix(e, agentexec.EnvProcOOMScore) ||
61+
strings.HasPrefix(e, agentexec.EnvProcNiceScore)
62+
})
63+
require.False(t, hasExecEnvs, "expected environment variables to be filtered")
64+
userEnv := slices.Contains(env, "CODER_TEST_ME_AGENTEXEC=true")
65+
require.True(t, userEnv, "expected user environment variables to be preserved")
66+
})
67+
3968
t.Run("Defaults", func(t *testing.T) {
4069
ctx := testutil.Context(t, testutil.WaitMedium)
4170
cmd, path := cmd(ctx, t, 0, 0)
@@ -50,6 +79,32 @@ func TestCLI(t *testing.T) {
5079
requireOOMScore(t, cmd.Process.Pid, expectedOOM)
5180
requireNiceScore(t, cmd.Process.Pid, expectedNice)
5281
})
82+
83+
t.Run("Capabilities", func(t *testing.T) {
84+
testdir := filepath.Dir(TestBin)
85+
capDir := filepath.Join(testdir, "caps")
86+
err := os.Mkdir(capDir, 0o755)
87+
require.NoError(t, err)
88+
bin := buildBinary(capDir)
89+
// Try to set capabilities on the binary. This should work fine in CI but
90+
// it's possible some developers may be working in an environment where they don't have the necessary permissions.
91+
err = setCaps(t, bin, "cap_net_admin")
92+
if os.Getenv("CI") != "" {
93+
require.NoError(t, err)
94+
} else if err != nil {
95+
t.Skipf("unable to set capabilities for test: %v", err)
96+
}
97+
ctx := testutil.Context(t, testutil.WaitMedium)
98+
cmd, path := binCmd(ctx, t, bin, 123, 12)
99+
err = cmd.Start()
100+
require.NoError(t, err)
101+
go cmd.Wait()
102+
103+
waitForSentinel(ctx, t, cmd, path)
104+
// This is what we're really testing, a binary with added capabilities requires setting dumpable.
105+
requireOOMScore(t, cmd.Process.Pid, 123)
106+
requireNiceScore(t, cmd.Process.Pid, 12)
107+
})
53108
}
54109

55110
func requireNiceScore(t *testing.T, pid int, score int) {
@@ -94,7 +149,7 @@ func waitForSentinel(ctx context.Context, t *testing.T, cmd *exec.Cmd, path stri
94149
}
95150
}
96151

97-
func cmd(ctx context.Context, t *testing.T, oom, nice int) (*exec.Cmd, string) {
152+
func binCmd(ctx context.Context, t *testing.T, bin string, oom, nice int) (*exec.Cmd, string) {
98153
var (
99154
args = execArgs(oom, nice)
100155
dir = t.TempDir()
@@ -103,7 +158,7 @@ func cmd(ctx context.Context, t *testing.T, oom, nice int) (*exec.Cmd, string) {
103158

104159
args = append(args, "sh", "-c", fmt.Sprintf("touch %s && sleep 10m", file))
105160
//nolint:gosec
106-
cmd := exec.CommandContext(ctx, TestBin, args...)
161+
cmd := exec.CommandContext(ctx, bin, args...)
107162

108163
// We set this so we can also easily kill the sleep process the shell spawns.
109164
cmd.SysProcAttr = &syscall.SysProcAttr{
@@ -127,6 +182,10 @@ func cmd(ctx context.Context, t *testing.T, oom, nice int) (*exec.Cmd, string) {
127182
return cmd, file
128183
}
129184

185+
func cmd(ctx context.Context, t *testing.T, oom, nice int) (*exec.Cmd, string) {
186+
return binCmd(ctx, t, TestBin, oom, nice)
187+
}
188+
130189
func expectedOOMScore(t *testing.T) int {
131190
t.Helper()
132191

@@ -145,6 +204,15 @@ func expectedOOMScore(t *testing.T) int {
145204
return 998
146205
}
147206

207+
// procEnv returns the environment variables for a given process.
208+
func procEnv(t *testing.T, pid int) []string {
209+
t.Helper()
210+
211+
env, err := os.ReadFile(fmt.Sprintf("/proc/%d/environ", pid))
212+
require.NoError(t, err)
213+
return strings.Split(string(env), "\x00")
214+
}
215+
148216
func expectedNiceScore(t *testing.T) int {
149217
t.Helper()
150218

@@ -171,3 +239,14 @@ func execArgs(oom int, nice int) []string {
171239
execArgs = append(execArgs, "--")
172240
return execArgs
173241
}
242+
243+
func setCaps(t *testing.T, bin string, caps ...string) error {
244+
t.Helper()
245+
246+
setcap := fmt.Sprintf("sudo -n setcap %s=ep %s", strings.Join(caps, ", "), bin)
247+
out, err := exec.CommandContext(context.Background(), "sh", "-c", setcap).CombinedOutput()
248+
if err != nil {
249+
return xerrors.Errorf("setcap %q (%s): %w", setcap, out, err)
250+
}
251+
return nil
252+
}

cli/testdata/coder_templates_init_--help.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ USAGE:
66
Get started with a templated template.
77

88
OPTIONS:
9-
--id aws-devcontainer|aws-linux|aws-windows|azure-linux|devcontainer-docker|devcontainer-kubernetes|do-linux|docker|gcp-devcontainer|gcp-linux|gcp-vm-container|gcp-windows|kubernetes|nomad-docker|scratch
9+
--id aws-devcontainer|aws-linux|aws-windows|azure-linux|digitalocean-linux|docker|docker-devcontainer|gcp-devcontainer|gcp-linux|gcp-vm-container|gcp-windows|kubernetes|kubernetes-devcontainer|nomad-docker|scratch
1010
Specify a given example template by ID.
1111

1212
———

docs/admin/templates/extending-templates/docker-in-workspaces.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ nodes. Refer to sysbox's
148148
to ensure your nodes are compliant.
149149

150150
To get started with `envbox` check out the
151-
[starter template](https://github.com/coder/coder/tree/main/examples/templates/envbox)
151+
[starter template](https://github.com/coder/coder/tree/main/examples/templates/kubernetes-envbox)
152152
or visit the [repo](https://github.com/coder/envbox).
153153

154154
### Authenticating with a Private Registry

docs/admin/templates/managing-templates/devcontainers/add-devcontainer.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ choose a template from the
3838
1. Use the `template init` command to initialize your choice of image:
3939

4040
```shell
41-
coder template init --id devcontainer-kubernetes
41+
coder template init --id kubernetes-devcontainer
4242
```
4343

4444
A list of available templates is shown in the
@@ -47,7 +47,7 @@ choose a template from the
4747
1. `cd` into the directory and push the template to your Coder deployment:
4848

4949
```shell
50-
cd devcontainer-kubernetes && coder templates push
50+
cd kubernetes-devcontainer && coder templates push
5151
```
5252

5353
You can also edit the files or make changes to the files before you push them
@@ -122,8 +122,8 @@ their development environments:
122122

123123
| Template | Description |
124124
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
125-
| [Docker dev containers](https://github.com/coder/coder/tree/main/examples/templates/devcontainer-docker) | Docker provisions a development container. |
126-
| [Kubernetes dev containers](https://github.com/coder/coder/tree/main/examples/templates/devcontainer-kubernetes) | Provisions a development container on the Kubernetes cluster. |
125+
| [Docker dev containers](https://github.com/coder/coder/tree/main/examples/templates/docker-devcontainer) | Docker provisions a development container. |
126+
| [Kubernetes dev containers](https://github.com/coder/coder/tree/main/examples/templates/kubernetes-devcontainer) | Provisions a development container on the Kubernetes cluster. |
127127
| [Google Compute Engine dev container](https://github.com/coder/coder/tree/main/examples/templates/gcp-devcontainer) | Runs a development container inside a single GCP instance. It also mounts the Docker socket from the VM inside the container to enable Docker inside the workspace. |
128128
| [AWS EC2 dev container](https://github.com/coder/coder/tree/main/examples/templates/aws-devcontainer) | Runs a development container inside a single EC2 instance. It also mounts the Docker socket from the VM inside the container to enable Docker inside the workspace. |
129129

docs/reference/cli/templates_init.md

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)