chore(superset): set pod security standards

This commit is contained in:
Masaki Yatsu
2025-11-23 21:14:45 +09:00
parent 8b2fe12a8c
commit 9155fcc697
3 changed files with 259 additions and 1 deletions

View File

@@ -193,6 +193,110 @@ Data Sources (via HTTPS)
- **Database Connections**: Must use external HTTPS hostnames for authenticated connections - **Database Connections**: Must use external HTTPS hostnames for authenticated connections
- **Role Mapping**: Keycloak groups map to Superset roles (Admin, Alpha, Gamma) - **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 ## Authentication
### User Login (OAuth) ### User Login (OAuth)

View File

@@ -157,6 +157,9 @@ install:
just create-namespace just create-namespace
kubectl label namespace ${SUPERSET_NAMESPACE} \
pod-security.kubernetes.io/enforce=baseline --overwrite
# Create Superset database and user # Create Superset database and user
POSTGRES_PASSWORD=$(just utils::random-password) POSTGRES_PASSWORD=$(just utils::random-password)
just postgres::create-user-and-db superset superset "${POSTGRES_PASSWORD}" just postgres::create-user-and-db superset superset "${POSTGRES_PASSWORD}"

View File

@@ -1,5 +1,5 @@
# Apache Superset Helm values # Apache Superset Helm values
# Generated by gomplate # https://github.com/apache/superset/tree/master/helm/superset
# Service configuration # Service configuration
service: service:
@@ -26,9 +26,88 @@ init:
createAdmin: false createAdmin: false
loadExamples: 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 # Superset node configuration
supersetNode: supersetNode:
replicaCount: 1 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: connections:
# Redis configuration # Redis configuration
redis_host: superset-redis-headless redis_host: superset-redis-headless
@@ -47,6 +126,45 @@ supersetNode:
supersetWorker: supersetWorker:
replicaCount: 1 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) # Database configuration (use existing PostgreSQL)
postgresql: postgresql:
enabled: false enabled: false
@@ -65,6 +183,26 @@ redis:
master: master:
persistence: persistence:
enabled: false 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 # Extra environment variables
extraEnv: extraEnv:
@@ -170,3 +308,16 @@ bootstrapScript: |
#!/bin/bash #!/bin/bash
uv pip install psycopg2-binary sqlalchemy-trino authlib uv pip install psycopg2-binary sqlalchemy-trino authlib
if [ ! -f ~/bootstrap ]; then echo "Bootstrap complete" > ~/bootstrap; fi 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