-
Notifications
You must be signed in to change notification settings - Fork 34
Add Kubernetes manifests and CI/CD workflows for deployment #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| name: Build and Push Docker Image | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| tags: ['v*'] | ||
| workflow_dispatch: | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_NAME: ${{ github.repository }} | ||
|
|
||
| jobs: | ||
| build-and-push: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Log in to Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Extract metadata (tags, labels) | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| tags: | | ||
| type=ref,event=branch | ||
| type=semver,pattern={{version}} | ||
| type=sha,prefix= | ||
|
|
||
| - name: Build and push Docker image | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| file: Dockerfile_simple | ||
| push: true | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: | | ||
| GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||
| name: K8s Manifests CI | ||||||
|
|
||||||
| on: | ||||||
| push: | ||||||
| paths: | ||||||
| - 'k8s/**' | ||||||
| pull_request: | ||||||
| paths: | ||||||
| - 'k8s/**' | ||||||
|
|
||||||
| jobs: | ||||||
| validate-manifests: | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - uses: actions/checkout@v4 | ||||||
|
|
||||||
| - name: Install kubeconform | ||||||
| run: | | ||||||
| curl -sSL https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz | tar xz | ||||||
| sudo mv kubeconform /usr/local/bin/ | ||||||
|
|
||||||
| - name: Validate K8s manifests (base) | ||||||
| run: | | ||||||
| kubeconform -summary -strict -kubernetes-version 1.28.0 -ignore-filename-pattern 'kustomization.yaml' k8s/base/*.yaml | ||||||
|
|
||||||
| - name: Install kubectl | ||||||
| uses: azure/setup-kubectl@v3 | ||||||
|
|
||||||
| - name: Kustomize build (template-app overlay) | ||||||
| run: | | ||||||
| kubectl kustomize k8s/overlays/template-app/ > /dev/null | ||||||
| echo "Kustomize build succeeded for template-app" | ||||||
|
|
||||||
| - name: Validate kustomized output | ||||||
| run: | | ||||||
| kubectl kustomize k8s/overlays/template-app/ | kubeconform -summary -strict -kubernetes-version 1.28.0 | ||||||
|
|
||||||
| integration-test: | ||||||
| runs-on: ubuntu-latest | ||||||
| needs: validate-manifests | ||||||
| strategy: | ||||||
| fail-fast: false | ||||||
| matrix: | ||||||
| dockerfile: | ||||||
| - Dockerfile_simple | ||||||
| - Dockerfile | ||||||
| steps: | ||||||
| - uses: actions/checkout@v4 | ||||||
|
|
||||||
| - name: Check if Dockerfile exists | ||||||
| id: check | ||||||
| run: | | ||||||
| if [ -f "${{ matrix.dockerfile }}" ]; then | ||||||
| echo "exists=true" >> "$GITHUB_OUTPUT" | ||||||
| echo "Found ${{ matrix.dockerfile }}, will run integration test" | ||||||
| else | ||||||
| echo "exists=false" >> "$GITHUB_OUTPUT" | ||||||
| echo "Skipping: ${{ matrix.dockerfile }} not found" | ||||||
| fi | ||||||
|
|
||||||
| - name: Build Docker image from current code | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| docker build -t openms-streamlit:test -f ${{ matrix.dockerfile }} . | ||||||
|
|
||||||
| - name: Create kind cluster | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| uses: helm/kind-action@v1 | ||||||
| with: | ||||||
| cluster_name: test-cluster | ||||||
|
|
||||||
| - name: Load image into kind cluster | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| kind load docker-image openms-streamlit:test --name test-cluster | ||||||
|
|
||||||
| - name: Install nginx ingress controller | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml | ||||||
| kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=90s | ||||||
|
|
||||||
| - name: Deploy with Kustomize | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| kubectl kustomize k8s/overlays/template-app/ | \ | ||||||
| sed 's|imagePullPolicy: IfNotPresent|imagePullPolicy: Never|g' | \ | ||||||
| kubectl apply -f - | ||||||
|
|
||||||
| - name: Wait for Redis to be ready | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| kubectl wait --for=condition=ready pod -l app=template-app,component=redis --timeout=60s | ||||||
|
|
||||||
| - name: Verify Redis Service is reachable | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| kubectl run redis-test --image=redis:7-alpine --rm -i --restart=Never -- redis-cli -h template-app-redis ping | ||||||
|
|
||||||
| - name: Verify all deployments are available | ||||||
| if: steps.check.outputs.exists == 'true' | ||||||
| run: | | ||||||
| kubectl wait --for=condition=available deployment -l app=template-app --timeout=120s || true | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, locate the workflow file and check its structure
fd "k8s-manifests-ci.yml" .github/workflows/Repository: OpenMS/streamlit-template Length of output: 107 🏁 Script executed: # Check the specific line and surrounding context
sed -n '95,110p' .github/workflows/k8s-manifests-ci.yml | cat -nRepository: OpenMS/streamlit-template Length of output: 694 🏁 Script executed: # Look for all instances of kubectl wait and || true patterns in workflows
rg -n "kubectl wait" .github/workflows/Repository: OpenMS/streamlit-template Length of output: 536 Remove Line 103 uses Proposed fix- kubectl wait --for=condition=available deployment -l app=template-app --timeout=120s || true
+ kubectl wait --for=condition=available deployment -l app=template-app --timeout=120s📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| kubectl get pods -l app=template-app | ||||||
| kubectl get services -l app=template-app | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |
| from datetime import datetime | ||
|
|
||
| # Define the workspaces directory | ||
| workspaces_directory = Path("/workspaces-streamlit-template") | ||
| workspaces_directory = Path(os.environ.get("WORKSPACES_DIR", "/workspaces-streamlit-template")) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate If 🛡️ Proposed safety guard-workspaces_directory = Path(os.environ.get("WORKSPACES_DIR", "/workspaces-streamlit-template"))
+raw_workspaces_dir = os.environ.get("WORKSPACES_DIR", "/workspaces-streamlit-template").strip()
+if not raw_workspaces_dir:
+ raise ValueError("WORKSPACES_DIR cannot be empty")
+workspaces_directory = Path(raw_workspaces_dir).resolve()
+if not workspaces_directory.exists() or not workspaces_directory.is_dir():
+ raise FileNotFoundError(f"WORKSPACES_DIR is invalid: {workspaces_directory}")🤖 Prompt for AI Agents |
||
|
|
||
| # Get the current time in seconds | ||
| current_time = time.time() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| apiVersion: batch/v1 | ||
| kind: CronJob | ||
| metadata: | ||
| name: workspace-cleanup | ||
| labels: | ||
| component: cleanup | ||
| spec: | ||
| schedule: "0 3 * * *" | ||
| concurrencyPolicy: Forbid | ||
| successfulJobsHistoryLimit: 3 | ||
| failedJobsHistoryLimit: 3 | ||
| jobTemplate: | ||
| spec: | ||
| template: | ||
| metadata: | ||
| labels: | ||
| component: cleanup | ||
| spec: | ||
| restartPolicy: OnFailure | ||
| containers: | ||
| - name: cleanup | ||
| image: openms-streamlit | ||
| imagePullPolicy: IfNotPresent | ||
| command: ["/bin/bash", "-c"] | ||
| args: | ||
| - | | ||
| source /root/miniforge3/bin/activate streamlit-env | ||
| exec python clean-up-workspaces.py | ||
| env: | ||
| - name: WORKSPACES_DIR | ||
| value: "/workspaces-streamlit-template" | ||
| volumeMounts: | ||
| - name: workspaces | ||
| mountPath: /workspaces-streamlit-template | ||
| resources: | ||
| requests: | ||
| memory: "256Mi" | ||
| cpu: "100m" | ||
| limits: | ||
| memory: "512Mi" | ||
| cpu: "500m" | ||
| volumes: | ||
| - name: workspaces | ||
| persistentVolumeClaim: | ||
| claimName: workspaces-pvc | ||
|
Comment on lines
+18
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check current security context in cleanup-cronjob.yaml
echo "=== Current state in cleanup-cronjob.yaml ==="
rg -n 'securityContext|allowPrivilegeEscalation|readOnlyRootFilesystem|runAsNonRoot|seccompProfile' k8s/base/cleanup-cronjob.yaml -C2
# Find and examine Dockerfile
echo ""
echo "=== Searching for Dockerfile ==="
fd -HI '^Dockerfile' | head -20
# Check Dockerfile content for USER and relevant directives
echo ""
echo "=== Dockerfile USER/WORKDIR settings ==="
fd -HI '^Dockerfile' | xargs -r head -50Repository: OpenMS/streamlit-template Length of output: 5906 Harden pod/container security context for the cleanup job. Lines 18-45 currently run with default privileges. The Dockerfile defaults to root user and installs miniforge to 🛡️ Proposed hardening patch spec:
+ securityContext:
+ seccompProfile:
+ type: RuntimeDefault
restartPolicy: OnFailure
containers:
- name: cleanup
image: openms-streamlit
imagePullPolicy: IfNotPresent
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
+ readOnlyRootFilesystem: true
command: ["/bin/bash", "-c"]To migrate to 🧰 Tools🪛 Checkov (3.2.334)[medium] 1-45: Containers should not run with allowPrivilegeEscalation (CKV_K8S_20) [medium] 1-45: Minimize the admission of root containers (CKV_K8S_23) 🪛 Trivy (0.69.1)[error] 21-41: Root file system is not read-only Container 'cleanup' of CronJob 'workspace-cleanup' should set 'securityContext.readOnlyRootFilesystem' to true Rule: KSV-0014 (IaC/Kubernetes) [error] 21-41: Default security context configured container workspace-cleanup in default namespace is using the default security context Rule: KSV-0118 (IaC/Kubernetes) [error] 18-45: Default security context configured cronjob workspace-cleanup in default namespace is using the default security context, which allows root privileges Rule: KSV-0118 (IaC/Kubernetes) 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||
| apiVersion: v1 | ||||||
| kind: ConfigMap | ||||||
| metadata: | ||||||
| name: streamlit-config | ||||||
| data: | ||||||
| settings.json: | | ||||||
| { | ||||||
| "app-name": "OpenMS WebApp Template", | ||||||
| "online_deployment": true, | ||||||
| "enable_workspaces": true, | ||||||
| "workspaces_dir": "..", | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Using 🔧 Proposed fix- "workspaces_dir": "..",
+ "workspaces_dir": "/workspaces-streamlit-template",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| "queue_settings": { | ||||||
| "default_timeout": 7200, | ||||||
| "result_ttl": 86400 | ||||||
| }, | ||||||
| "demo_workspaces": { | ||||||
| "enabled": true, | ||||||
| "source_dirs": ["example-data/workspaces"] | ||||||
| }, | ||||||
| "max_threads": { | ||||||
| "local": 4, | ||||||
| "online": 2 | ||||||
| }, | ||||||
| "analytics": { | ||||||
| "matomo": { | ||||||
| "enabled": true, | ||||||
| "url": "https://cdn.matomo.cloud/openms.matomo.cloud", | ||||||
| "tag": "yDGK8bfY" | ||||||
| }, | ||||||
| "google-analytics": { | ||||||
| "enabled": false, | ||||||
| "tag": "" | ||||||
| }, | ||||||
| "piwik-pro": { | ||||||
| "enabled": false, | ||||||
| "tag": "" | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| apiVersion: networking.k8s.io/v1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kind: Ingress | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: streamlit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| annotations: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # WebSocket support (Streamlit requires WebSockets) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/proxy-read-timeout: "86400" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/proxy-send-timeout: "86400" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/proxy-http-version: "1.1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Session affinity (user stays on same pod) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/affinity: "cookie" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/affinity-mode: "persistent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/session-cookie-name: "stroute" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/session-cookie-path: "/" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/session-cookie-samesite: "Lax" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # File upload (no limit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/proxy-body-size: "0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's read the ingress.yaml file to understand context
cat -n k8s/base/ingress.yamlRepository: OpenMS/streamlit-template Length of output: 1419 🏁 Script executed: # Check all proxy-body-size settings across the codebase
rg -n --type=yaml 'proxy-body-size' k8sRepository: OpenMS/streamlit-template Length of output: 146 🏁 Script executed: # Look for any documentation or comments about upload limits
rg -r 'upload\|body.*size\|file.*size' k8s --type=yaml -i -B 2 -A 2Repository: OpenMS/streamlit-template Length of output: 627 🌐 Web query:
💡 Result: Setting Security / operational implications of “unlimited”
Safer practicePrefer a specific maximum (e.g., Sources: 🏁 Script executed: # Check for documentation about file upload requirements or deployment notes
find . -name "README*" -o -name "*.md" | head -20 | xargs grep -l "upload\|ingress\|body" 2>/dev/null || echo "No relevant docs found"Repository: OpenMS/streamlit-template Length of output: 218 🏁 Script executed: # Search for any comments or documentation in k8s directory explaining the unlimited requirement
rg -r "upload\|body.*size" . --type=md -A 3 -B 3Repository: OpenMS/streamlit-template Length of output: 50383 🏁 Script executed: # Read the installation/user guide documentation to understand file upload requirements
cat docs/installation.md 2>/dev/null | head -200Repository: OpenMS/streamlit-template Length of output: 3677 🏁 Script executed: # Check for any application-level upload limits
rg -r "file.*upload\|upload.*size\|st.file_uploader" . --type=py -A 2 -B 2Repository: OpenMS/streamlit-template Length of output: 50384 🏁 Script executed: # Search for actual st.file_uploader usage in Python files (simpler search)
find . -name "*.py" -type f -exec grep -l "file_uploader" {} \;Repository: OpenMS/streamlit-template Length of output: 123 🏁 Script executed: # Check the app.py file directly to understand what the application does
head -100 app.pyRepository: OpenMS/streamlit-template Length of output: 2225 🏁 Script executed: # Check the file_upload.py to see what file sizes might be expected
cat content/file_upload.pyRepository: OpenMS/streamlit-template Length of output: 4580 Set a bounded request body size limit instead of unlimited.
🔧 Proposed fix- nginx.ingress.kubernetes.io/proxy-body-size: "0"
+ nginx.ingress.kubernetes.io/proxy-body-size: "100m"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Disable buffering for streaming | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nginx.ingress.kubernetes.io/proxy-buffering: "off" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| spec: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ingressClassName: nginx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rules: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - host: streamlit.openms.example.de | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| http: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| paths: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - path: / | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pathType: Prefix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backend: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| service: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: streamlit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| port: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| number: 8501 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, examine the target file to confirm the code snippet
cat k8s/base/ingress.yamlRepository: OpenMS/streamlit-template Length of output: 1195 🏁 Script executed: # Verify TLS configuration patterns in ingress manifests
rg -n --type=yaml 'kind:\s*Ingress|^\s*tls:|secretName:' k8s/Repository: OpenMS/streamlit-template Length of output: 174 🏁 Script executed: # Check if there are any TLS/secret configurations related to streamlit
rg -n --type=yaml 'streamlit|tls|TLS|secret' k8s/ | head -50Repository: OpenMS/streamlit-template Length of output: 2343 Add TLS configuration to the Ingress. Public host routing (streamlit.openms.example.de) should include TLS at the ingress layer. 🔧 Proposed fix spec:
ingressClassName: nginx
+ tls:
+ - hosts:
+ - streamlit.openms.example.de
+ secretName: streamlit-tls
rules:📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| apiVersion: kustomize.config.k8s.io/v1beta1 | ||
| kind: Kustomization | ||
|
|
||
| resources: | ||
| - namespace.yaml | ||
| - configmap.yaml | ||
| - redis.yaml | ||
| - workspace-pvc.yaml | ||
| - streamlit-deployment.yaml | ||
| - streamlit-service.yaml | ||
| - rq-worker-deployment.yaml | ||
| - ingress.yaml | ||
| - cleanup-cronjob.yaml |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| apiVersion: v1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: openms | ||
| labels: | ||
| app.kubernetes.io/part-of: openms-streamlit |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: redis | ||
| labels: | ||
| component: redis | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| component: redis | ||
| template: | ||
| metadata: | ||
| labels: | ||
| component: redis | ||
| spec: | ||
| containers: | ||
| - name: redis | ||
| image: redis:7-alpine | ||
| ports: | ||
| - containerPort: 6379 | ||
| resources: | ||
| requests: | ||
| memory: "64Mi" | ||
| cpu: "50m" | ||
| limits: | ||
| memory: "256Mi" | ||
| cpu: "250m" | ||
| readinessProbe: | ||
| exec: | ||
| command: ["redis-cli", "ping"] | ||
| initialDelaySeconds: 5 | ||
| periodSeconds: 10 | ||
| livenessProbe: | ||
| exec: | ||
| command: ["redis-cli", "ping"] | ||
| initialDelaySeconds: 15 | ||
| periodSeconds: 20 | ||
|
Comment on lines
+16
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ Verification inconclusiveScript executed: cat k8s/base/redis.yamlRepository: OpenMS/streamlit-template Repository: OpenMS/streamlit-template stdout: redis:7-alpine readOnlyRootFilesystem kubernetes security context compatibility Yes— What must be writable
SecurityContext that’s typically compatibleThe Redis official image is designed to drop privileges to a non-root securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: data
mountPath: /data
- name: tmp
mountPath: /tmp
volumes:
- name: data
persistentVolumeClaim:
claimName: redis-data
- name: tmp
emptyDir: {}Common gotchas
Sources: Kubernetes security context docs (for Script executed: # Check if there are any Redis configuration files or persistence setup in the repo
find . -type f -name "redis*" -o -name "*redis*" | head -20Repository: OpenMS/streamlit-template Repository: OpenMS/streamlit-template stdout: Harden Redis pod/container security context. The deployment currently runs with default security settings. Please add pod and container security contexts to enforce non-root execution, drop capabilities, and disable privilege escalation. Note: If enabling 🔒 Corrected hardening patch spec:
replicas: 1
selector:
matchLabels:
component: redis
template:
metadata:
labels:
component: redis
spec:
+ securityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
+ readOnlyRootFilesystem: true
+ volumeMounts:
+ - name: redis-data
+ mountPath: /data
+ - name: tmp
+ mountPath: /tmp
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "250m"
readinessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 15
periodSeconds: 20
+ volumes:
+ - name: redis-data
+ emptyDir: {}
+ - name: tmp
+ emptyDir: {}🧰 Tools🪛 Trivy (0.69.1)[error] 18-38: Root file system is not read-only Container 'redis' of Deployment 'redis' should set 'securityContext.readOnlyRootFilesystem' to true Rule: KSV-0014 (IaC/Kubernetes) [error] 18-38: Default security context configured container redis in default namespace is using the default security context Rule: KSV-0118 (IaC/Kubernetes) [error] 16-38: Default security context configured deployment redis in default namespace is using the default security context, which allows root privileges Rule: KSV-0118 (IaC/Kubernetes) 🤖 Prompt for AI Agents |
||
| --- | ||
| apiVersion: v1 | ||
| kind: Service | ||
| metadata: | ||
| name: redis | ||
| labels: | ||
| component: redis | ||
| spec: | ||
| type: ClusterIP | ||
| ports: | ||
| - port: 6379 | ||
| targetPort: 6379 | ||
| selector: | ||
| component: redis | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: OpenMS/streamlit-template
Length of output: 97
🏁 Script executed:
Repository: OpenMS/streamlit-template
Length of output: 103
🏁 Script executed:
Repository: OpenMS/streamlit-template
Length of output: 205
🏁 Script executed:
Repository: OpenMS/streamlit-template
Length of output: 487
🏁 Script executed:
# Check Dockerfile_simple cat -n Dockerfile_simpleRepository: OpenMS/streamlit-template
Length of output: 8542
🏁 Script executed:
# Check regular Dockerfile cat -n DockerfileRepository: OpenMS/streamlit-template
Length of output: 12981
🏁 Script executed:
Repository: OpenMS/streamlit-template
Length of output: 901
Use Docker BuildKit secret mounts instead of build-args for
GITHUB_TOKEN.Lines 48-49 pass the credential through
build-args, which embeds it in image layers and cache. BothDockerfile_simple(line 15) andDockerfile(line 15) convert the argument to an environment variable that persists in the final image.Since the token is only needed during build time, use
--mount=type=secretwith BuildKit to prevent the credential from being written to any image layers.🔐 Proposed workflow change
- name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: Dockerfile_simple push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - build-args: | - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + secrets: | + github_token=${{ secrets.GITHUB_TOKEN }}Update the Dockerfile to use the secret mount instead of
ARG/ENV:- ARG GITHUB_TOKEN - ENV GH_TOKEN=${GITHUB_TOKEN} + RUN --mount=type=secret,id=github_token \ + GH_TOKEN=$(cat /run/secrets/github_token) && \ + if [ -n "$GH_TOKEN" ]; then \ + gh release download -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + fi🤖 Prompt for AI Agents