diff --git a/.env.example b/.env.example index 290e038..7ec595f 100644 --- a/.env.example +++ b/.env.example @@ -30,13 +30,18 @@ UI_URL=https://dashboard.example.com/ # login that hasn't signed in yet still consumes a seat the moment it # does — past the cap, new sign-ins are rejected at the OAuth # callback. An admin can free seats at /admin. -ALLOWED_GITHUB_USERS=sppidy +ALLOWED_GITHUB_USERS= # Optional: pin a specific GitHub login as the bootstrap admin. Useful # when the allowlist contains multiple users and you need a particular # one to land in the admin role on first contact. # BOOTSTRAP_ADMIN=sppidy +# Exact CIDR(s) of the reverse proxy peer as seen by the server container. +# Forwarded client-IP headers are ignored from every other peer. Do not use +# 0.0.0.0/0; inspect the Docker/ingress network and list only its gateway CIDR. +TRUSTED_PROXY_CIDRS= + # Public WebSocket URL the Next.js client connects to. NEXT_PUBLIC_WS_URL=wss://dashboard.example.com/ui/ws diff --git a/.github/workflows/agent-k8s-image.yml b/.github/workflows/agent-k8s-image.yml index 2e45559..07bb605 100644 --- a/.github/workflows/agent-k8s-image.yml +++ b/.github/workflows/agent-k8s-image.yml @@ -4,11 +4,15 @@ on: push: branches: [main] paths: + - "Cargo.toml" + - "Cargo.lock" - "agent/**" + - "server/**" - "shared/**" - "Dockerfile.agent.k8s" - "helm/**" - "agent" + - "server" - "shared" - ".github/workflows/agent-k8s-image.yml" workflow_dispatch: @@ -60,7 +64,7 @@ jobs: url."https://x-access-token:${SUBMODULES_PAT}@github.com/".insteadOf \ "https://github.com/" fi - git submodule update --init --recursive --depth 1 agent shared + git submodule update --init --recursive --depth 1 agent server shared - name: Set up Buildx uses: docker/setup-buildx-action@v4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 517b67f..904f02f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,6 @@ jobs: - uses: actions/checkout@v6 with: submodules: recursive - token: ${{ secrets.SUBMODULES_PAT }} - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -44,7 +43,6 @@ jobs: - uses: actions/checkout@v6 with: submodules: recursive - token: ${{ secrets.SUBMODULES_PAT }} - uses: actions/setup-node@v4 with: node-version: 22 @@ -60,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: azure/setup-helm@v4 + - uses: azure/setup-helm@v5 - name: Lint chart run: helm lint helm/shellfleet-agent - name: Template smoke test (defaults render) diff --git a/Cargo.lock b/Cargo.lock index fc8eab6..19e0137 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,9 +90,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arc-swap" @@ -174,6 +174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" dependencies = [ "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] @@ -437,7 +438,7 @@ dependencies = [ "hyper-util", "pin-project-lite", "rustls", - "rustls-native-certs 0.8.3", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", @@ -853,16 +854,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -1018,33 +1009,6 @@ dependencies = [ "cmov", ] -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "data-encoding" version = "2.11.0" @@ -1156,30 +1120,6 @@ dependencies = [ "spki", ] -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2 0.10.9", - "subtle", - "zeroize", -] - [[package]] name = "either" version = "1.15.0" @@ -1201,7 +1141,6 @@ dependencies = [ "ff", "generic-array", "group", - "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", @@ -1253,12 +1192,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "filedescriptor" version = "0.8.3" @@ -1593,15 +1526,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac 0.12.1", -] - [[package]] name = "hmac" version = "0.12.1" @@ -1746,9 +1670,9 @@ dependencies = [ [[package]] name = "hyper-http-proxy" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad4b0a1e37510028bc4ba81d0e38d239c39671b0f0ce9e02dfa93a8133f7c08" +checksum = "8021e0ae20c08eadc94d0bdafdeda66d4f0858541c146ae6e46b219bfe58497e" dependencies = [ "bytes", "futures-util", @@ -1758,7 +1682,6 @@ dependencies = [ "hyper-rustls", "hyper-util", "pin-project-lite", - "rustls-native-certs 0.7.3", "tokio", "tokio-rustls", "tower-service", @@ -1775,7 +1698,7 @@ dependencies = [ "hyper-util", "log", "rustls", - "rustls-native-certs 0.8.3", + "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -2040,21 +1963,13 @@ version = "10.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" dependencies = [ + "aws-lc-rs", "base64", - "ed25519-dalek", "getrandom 0.2.17", - "hmac 0.12.1", "js-sys", - "p256", - "p384", - "pem", - "rand 0.8.6", - "rsa", "serde", "serde_json", - "sha2 0.10.9", "signature", - "simple_asn1", "zeroize", ] @@ -2141,9 +2056,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] [[package]] name = "leb128fmt" @@ -2157,12 +2069,6 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - [[package]] name = "libsqlite3-sys" version = "0.30.1" @@ -2283,10 +2189,10 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.2.1", + "openssl-probe", "openssl-sys", "schannel", - "security-framework 3.7.0", + "security-framework", "security-framework-sys", "tempfile", ] @@ -2322,32 +2228,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.6", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.2.2" @@ -2363,17 +2243,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2381,7 +2250,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -2421,12 +2289,6 @@ dependencies = [ "syn", ] -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -2472,18 +2334,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2 0.10.9", -] - [[package]] name = "parking" version = "2.2.1" @@ -2593,17 +2443,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -2838,6 +2677,7 @@ dependencies = [ "base64", "bytes", "futures-core", + "futures-util", "http 1.4.0", "http-body 1.0.1", "http-body-util", @@ -2856,12 +2696,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -2885,30 +2727,10 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid 0.9.6", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - [[package]] name = "rustc_version" version = "0.4.1" @@ -2947,38 +2769,16 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe 0.1.6", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework 2.11.1", -] - [[package]] name = "rustls-native-certs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.7.0", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", + "security-framework", ] [[package]] @@ -2999,7 +2799,7 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -3052,19 +2852,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.1", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - [[package]] name = "security-framework" version = "3.7.0" @@ -3072,7 +2859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags 2.11.1", - "core-foundation 0.10.1", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -3332,18 +3119,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simple_asn1" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.18", - "time", -] - [[package]] name = "slab" version = "0.4.12" @@ -3943,6 +3718,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -4124,6 +3905,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" diff --git a/Dockerfile.agent.k8s b/Dockerfile.agent.k8s index cb6edef..3f812c0 100644 --- a/Dockerfile.agent.k8s +++ b/Dockerfile.agent.k8s @@ -6,18 +6,12 @@ FROM rust:1-bookworm AS builder WORKDIR /src +COPY Cargo.toml Cargo.lock /src/ COPY agent/ /src/agent/ COPY shared/ /src/shared/ +COPY server/ /src/server/ -# Trim the workspace to what we actually build. -RUN cat > /src/Cargo.toml <<'EOF' -[workspace] -members = ["agent", "shared"] -resolver = "2" -EOF - -WORKDIR /src/agent -RUN cargo build --release --features kube +RUN cargo build --locked --release -p agent --features kube RUN strip /src/target/release/shellfleet-agent # ───────────────────────────────────────────────────────────────── diff --git a/agent b/agent index a52b9f6..69c1e6a 160000 --- a/agent +++ b/agent @@ -1 +1 @@ -Subproject commit a52b9f627816e0ad82a3dfb5fc12f934ca1fad65 +Subproject commit 69c1e6adaae06461453a46d911dd083f7544ef3f diff --git a/docker-compose.yml b/docker-compose.yml index 30fa1e6..f12aa3d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,9 @@ services: # publishing a default would mean every fresh deploy starts wide # open to whoever the default user is. - "ALLOWED_GITHUB_USERS=${ALLOWED_GITHUB_USERS:?ALLOWED_GITHUB_USERS must be set, comma-separated GitHub logins permitted to sign in}" + # Only these direct peer CIDRs may supply forwarded client-IP headers. + # Set this to the exact Docker/ingress gateway CIDR for your deployment. + - "TRUSTED_PROXY_CIDRS=${TRUSTED_PROXY_CIDRS:?TRUSTED_PROXY_CIDRS must be set to the exact nginx or ingress peer CIDR}" # Optional legacy shared agent token for one-shot bootstrapping. # Leave unset to disable; do NOT set it to "legacy_secret_off". - AGENT_SECRET=${AGENT_SECRET:-} diff --git a/helm/shellfleet-agent/templates/_helpers.tpl b/helm/shellfleet-agent/templates/_helpers.tpl index abd303f..7219362 100644 --- a/helm/shellfleet-agent/templates/_helpers.tpl +++ b/helm/shellfleet-agent/templates/_helpers.tpl @@ -43,6 +43,6 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- if .Values.serviceAccount.create -}} {{- default (include "shellfleet-agent.fullname" .) .Values.serviceAccount.name -}} {{- else -}} -{{- default "default" .Values.serviceAccount.name -}} +{{- required "serviceAccount.name is required when serviceAccount.create=false; refusing to bind cluster privileges to the namespace default account" .Values.serviceAccount.name -}} {{- end -}} {{- end -}} diff --git a/helm/shellfleet-agent/templates/clusterrole.yaml b/helm/shellfleet-agent/templates/clusterrole.yaml index 9c6eb49..7be5732 100644 --- a/helm/shellfleet-agent/templates/clusterrole.yaml +++ b/helm/shellfleet-agent/templates/clusterrole.yaml @@ -17,7 +17,6 @@ rules: - events - namespaces - nodes - - configmaps verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: @@ -75,7 +74,7 @@ rules: # currently send mutating requests; this exists so flipping it on # later doesn't require a separate Helm upgrade for the binding. - apiGroups: [""] - resources: ["pods", "services", "persistentvolumeclaims", "configmaps"] + resources: ["pods", "services", "persistentvolumeclaims"] verbs: ["create", "update", "patch", "delete"] - apiGroups: ["apps"] resources: ["deployments", "statefulsets", "daemonsets"] diff --git a/helm/shellfleet-agent/values.yaml b/helm/shellfleet-agent/values.yaml index e704777..6bddeab 100644 --- a/helm/shellfleet-agent/values.yaml +++ b/helm/shellfleet-agent/values.yaml @@ -60,7 +60,9 @@ replicaCount: 1 serviceAccount: create: true annotations: {} - # name: "" # leave empty to derive from .Release.Name + # Required when create=false. Empty is rejected so cluster privileges are + # never attached to the namespace's shared default service account. + name: "" resources: requests: diff --git a/server b/server index a7419da..16173c9 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit a7419da997c0b372c7df9372361fc1766a2eec20 +Subproject commit 16173c9cac26919cce6756e370f1381d249ff552 diff --git a/shared b/shared index 9f94668..949f0ec 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 9f94668423bb6ace311f6759a5052e7ea8d783ab +Subproject commit 949f0ec96e60ac5c72b98688fb565871dc5a9519