Skip to content

Commit a9b4b7a

Browse files
authored
feat: security hardening (#256)
* feat: sec hardening - passlib -> pwdlib - preciser limit enforcements for k8s pods, also psa labels - bumped up dependencies (last ones got CVEs inside), also added up passwords * feat: csp nonce + replaced ansi-to-html with ansi_up - better class application * fix: detected issues - index.html: aria labels for loading wheel - worker: well-typed objs for k8s-client internal stuff - seed user: moved pwd hasher creation to main, taking num of rounds from settings obj - docs: update on diff between style-src-elem vs. style-src-attr - docs: fix of identation in nginx-config doc * fix: missing permissions for created k8s role - added * fix: deleting pods after any end (e.g. success/fail/timeout) in monitor * fix: cleaning up stale pods * fix: adding security context * fix: limit to pre-pull containers added * fix: remove backend-wide k8s_max_concurrent_pods, replaced with dedicated limits for system-wide k8s quota on cpu and memory * fix: replaced hard enforced local limits with kueue
1 parent 4339989 commit a9b4b7a

31 files changed

Lines changed: 700 additions & 282 deletions

.github/actions/e2e-ready/action.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,49 @@ runs:
2626
/home/runner/.kube/config > backend/kubeconfig.yaml
2727
chmod 644 backend/kubeconfig.yaml
2828
29+
- name: Install Kueue
30+
shell: bash
31+
run: |
32+
KUEUE_VERSION="${KUEUE_VERSION:-v0.16.1}"
33+
KUEUE_MANIFEST_SHA256="${KUEUE_MANIFEST_SHA256:-3201a66ff731be440ecfcf3c0fa5979d001b834f68389208fe7ee18017fbcfe8}"
34+
KUEUE_MANIFEST="/tmp/kueue-manifests.yaml"
35+
curl -fsSL -o "$KUEUE_MANIFEST" "https://github.com/kubernetes-sigs/kueue/releases/download/${KUEUE_VERSION}/manifests.yaml"
36+
echo "${KUEUE_MANIFEST_SHA256} ${KUEUE_MANIFEST}" | sha256sum -c -
37+
kubectl apply --server-side -f "$KUEUE_MANIFEST"
38+
rm -f "$KUEUE_MANIFEST"
39+
kubectl wait --for=condition=Available --timeout=120s \
40+
deployment/kueue-controller-manager -n kueue-system
41+
kubectl apply --server-side -f - <<'EOF'
42+
apiVersion: kueue.x-k8s.io/v1beta1
43+
kind: ResourceFlavor
44+
metadata:
45+
name: default-flavor
46+
---
47+
apiVersion: kueue.x-k8s.io/v1beta1
48+
kind: ClusterQueue
49+
metadata:
50+
name: executor-queue
51+
spec:
52+
namespaceSelector: {}
53+
resourceGroups:
54+
- coveredResources: ["cpu", "memory"]
55+
flavors:
56+
- name: default-flavor
57+
resources:
58+
- name: cpu
59+
nominalQuota: "32"
60+
- name: memory
61+
nominalQuota: "4Gi"
62+
---
63+
apiVersion: kueue.x-k8s.io/v1beta1
64+
kind: LocalQueue
65+
metadata:
66+
name: executor-queue
67+
namespace: integr8scode
68+
spec:
69+
clusterQueue: executor-queue
70+
EOF
71+
2972
- name: Use test environment config
3073
shell: bash
3174
run: |

.github/workflows/release-deploy.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,14 @@ jobs:
129129
MAILJET_FROM_ADDRESS: ${{ secrets.MAILJET_FROM_ADDRESS }}
130130
MAILJET_HOST: ${{ secrets.MAILJET_HOST }}
131131
GRAFANA_ALERT_RECIPIENTS: ${{ secrets.GRAFANA_ALERT_RECIPIENTS }}
132+
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
133+
MONGO_ROOT_USER: ${{ secrets.MONGO_ROOT_USER }}
134+
MONGO_ROOT_PASSWORD: ${{ secrets.MONGO_ROOT_PASSWORD }}
132135
with:
133136
host: ${{ secrets.DEPLOY_HOST }}
134137
username: ${{ secrets.DEPLOY_USER }}
135138
key: ${{ secrets.DEPLOY_SSH_KEY }}
136-
envs: GHCR_TOKEN,GHCR_USER,IMAGE_TAG,GRAFANA_ADMIN_USER,GRAFANA_ADMIN_PASSWORD,MAILJET_API_KEY,MAILJET_SECRET_KEY,MAILJET_FROM_ADDRESS,MAILJET_HOST,GRAFANA_ALERT_RECIPIENTS
139+
envs: GHCR_TOKEN,GHCR_USER,IMAGE_TAG,GRAFANA_ADMIN_USER,GRAFANA_ADMIN_PASSWORD,MAILJET_API_KEY,MAILJET_SECRET_KEY,MAILJET_FROM_ADDRESS,MAILJET_HOST,GRAFANA_ALERT_RECIPIENTS,REDIS_PASSWORD,MONGO_ROOT_USER,MONGO_ROOT_PASSWORD
137140
command_timeout: 10m
138141
script: |
139142
set -e
@@ -153,6 +156,9 @@ jobs:
153156
export MAILJET_FROM_ADDRESS="$MAILJET_FROM_ADDRESS"
154157
export GF_SMTP_HOST="$MAILJET_HOST"
155158
export GRAFANA_ALERT_RECIPIENTS="$GRAFANA_ALERT_RECIPIENTS"
159+
export REDIS_PASSWORD="$REDIS_PASSWORD"
160+
export MONGO_ROOT_USER="$MONGO_ROOT_USER"
161+
export MONGO_ROOT_PASSWORD="$MONGO_ROOT_PASSWORD"
156162
docker compose pull
157163
docker compose up -d --remove-orphans --no-build --wait --wait-timeout 180
158164

.github/workflows/stack-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ env:
3131
KAFKA_IMAGE: confluentinc/cp-kafka:7.8.2
3232
K3S_VERSION: v1.32.11+k3s1
3333
K3S_INSTALL_SHA256: d75e014f2d2ab5d30a318efa5c326f3b0b7596f194afcff90fa7a7a91166d5f7
34+
KUEUE_VERSION: v0.16.1
35+
KUEUE_MANIFEST_SHA256: 3201a66ff731be440ecfcf3c0fa5979d001b834f68389208fe7ee18017fbcfe8
3436

3537
jobs:
3638
# Fast unit tests (no infrastructure needed)

backend/app/core/security.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
import jwt
77
from fastapi import Request
88
from fastapi.security import OAuth2PasswordBearer
9-
from passlib.context import CryptContext
9+
from pwdlib import PasswordHash
10+
from pwdlib.hashers.bcrypt import BcryptHasher
1011

1112
from app.core.metrics import SecurityMetrics
1213
from app.domain.user import CSRFValidationError, InvalidCredentialsError
@@ -20,17 +21,15 @@ def __init__(self, settings: Settings, security_metrics: SecurityMetrics) -> Non
2021
self.settings = settings
2122
self._security_metrics = security_metrics
2223
# --8<-- [start:password_hashing]
23-
self.pwd_context = CryptContext(
24-
schemes=["bcrypt"],
25-
deprecated="auto",
26-
bcrypt__rounds=self.settings.BCRYPT_ROUNDS,
27-
)
24+
self._password_hash = PasswordHash((
25+
BcryptHasher(rounds=self.settings.BCRYPT_ROUNDS),
26+
))
2827

2928
def verify_password(self, plain_password: str, hashed_password: str) -> bool:
30-
return self.pwd_context.verify(plain_password, hashed_password) # type: ignore
29+
return self._password_hash.verify(plain_password, hashed_password)
3130

3231
def get_password_hash(self, password: str) -> str:
33-
return self.pwd_context.hash(password) # type: ignore
32+
return self._password_hash.hash(password)
3433
# --8<-- [end:password_hashing]
3534

3635
# --8<-- [start:create_access_token]

backend/app/services/k8s_worker/pod_builder.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ def _build_pod_spec(
106106
containers=[container],
107107
restart_policy="Never",
108108
active_deadline_seconds=timeout,
109+
runtime_class_name=self._settings.K8S_POD_RUNTIME_CLASS_NAME,
110+
host_users=False, # User namespace isolation — remaps container UIDs to unprivileged host UIDs
109111
volumes=[
110112
k8s_client.V1Volume(
111113
name="script-volume",
@@ -155,6 +157,7 @@ def _build_pod_metadata(
155157
) -> k8s_client.V1ObjectMeta:
156158
"""Build pod metadata with saga tracking"""
157159
labels = {"app": "integr8s", "component": "executor", "execution-id": execution_id, "language": language}
160+
labels["kueue.x-k8s.io/queue-name"] = "executor-queue"
158161

159162
labels["user-id"] = user_id[:63] # K8s label value limit
160163

0 commit comments

Comments
 (0)