Security

Templates Security Model

Threat model and security architecture for self-hosted application templates

Templates provide one-click deployment of self-hosted applications. This document outlines the security model for the template system, including isolation, updates, and customer data protection.


Security Goals

The Templates security model aims to provide:

  • Application isolation — Each template instance runs in isolation from others
  • Supply chain integrity — Template images and configurations are verified
  • Automatic security updates — Critical patches applied without customer intervention
  • Data persistence safety — Customer data survives updates and migrations
  • Least privilege — Templates run with minimal required permissions
  • Observability — Security events and anomalies are logged and alerted

Threat Model

In Scope (Threats We Protect Against)

  • Container escape — Application breaking out of container isolation
  • Image vulnerabilities — Known CVEs in base images or dependencies
  • Configuration injection — Malicious config via environment variables
  • Cross-template attacks — One template instance attacking another
  • Secret leakage — Hardcoded credentials or exposed environment variables
  • Privilege escalation — Container gaining host-level access
  • Resource exhaustion — Template consuming excessive resources
  • Supply chain poisoning — Compromised upstream images or packages
  • Data loss during updates — Customer data corrupted or deleted on update

Out of Scope (Threats We Do Not Protect Against)

  • Application vulnerabilities — SQL injection, XSS in the deployed app itself
  • Weak default passwords — If customer doesn't change default credentials
  • Misconfigured applications — Customer exposing services unintentionally
  • Malicious templates — Templates we curate are trusted; custom templates are customer responsibility
  • Data exfiltration by application — If the app itself is malicious or compromised
  • Network attacks from within — Attacks originating from customer's other infrastructure
  • Cryptographic breaks — Failure of container encryption or TLS
  • Host compromise — If the underlying node is compromised, containers are at risk

External Threat Overview

Architecture Components

┌─────────────────────────────────────────────────────────────────┐
│                    Template Controller                          │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                   Template Registry                     │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │   │
│  │  │ PostgreSQL  │  │    Gitea    │  │    n8n      │     │   │
│  │  │ Template    │  │  Template   │  │  Template   │     │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘     │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              │                                  │
│  ┌───────────────────────────▼───────────────────────────┐     │
│  │              Template Runner (Kubernetes)             │     │
│  │  ┌───────────────────────────────────────────────┐   │     │
│  │  │  Pod Security Standards: Restricted           │   │     │
│  │  │                                               │   │     │
│  │  │  ┌─────────────┐      ┌─────────────┐        │   │     │
│  │  │  │  App        │◀────▶│  Sidecar    │        │   │     │
│  │  │  │  Container  │      │  (Monitor)  │        │   │     │
│  │  │  └─────────────┘      └─────────────┘        │   │     │
│  │  │                                               │   │     │
│  │  │  ┌─────────────┐      ┌─────────────┐        │   │     │
│  │  │  │  Data       │      │  Config     │        │   │     │
│  │  │  │  Volume     │      │  (Secrets)  │        │   │     │
│  │  │  └─────────────┘      └─────────────┘        │   │     │
│  │  └───────────────────────────────────────────────┘   │     │
│  └───────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────────┘

Trust Boundaries

Untrusted:

  • Customer-provided configuration values
  • External data sources (webhooks, APIs)
  • User-uploaded files (if applicable)

Semi-Trusted:

  • Template images (scanned, signed, but from external sources)
  • Helm charts and Kubernetes manifests

Trusted:

  • Template Controller (our code)
  • Kubernetes control plane
  • Container runtime (containerd)
  • Secrets management (external-secrets operator)

Container Isolation

Templates run in Kubernetes with strict security contexts:

apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    resources:
      limits:
        cpu: "1000m"
        memory: "1Gi"
      requests:
        cpu: "100m"
        memory: "256Mi"
    volumeMounts:
    - name: data
      mountPath: /data
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: template-data
  - name: tmp
    emptyDir: {}

Internal Threat Overview

Template Build Pipeline

1. Image Building

┌─────────────────────────────────────────────────────────────┐
│                  Template Build Pipeline                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. SOURCE                                                  │
│     └── Pull from upstream (Docker Hub, GHCR, etc.)         │
│         └── Verify image signature (cosign)                 │
│                                                             │
│  2. SCAN                                                    │
│     └── Trivy vulnerability scan                            │
│         └── Block if critical CVEs found                    │
│                                                             │
│  3. HARDEN                                                  │
│     └── Add security patches                                │
│     └── Remove unnecessary packages                         │
│     └── Configure non-root user                             │
│                                                             │
│  4. TEST                                                    │
│     └── Run in isolated environment                         │
│     └── Verify no privilege escalation                      │
│                                                             │
│  5. PUBLISH                                                 │
│     └── Push to internal registry                           │
│     └── Sign with Notary/Cosign                             │
│     └── Update vulnerability database                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2. Vulnerability Management

SeverityActionTimeline
CriticalAuto-update templateWithin 24 hours
HighNotify customers, schedule updateWithin 7 days
MediumInclude in next maintenance windowNext release
LowTrack, patch when convenientAs needed

Secret Management

Templates requiring credentials use external secrets:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: postgres-credentials
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: vault-backend
  target:
    name: postgres-secret
    creationPolicy: Owner
  data:
  - secretKey: password
    remoteRef:
      key: templates/postgres
      property: password

Security properties:

  • Secrets never stored in Git
  • Automatic rotation (database passwords every 30 days)
  • Short-lived tokens where possible
  • No secrets in environment variables (mounted as files)

Network Policies

Default deny-all with explicit allow rules:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-postgres
spec:
  podSelector:
    matchLabels:
      app: web-app
  policyTypes:
  - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
    ports:
    - protocol: TCP
      port: 5432

Update Mechanism

Templates update via GitOps (FluxCD):

┌─────────────────────────────────────────────────────────────┐
│                    Update Flow                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. New image available                                     │
│       │                                                     │
│       ▼                                                     │
│  2. Automated PR to config repo                             │
│       │                                                     │
│       ▼                                                     │
│  3. CI tests (backup, upgrade, verify)                      │
│       │                                                     │
│       ▼                                                     │
│  4. Merge to main                                           │
│       │                                                     │
│       ▼                                                     │
│  5. Flux applies canary deployment                          │
│       │                                                     │
│       ▼                                                     │
│  6. Health checks pass → full rollout                       │
│       │                                                     │
│       ▼                                                     │
│  7. Old version scaled down (data preserved)                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Rollback:

  • Automatic rollback if health checks fail
  • One-click rollback via O2S
  • Data snapshots before update

Data Protection

Persistence

Customer data is stored in PersistentVolumes:

Storage TypeUse CaseBackup
LonghornBlock storage for databasesHourly snapshots
RustFS S3Object storage for filesReal-time replication

Backup Strategy

# Velero backup schedule
apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: template-backup
spec:
  schedule: "0 */6 * * *"  # Every 6 hours
  template:
    includedNamespaces:
    - template-*
    snapshotVolumes: true
    storageLocation: default
    ttl: 720h0m0s  # 30 days

Data Isolation

  • Each template instance gets its own namespace
  • PersistentVolumes are not shared between instances
  • Cross-namespace access blocked by default

Security Controls Summary

ControlImplementationVerification
Image scanningTrivy on buildCI/CD pipeline
Image signingCosign/NotarySignature verification
Runtime securityPod Security StandardsOPA/Gatekeeper
Network isolationCilium network policiesConnectivity tests
Secret managementExternal Secrets OperatorAudit logs
Update automationFluxCD GitOpsAutomated testing
BackupVelero + Longhorn snapshotsRestore testing
MonitoringFalco for runtime threatsAlerting rules