mirror of
https://github.com/sredevopsorg/deepseek-kubernetes-sandbox.git
synced 2026-11-03 01:13:24 +00:00
fix(gateway)!: replace token cookies with revocable sessions and harden abuse controls (#40)
- Replace user:token cookies with opaque server-side sessions, revocable on logout, delete, recreate and expiry; bind every credential to the path user and add cross-user regression tests. - Rate-limit /users and /login, add optional signup invite codes and form same-origin checks, and unify login failures to 401. - Require explicit ALLOW_INSECURE_COOKIES for plain HTTP, use __Host-dsh_gw_session when secure, and fix secure cookie deletion. - Strip client-supplied forwarding/SSO headers, add an optional upstream allowlist, per-user in-flight and WebSocket message caps, credential-free handshake errors, and sweep-time state pruning. - Digest-pin base images, hash-pin gateway dependencies, verify DSH_COMMIT in the build, and run the gateway image as non-root with a pinned Python base. - Update manifests, production checklist, architecture/security docs and add an SSO preflight script. BREAKING CHANGE: existing dsh_gw_session=<user>:<token> cookies are invalid; users must sign in again. Unknown users on POST /login now return 401 instead of 404, and the gateway refuses COOKIE_SECURE=false unless ALLOW_INSECURE_COOKIES=1. Co-authored-by: SREDevOpsAgents <agents@sredevops.org>
This commit is contained in:
committed by
GitHub
parent
2f959e6afb
commit
1a929e3e24
@@ -22,14 +22,48 @@ expect() { # <file> <literal>
|
||||
|
||||
# Main Dockerfile build args
|
||||
expect Dockerfile "ARG DSH_VERSION=$DSH_VERSION"
|
||||
expect Dockerfile "ARG DSH_COMMIT=$DSH_COMMIT"
|
||||
expect Dockerfile "ARG PNPM_VERSION=$PNPM_VERSION"
|
||||
expect Dockerfile "ARG RELEASE_REVISION=$RELEASE_REVISION"
|
||||
expect Dockerfile "ARG NODE_BUILD_IMAGE=$NODE_BUILD_IMAGE"
|
||||
expect Dockerfile "ARG NODE_RUNTIME_IMAGE=$NODE_RUNTIME_IMAGE"
|
||||
# The build stage must fail closed when the tag no longer resolves to DSH_COMMIT.
|
||||
expect Dockerfile "git -C /src rev-parse HEAD"
|
||||
|
||||
# Example gateway build base image
|
||||
expect examples/dsh-agents-as-a-service/gateway/Dockerfile \
|
||||
"ARG GATEWAY_PYTHON_IMAGE=$GATEWAY_PYTHON_IMAGE"
|
||||
|
||||
# Local configuration mirrored for humans
|
||||
expect .env.sample "DSH_VERSION=$DSH_VERSION"
|
||||
expect .env.sample "DSH_COMMIT=$DSH_COMMIT"
|
||||
expect .env.sample "PNPM_VERSION=$PNPM_VERSION"
|
||||
expect .env.sample "RELEASE_REVISION=$RELEASE_REVISION"
|
||||
|
||||
# Human-facing docs embed the resolved commit in the build example and table.
|
||||
expect docs/main-image.md "$DSH_COMMIT"
|
||||
expect docs/versioning.md "$DSH_COMMIT"
|
||||
|
||||
# Every container image is pinned to an immutable multi-arch index digest, not
|
||||
# a mutable tag. This is a supply-chain invariant: don't "fix" a failing check
|
||||
# by dropping the digest.
|
||||
for image_var in NODE_BUILD_IMAGE NODE_RUNTIME_IMAGE GATEWAY_PYTHON_IMAGE OAUTH2_PROXY_IMAGE; do
|
||||
image_value="${!image_var:-}"
|
||||
if ! printf '%s' "$image_value" | grep -qE '@sha256:[0-9a-f]{64}$'; then
|
||||
err "versions.env: $image_var must be digest-pinned (name:tag@sha256:<64 hex>), got: ${image_value:-<unset>}"
|
||||
fi
|
||||
done
|
||||
|
||||
if ! printf '%s' "${DSH_COMMIT:-}" | grep -qE '^[0-9a-f]{40}$'; then
|
||||
err "versions.env: DSH_COMMIT must be a full 40-character commit SHA, got: ${DSH_COMMIT:-<unset>}"
|
||||
fi
|
||||
|
||||
# Build entrypoints must pass DSH_COMMIT rather than trusting the tag alone.
|
||||
expect Makefile "--build-arg DSH_COMMIT=\$(DSH_COMMIT)"
|
||||
for wf in build-main.yaml release.yaml ci-image.yaml; do
|
||||
expect ".github/workflows/$wf" "DSH_COMMIT="
|
||||
done
|
||||
|
||||
# Image references in the example manifests
|
||||
for f in \
|
||||
examples/dsh-agents-as-a-service/manifests/10-sandboxtemplate.yaml \
|
||||
@@ -38,6 +72,10 @@ for f in \
|
||||
done
|
||||
expect examples/dsh-agents-as-a-service/manifests/50-gateway.yaml "image: $GATEWAY_IMAGE:$IDENTITY"
|
||||
|
||||
# The oauth2-proxy exposure variant pins the digest, not the mutable tag.
|
||||
expect examples/dsh-agents-as-a-service/manifests/exposure/gateway-api-sso/72-oauth2-proxy.yaml \
|
||||
"image: $OAUTH2_PROXY_IMAGE"
|
||||
|
||||
# The gateway image must live below the repository image (one package per
|
||||
# example component, no unrelated top-level packages).
|
||||
case "$GATEWAY_IMAGE" in
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#
|
||||
# ./scripts/set-version.sh dsh-v0.2.2-rc.1 # new DSH ref, no revision
|
||||
# ./scripts/set-version.sh dsh-v0.2.1-rc.5 r3 # same ref, new repo revision
|
||||
#
|
||||
# For a new DSH ref the script resolves the tag's commit with "git ls-remote"
|
||||
# (peeled ^{} commit first, tag ref as fallback) and rewrites DSH_COMMIT in
|
||||
# versions.env, the Dockerfile ARG default and .env.sample. A revision-only bump
|
||||
# at the same ref keeps the existing commit and needs no network access.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
@@ -23,9 +28,11 @@ if [ -n "$new_revision" ]; then
|
||||
fi
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
old_version="$(. ./versions.env; printf '%s' "$DSH_VERSION")"
|
||||
# shellcheck source=/dev/null
|
||||
old_revision="$(. ./versions.env; printf '%s' "${RELEASE_REVISION:-}")"
|
||||
. ./versions.env
|
||||
|
||||
old_version="$DSH_VERSION"
|
||||
old_revision="${RELEASE_REVISION:-}"
|
||||
old_commit="${DSH_COMMIT:-}"
|
||||
|
||||
old_identity="$old_version"
|
||||
[ -z "$old_revision" ] || old_identity="$old_version-$old_revision"
|
||||
@@ -37,6 +44,29 @@ if [ "$old_identity" = "$new_identity" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Resolve the commit the new upstream tag points at. Annotated tags expose a
|
||||
# peeled ^{} line (the commit) and a tag-object line; prefer the peeled commit
|
||||
# and fall back to the tag ref for lightweight tags. A repository-only revision
|
||||
# bump keeps the already pinned commit, so it needs no network.
|
||||
new_commit="$old_commit"
|
||||
if [ "$new_version" != "$old_version" ] || [ -z "$new_commit" ]; then
|
||||
if ! refs="$(git ls-remote --tags --exit-code "$DSH_REPO" \
|
||||
"refs/tags/$new_version^{}" "refs/tags/$new_version")"; then
|
||||
echo "set-version: upstream tag '$new_version' not found in $DSH_REPO" >&2
|
||||
exit 1
|
||||
fi
|
||||
new_commit="$(printf '%s\n' "$refs" \
|
||||
| awk -v t="refs/tags/$new_version^{}" '$2 == t {print $1}' | head -n1)"
|
||||
if [ -z "$new_commit" ]; then
|
||||
new_commit="$(printf '%s\n' "$refs" \
|
||||
| awk -v t="refs/tags/$new_version" '$2 == t {print $1}' | head -n1)"
|
||||
fi
|
||||
if ! printf '%s' "$new_commit" | grep -qE '^[0-9a-f]{40}$'; then
|
||||
echo "set-version: could not resolve a commit for '$new_version' in $DSH_REPO" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Replace the identity first (it contains the base version), then the base
|
||||
# version itself. CHANGELOG.md is generated from tags, so it is left alone.
|
||||
while IFS= read -r file; do
|
||||
@@ -48,17 +78,30 @@ while IFS= read -r file; do
|
||||
sed -i "s/$old_identity/$new_identity/g; s/$old_version/$new_version/g" "$file"
|
||||
done < <(git ls-files)
|
||||
|
||||
# versions.env is authoritative: write both keys explicitly so an empty
|
||||
# versions.env is authoritative: write these keys explicitly so an empty
|
||||
# revision is stored as an empty value.
|
||||
sed -i "s/^DSH_VERSION=.*/DSH_VERSION=$new_version/" versions.env
|
||||
sed -i "s/^RELEASE_REVISION=.*/RELEASE_REVISION=$new_revision/" versions.env
|
||||
sed -i "s/^DSH_COMMIT=.*/DSH_COMMIT=$new_commit/" versions.env
|
||||
|
||||
# Values stored separately from the full identity: Dockerfile ARG defaults and
|
||||
# the human-facing sample cannot be reached by the identity replacement above.
|
||||
sed -i "s/^ARG DSH_VERSION=.*/ARG DSH_VERSION=$new_version/" Dockerfile
|
||||
sed -i "s/^ARG RELEASE_REVISION=.*/ARG RELEASE_REVISION=$new_revision/" Dockerfile
|
||||
sed -i "s/^ARG DSH_COMMIT=.*/ARG DSH_COMMIT=$new_commit/" Dockerfile
|
||||
sed -i "s/^DSH_VERSION=.*/DSH_VERSION=$new_version/" .env.sample
|
||||
sed -i "s/^RELEASE_REVISION=.*/RELEASE_REVISION=$new_revision/" .env.sample
|
||||
sed -i "s/^DSH_COMMIT=.*/DSH_COMMIT=$new_commit/" .env.sample
|
||||
|
||||
# The human-facing build examples embed the full SHA, which the version
|
||||
# replacement above cannot see. Keep them in step; check-versions.sh verifies.
|
||||
if [ -n "$old_commit" ] && [ "$old_commit" != "$new_commit" ]; then
|
||||
for doc in docs/main-image.md docs/versioning.md; do
|
||||
[ -f "$doc" ] || continue
|
||||
sed -i "s/$old_commit/$new_commit/g" "$doc"
|
||||
done
|
||||
fi
|
||||
|
||||
echo "set-version: $old_identity -> $new_identity"
|
||||
echo "set-version: DSH_COMMIT=$new_commit"
|
||||
echo "set-version: run 'make check' and review the diff"
|
||||
|
||||
Reference in New Issue
Block a user