Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile.serviceoffer-controller
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /serviceoffer-controller ./cmd/serviceoffer-controller

FROM gcr.io/distroless/static-debian12
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /serviceoffer-controller /serviceoffer-controller
ENTRYPOINT ["/serviceoffer-controller"]
57 changes: 57 additions & 0 deletions internal/embed/infrastructure/base/templates/llm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ apiVersion: v1
kind: Namespace
metadata:
name: llm
labels:
# Pod Security Standards: Restricted profile enforced at admission.
# The litellm pod (litellm + x402-buyer sidecar) runs as non-root with
# all caps dropped, seccomp=RuntimeDefault, and readOnlyRootFilesystem;
# write paths are routed to named emptyDir mounts.
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted

---
# ClusterIP Service + Endpoints: routes ollama.llm.svc.cluster.local → host Ollama.
Expand Down Expand Up @@ -142,6 +151,17 @@ spec:
secret.reloader.stakater.com/reload: "litellm-secrets"
spec:
terminationGracePeriodSeconds: 60
# PSS Restricted: pod-level identity. UID/GID 65532 is the nonroot
# distroless convention; the Obol LiteLLM fork's working dirs are
# routed onto emptyDir mounts below so readOnlyRootFilesystem can
# stay on without breaking Python's tempfile / cache writes.
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: litellm
# Obol fork of LiteLLM with config-only model management API.
Expand All @@ -150,6 +170,13 @@ spec:
# Source: https://github.com/ObolNetwork/litellm
image: ghcr.io/obolnetwork/litellm:sha-c16b156
imagePullPolicy: IfNotPresent
# PSS Restricted: drop all caps, no privilege escalation, RO rootfs.
# Python writes are funneled to the emptyDir mounts below.
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
args:
- --config
- /etc/litellm/config.yaml
Expand All @@ -167,10 +194,22 @@ spec:
value: "false"
- name: DISABLE_SCHEMA_UPDATE
value: "true"
# Redirect Python / HF / pip cache lookups onto the writeable
# emptyDir at /home/litellm so readOnlyRootFilesystem=true holds.
- name: HOME
value: /home/litellm
- name: XDG_CACHE_HOME
value: /home/litellm/.cache
- name: HF_HOME
value: /home/litellm/.cache/huggingface
volumeMounts:
- name: litellm-config
mountPath: /etc/litellm/config.yaml
subPath: config.yaml
- name: litellm-tmp
mountPath: /tmp
- name: litellm-home
mountPath: /home/litellm
startupProbe:
httpGet:
path: /health/readiness
Expand Down Expand Up @@ -214,6 +253,14 @@ spec:
# across flow-08/11/14/13. See internal/embed/embed_image_pin_test.go.
image: ghcr.io/obolnetwork/x402-buyer:b13254e@sha256:446d730fefbe1860e8b3245289aa8979d765ae977b7f0eaa053543e2468313cb
imagePullPolicy: IfNotPresent
# PSS Restricted: Go distroless:nonroot image already runs as
# UID 65532; only the state dir under /state needs to be writeable
# and it's already an emptyDir mount.
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
args:
- --config-dir=/config/buyer-config
- --auths-dir=/config/buyer-auths
Expand Down Expand Up @@ -258,6 +305,16 @@ spec:
items:
- key: config.yaml
path: config.yaml
# Writable /tmp for Python tempfile / multipart uploads. Sized
# modestly — LiteLLM streams responses rather than buffering them.
- name: litellm-tmp
emptyDir:
sizeLimit: 128Mi
# Writable HOME for LiteLLM's pip/HF/XDG cache lookups so the
# container can run with readOnlyRootFilesystem=true.
- name: litellm-home
emptyDir:
sizeLimit: 256Mi
- name: buyer-config
configMap:
name: x402-buyer-config
Expand Down
42 changes: 42 additions & 0 deletions internal/embed/infrastructure/base/templates/x402.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ apiVersion: v1
kind: Namespace
metadata:
name: x402
labels:
# Pod Security Standards: Restricted profile enforced at admission.
# Future Deployment edits that omit the per-pod securityContext will be
# rejected by the apiserver. Both x402-verifier and serviceoffer-controller
# run as non-root with all caps dropped, seccomp=RuntimeDefault, and
# readOnlyRootFilesystem.
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted

---
# Static gateway settings plus optional manual routes. In cluster mode the
Expand Down Expand Up @@ -210,10 +220,25 @@ spec:
app: x402-verifier
spec:
serviceAccountName: x402-verifier
# PSS Restricted: pod-level identity.
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: verifier
image: ghcr.io/obolnetwork/x402-verifier:b13254e
imagePullPolicy: IfNotPresent
# PSS Restricted: per-container hardening. Verifier is a Go binary
# reading two RO ConfigMaps; no writeable rootfs paths required.
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
ports:
- name: http
containerPort: 8080
Expand Down Expand Up @@ -281,10 +306,27 @@ spec:
app: serviceoffer-controller
spec:
serviceAccountName: serviceoffer-controller
# PSS Restricted: pod-level identity. Paired with Dockerfile
# FROM gcr.io/distroless/static-debian12:nonroot which default-runs
# as UID/GID 65532. Container escape via a Go-runtime CVE on a
# UID-0 / no-seccomp / no-cap-drop / RW-rootfs container was the
# easiest path to host pivot on k3s single-node; this closes it.
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: controller
image: ghcr.io/obolnetwork/serviceoffer-controller:b13254e
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
env:
- name: POD_NAMESPACE
valueFrom:
Expand Down
Loading