Skip to content

Commit 254cdbe

Browse files
committed
fix(kubernetes): stage sidecar mtls files before proxy chown
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
1 parent 8e6c504 commit 254cdbe

5 files changed

Lines changed: 111 additions & 7 deletions

File tree

.agents/skills/helm-dev-environment/SKILL.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ mise run helm:skaffold:run
6565
mise run helm:skaffold:run:sidecar
6666
```
6767

68+
**Supervisor sidecar topology with TLS/mTLS enabled** (build once and leave running):
69+
```bash
70+
mise run helm:skaffold:run:sidecar-mtls
71+
```
72+
6873
Both commands build the `gateway` and `supervisor` images and deploy the OpenShell Helm
6974
chart. The sidecar profile renders an `openshell-network-init` init container for
7075
nftables setup and a non-root `openshell-supervisor-network` runtime sidecar for
@@ -265,6 +270,7 @@ for dependencies still declared in `Chart.yaml`.
265270
| `deploy/helm/openshell/ci/values-high-availability.yaml` | HA test overlay (`replicaCount: 2` with external PostgreSQL Secret) |
266271
| `deploy/helm/openshell/ci/values-keycloak.yaml` | Keycloak OIDC overlay |
267272
| `deploy/helm/openshell/ci/values-sidecar.yaml` | Supervisor sidecar topology overlay for Kubernetes e2e/dev |
273+
| `deploy/helm/openshell/ci/values-sidecar-mtls.yaml` | Supervisor sidecar topology overlay with built-in TLS/mTLS re-enabled after Skaffold dev values |
268274
| `deploy/helm/openshell/ci/values-spire.yaml` | SPIFFE/SPIRE provider token grant overlay |
269275
| `deploy/helm/openshell/ci/values-spire-stack.yaml` | SPIRE hardened chart values for local dev |
270276
| `deploy/helm/openshell/ci/values-tls-disabled.yaml` | Lint-only: TLS + auth disabled (reverse-proxy edge termination) |

crates/openshell-sandbox/src/main.rs

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ const CLIENT_TLS_DIR: &str = "/etc/openshell-tls/client";
4343
const SIDECAR_CLIENT_TLS_SUBDIR: &str = "client";
4444
#[cfg(target_os = "linux")]
4545
const CLIENT_TLS_FILES: [&str; 3] = ["ca.crt", "tls.crt", "tls.key"];
46+
#[cfg(target_os = "linux")]
47+
const SIDECAR_STATE_DIR_MODE: u32 = 0o2775;
48+
#[cfg(target_os = "linux")]
49+
const SIDECAR_TLS_DIR_MODE: u32 = 0o755;
50+
#[cfg(target_os = "linux")]
51+
const SIDECAR_TLS_STAGING_DIR_MODE: u32 = 0o700;
52+
#[cfg(target_os = "linux")]
53+
const SIDECAR_CLIENT_TLS_DIR_MODE: u32 = 0o750;
54+
#[cfg(target_os = "linux")]
55+
const SIDECAR_CLIENT_TLS_FILE_MODE: u32 = 0o400;
4656

4757
/// Which supervisor leaves are enabled in this process.
4858
///
@@ -256,6 +266,35 @@ fn prepare_sidecar_directory(path: &Path, uid: u32, gid: u32, mode: u32) -> Resu
256266
Ok(())
257267
}
258268

269+
#[cfg(target_os = "linux")]
270+
fn prepare_sidecar_directory_for_current_user(path: &Path, mode: u32) -> Result<()> {
271+
use miette::Context as _;
272+
use nix::unistd::{Gid, Uid, chown};
273+
use std::os::unix::fs::PermissionsExt;
274+
275+
let uid = Uid::current();
276+
let gid = Gid::current();
277+
std::fs::create_dir_all(path)
278+
.into_diagnostic()
279+
.wrap_err_with(|| format!("failed to create sidecar directory {}", path.display()))?;
280+
chown(path, Some(uid), Some(gid))
281+
.into_diagnostic()
282+
.wrap_err_with(|| {
283+
format!(
284+
"failed to chown sidecar directory {} to {}:{}",
285+
path.display(),
286+
uid.as_raw(),
287+
gid.as_raw()
288+
)
289+
})?;
290+
let mut perms = std::fs::metadata(path).into_diagnostic()?.permissions();
291+
perms.set_mode(mode);
292+
std::fs::set_permissions(path, perms)
293+
.into_diagnostic()
294+
.wrap_err_with(|| format!("failed to chmod sidecar directory {}", path.display()))?;
295+
Ok(())
296+
}
297+
259298
#[cfg(target_os = "linux")]
260299
fn copy_sidecar_client_tls_if_present(
261300
source_dir: &Path,
@@ -272,7 +311,7 @@ fn copy_sidecar_client_tls_if_present(
272311
}
273312

274313
let dest_dir = sidecar_tls_dir.join(SIDECAR_CLIENT_TLS_SUBDIR);
275-
prepare_sidecar_directory(&dest_dir, uid, gid, 0o750)?;
314+
prepare_sidecar_directory_for_current_user(&dest_dir, SIDECAR_TLS_STAGING_DIR_MODE)?;
276315
for file_name in CLIENT_TLS_FILES {
277316
let source = source_dir.join(file_name);
278317
if !source.exists() {
@@ -282,6 +321,13 @@ fn copy_sidecar_client_tls_if_present(
282321
));
283322
}
284323
let dest = dest_dir.join(file_name);
324+
if dest.exists() {
325+
std::fs::remove_file(&dest)
326+
.into_diagnostic()
327+
.wrap_err_with(|| {
328+
format!("failed to remove stale client TLS file {}", dest.display())
329+
})?;
330+
}
285331
std::fs::copy(&source, &dest)
286332
.into_diagnostic()
287333
.wrap_err_with(|| {
@@ -292,7 +338,7 @@ fn copy_sidecar_client_tls_if_present(
292338
)
293339
})?;
294340
let mut perms = std::fs::metadata(&dest).into_diagnostic()?.permissions();
295-
perms.set_mode(0o400);
341+
perms.set_mode(SIDECAR_CLIENT_TLS_FILE_MODE);
296342
std::fs::set_permissions(&dest, perms)
297343
.into_diagnostic()
298344
.wrap_err_with(|| {
@@ -308,6 +354,8 @@ fn copy_sidecar_client_tls_if_present(
308354
})?;
309355
}
310356

357+
prepare_sidecar_directory(&dest_dir, uid, gid, SIDECAR_CLIENT_TLS_DIR_MODE)?;
358+
311359
Ok(())
312360
}
313361

@@ -337,19 +385,23 @@ fn run_network_init(
337385
sidecar_state_dir,
338386
proxy_user_id,
339387
proxy_primary_group_id,
340-
0o2775,
388+
SIDECAR_STATE_DIR_MODE,
341389
)?;
342-
prepare_sidecar_directory(
390+
// The init container runs as uid 0 with CAP_DAC_OVERRIDE dropped. Keep the
391+
// TLS work directory owned by the init user until the client cert copy is
392+
// complete, then hand it to the long-running proxy UID.
393+
prepare_sidecar_directory_for_current_user(sidecar_tls_dir, SIDECAR_TLS_DIR_MODE)?;
394+
copy_sidecar_client_tls_if_present(
395+
Path::new(CLIENT_TLS_DIR),
343396
sidecar_tls_dir,
344397
proxy_user_id,
345398
proxy_primary_group_id,
346-
0o755,
347399
)?;
348-
copy_sidecar_client_tls_if_present(
349-
Path::new(CLIENT_TLS_DIR),
400+
prepare_sidecar_directory(
350401
sidecar_tls_dir,
351402
proxy_user_id,
352403
proxy_primary_group_id,
404+
SIDECAR_TLS_DIR_MODE,
353405
)?;
354406
openshell_supervisor_process::netns::install_sidecar_bypass_rules(proxy_user_id)
355407
}
@@ -623,4 +675,13 @@ mod tests {
623675
let err = "".parse::<Mode>().unwrap_err();
624676
assert!(err.contains("at least one"));
625677
}
678+
679+
#[cfg(target_os = "linux")]
680+
#[test]
681+
fn sidecar_tls_modes_preserve_proxy_owned_parent_and_private_client_dir() {
682+
assert_eq!(SIDECAR_TLS_DIR_MODE, 0o755);
683+
assert_eq!(SIDECAR_TLS_STAGING_DIR_MODE, 0o700);
684+
assert_eq!(SIDECAR_CLIENT_TLS_DIR_MODE, 0o750);
685+
assert_eq!(SIDECAR_CLIENT_TLS_FILE_MODE, 0o400);
686+
}
626687
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# CI/dev overlay for exercising the Kubernetes supervisor sidecar topology with
5+
# the built-in PKI init job and gateway TLS/mTLS enabled.
6+
#
7+
# Merge after ci/values-skaffold.yaml so local image pull policy and developer
8+
# auth settings remain active while this file restores TLS:
9+
# helm install ... -f values.yaml -f ci/values-skaffold.yaml -f ci/values-sidecar-mtls.yaml
10+
#
11+
# Or use the Skaffold profile:
12+
# mise run helm:skaffold:run:sidecar-mtls
13+
server:
14+
disableTls: false
15+
16+
supervisor:
17+
topology: sidecar

deploy/helm/openshell/skaffold.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,8 @@ profiles:
134134
- op: add
135135
path: /deploy/helm/releases/0/valuesFiles/-
136136
value: ci/values-sidecar.yaml
137+
- name: sidecar-mtls
138+
patches:
139+
- op: add
140+
path: /deploy/helm/releases/0/valuesFiles/-
141+
value: ci/values-sidecar-mtls.yaml

tasks/helm.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ description = "Run skaffold dev with the Kubernetes supervisor sidecar topology"
6060
dir = "deploy/helm/openshell"
6161
run = "skaffold dev -p sidecar"
6262

63+
["helm:skaffold:dev:sidecar-mtls"]
64+
description = "Run skaffold dev with the Kubernetes supervisor sidecar topology and TLS/mTLS enabled"
65+
dir = "deploy/helm/openshell"
66+
run = "skaffold dev -p sidecar-mtls"
67+
6368
["helm:skaffold:run"]
6469
description = "Run skaffold run for deploy/helm/openshell (one-shot deploy)"
6570
dir = "deploy/helm/openshell"
@@ -70,6 +75,11 @@ description = "Run skaffold run with the Kubernetes supervisor sidecar topology"
7075
dir = "deploy/helm/openshell"
7176
run = "skaffold run -p sidecar"
7277

78+
["helm:skaffold:run:sidecar-mtls"]
79+
description = "Run skaffold run with the Kubernetes supervisor sidecar topology and TLS/mTLS enabled"
80+
dir = "deploy/helm/openshell"
81+
run = "skaffold run -p sidecar-mtls"
82+
7383
["helm:skaffold:delete"]
7484
description = "Run skaffold delete for deploy/helm/openshell"
7585
dir = "deploy/helm/openshell"
@@ -80,6 +90,11 @@ description = "Run skaffold delete for the Kubernetes supervisor sidecar topolog
8090
dir = "deploy/helm/openshell"
8191
run = "skaffold delete -p sidecar"
8292

93+
["helm:skaffold:delete:sidecar-mtls"]
94+
description = "Run skaffold delete for the Kubernetes supervisor sidecar topology with TLS/mTLS enabled"
95+
dir = "deploy/helm/openshell"
96+
run = "skaffold delete -p sidecar-mtls"
97+
8398
["helm:skaffold:diagnose"]
8499
description = "Run skaffold diagnose for deploy/helm/openshell"
85100
dir = "deploy/helm/openshell"

0 commit comments

Comments
 (0)