chore(superset): set pod security standards
This commit is contained in:
@@ -193,6 +193,110 @@ Data Sources (via HTTPS)
|
||||
- **Database Connections**: Must use external HTTPS hostnames for authenticated connections
|
||||
- **Role Mapping**: Keycloak groups map to Superset roles (Admin, Alpha, Gamma)
|
||||
|
||||
## Security
|
||||
|
||||
### Pod Security Standards
|
||||
|
||||
This deployment applies Kubernetes Pod Security Standards at the **baseline** level.
|
||||
|
||||
#### Security Configuration
|
||||
|
||||
**Namespace Level**:
|
||||
|
||||
```bash
|
||||
pod-security.kubernetes.io/enforce=baseline
|
||||
```
|
||||
|
||||
**Container Security Context**:
|
||||
|
||||
- `runAsUser: 1000` (non-root user)
|
||||
- `runAsNonRoot: true`
|
||||
- `allowPrivilegeEscalation: false`
|
||||
- `capabilities: drop ALL`
|
||||
- `seccompProfile: RuntimeDefault`
|
||||
- `readOnlyRootFilesystem: false` (required for Python package installation)
|
||||
|
||||
**Init Container (copy-venv)**:
|
||||
|
||||
- Purpose: Copy Python virtual environment to writable emptyDir volume
|
||||
- `runAsUser: 0` (root) - required for `chown` operation
|
||||
- Runs before main container to prepare writable `.venv` directory
|
||||
|
||||
**Volume Configuration**:
|
||||
|
||||
Two emptyDir volumes are mounted for write operations:
|
||||
|
||||
1. `/app/.venv` - Python virtual environment (copied from image and made writable)
|
||||
2. `/app/superset_home/.cache` - uv package manager cache
|
||||
|
||||
#### Why Baseline Instead of Restricted?
|
||||
|
||||
The **baseline** level is required because:
|
||||
|
||||
1. **Init container needs root**: The `copy-venv` initContainer must run as root (uid=0) to:
|
||||
- Copy Python virtual environment from read-only image layer
|
||||
- Change ownership to uid=1000 for main container
|
||||
- Enable bootstrap script to install additional packages
|
||||
|
||||
2. **Image architecture limitation**: The official Apache Superset image:
|
||||
- Installs Python packages as root during build → `/app/.venv` owned by root
|
||||
- Runs application as uid=1000
|
||||
- Does not provide writable `.venv` for runtime package installation
|
||||
|
||||
3. **Restricted would require**:
|
||||
- All containers (including init) to run as non-root
|
||||
- Custom Docker image with pre-chowned directories
|
||||
- Or forgoing bootstrap script package installation
|
||||
|
||||
**Security Impact**:
|
||||
|
||||
- **Main application containers run as non-root** (uid=1000) ✓
|
||||
- **Init container runs as root** (uid=0) for ~2 seconds during pod startup
|
||||
- **Application runtime is non-root** - the attack surface is minimal
|
||||
- All other security controls (capabilities drop, seccomp, etc.) are applied
|
||||
|
||||
#### Achieving Restricted Level (Optional)
|
||||
|
||||
To deploy with **restricted** Pod Security Standards, create a custom Docker image:
|
||||
|
||||
```dockerfile
|
||||
FROM apachesuperset.docker.scarf.sh/apache/superset:5.0.0
|
||||
|
||||
# Switch to root to install packages and fix permissions
|
||||
USER root
|
||||
|
||||
# Install required packages into the existing venv
|
||||
RUN . /app/.venv/bin/activate && \
|
||||
uv pip install psycopg2-binary sqlalchemy-trino authlib
|
||||
|
||||
# Change ownership to superset user (uid=1000)
|
||||
RUN chown -R superset:superset /app/.venv
|
||||
|
||||
# Switch back to superset user
|
||||
USER superset
|
||||
```
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. Build and push custom image to your registry
|
||||
2. Update `superset-values.gomplate.yaml`:
|
||||
- Change `image.repository` to your custom image
|
||||
- Remove `extraVolumes` and `extraVolumeMounts` (emptyDir no longer needed)
|
||||
- Remove `initContainers` sections from `init`, `supersetNode`, `supersetWorker`
|
||||
- Add `runAsNonRoot: true` to Pod-level `podSecurityContext`
|
||||
- Remove `bootstrapScript` (packages already installed in image)
|
||||
3. Update namespace label to `restricted`:
|
||||
|
||||
```bash
|
||||
kubectl label namespace superset pod-security.kubernetes.io/enforce=restricted --overwrite
|
||||
```
|
||||
|
||||
**Trade-offs**:
|
||||
|
||||
- **Pros**: Strictest security posture, all containers run as non-root
|
||||
- **Cons**: Custom image maintenance required (rebuild on Superset version updates)
|
||||
- **Current approach**: Uses official images with minimal customization via bootstrap script
|
||||
|
||||
## Authentication
|
||||
|
||||
### User Login (OAuth)
|
||||
|
||||
@@ -157,6 +157,9 @@ install:
|
||||
|
||||
just create-namespace
|
||||
|
||||
kubectl label namespace ${SUPERSET_NAMESPACE} \
|
||||
pod-security.kubernetes.io/enforce=baseline --overwrite
|
||||
|
||||
# Create Superset database and user
|
||||
POSTGRES_PASSWORD=$(just utils::random-password)
|
||||
just postgres::create-user-and-db superset superset "${POSTGRES_PASSWORD}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Apache Superset Helm values
|
||||
# Generated by gomplate
|
||||
# https://github.com/apache/superset/tree/master/helm/superset
|
||||
|
||||
# Service configuration
|
||||
service:
|
||||
@@ -26,9 +26,88 @@ init:
|
||||
createAdmin: false
|
||||
loadExamples: false
|
||||
|
||||
# Security context for Pod Security Standards (baseline)
|
||||
podSecurityContext:
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
|
||||
initContainers:
|
||||
- name: copy-venv
|
||||
image: apachesuperset.docker.scarf.sh/apache/superset:5.0.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
if [ ! -d /venv-target/lib ]; then
|
||||
echo "Copying .venv to emptyDir..."
|
||||
cp -a /app/.venv/. /venv-target/
|
||||
chown -R 1000:1000 /venv-target
|
||||
else
|
||||
echo ".venv already initialized"
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: superset-venv
|
||||
mountPath: /venv-target
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
|
||||
# Superset node configuration
|
||||
supersetNode:
|
||||
replicaCount: 1
|
||||
|
||||
# Security context for Pod Security Standards (baseline)
|
||||
podSecurityContext:
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
|
||||
initContainers:
|
||||
- name: copy-venv
|
||||
image: apachesuperset.docker.scarf.sh/apache/superset:5.0.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
if [ ! -d /venv-target/lib ]; then
|
||||
echo "Copying .venv to emptyDir..."
|
||||
cp -a /app/.venv/. /venv-target/
|
||||
chown -R 1000:1000 /venv-target
|
||||
else
|
||||
echo ".venv already initialized"
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: superset-venv
|
||||
mountPath: /venv-target
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
|
||||
connections:
|
||||
# Redis configuration
|
||||
redis_host: superset-redis-headless
|
||||
@@ -47,6 +126,45 @@ supersetNode:
|
||||
supersetWorker:
|
||||
replicaCount: 1
|
||||
|
||||
# Security context for Pod Security Standards (baseline)
|
||||
podSecurityContext:
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
|
||||
initContainers:
|
||||
- name: copy-venv
|
||||
image: apachesuperset.docker.scarf.sh/apache/superset:5.0.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
if [ ! -d /venv-target/lib ]; then
|
||||
echo "Copying .venv to emptyDir..."
|
||||
cp -a /app/.venv/. /venv-target/
|
||||
chown -R 1000:1000 /venv-target
|
||||
else
|
||||
echo ".venv already initialized"
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: superset-venv
|
||||
mountPath: /venv-target
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
|
||||
# Database configuration (use existing PostgreSQL)
|
||||
postgresql:
|
||||
enabled: false
|
||||
@@ -65,6 +183,26 @@ redis:
|
||||
master:
|
||||
persistence:
|
||||
enabled: false
|
||||
# Security context for Pod Security Standards (restricted)
|
||||
podSecurityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
|
||||
# Extra environment variables
|
||||
extraEnv:
|
||||
@@ -170,3 +308,16 @@ bootstrapScript: |
|
||||
#!/bin/bash
|
||||
uv pip install psycopg2-binary sqlalchemy-trino authlib
|
||||
if [ ! -f ~/bootstrap ]; then echo "Bootstrap complete" > ~/bootstrap; fi
|
||||
|
||||
# Extra volumes and volume mounts for cache directories and venv
|
||||
extraVolumes:
|
||||
- name: superset-cache
|
||||
emptyDir: {}
|
||||
- name: superset-venv
|
||||
emptyDir: {}
|
||||
|
||||
extraVolumeMounts:
|
||||
- name: superset-cache
|
||||
mountPath: /app/superset_home/.cache
|
||||
- name: superset-venv
|
||||
mountPath: /app/.venv
|
||||
|
||||
Reference in New Issue
Block a user