Skip to content

Production

VM

Provision a VM using the provider of your choice. Ensure DNS records are setup properly (both platform and wildcard challenge url entries):

.ctf.yourdomain.example 127.0.0.1
.ctf.yourdomain.example ::1
*.ctf.yourdomain.example 127.0.0.1
*.ctf.yourdomain.example ::1

Firewall

Ensure that the following ports are open:

80/tcp    platform http
443/tcp   platform https
1337/tcp  challenge https
31337/tcp challenge tls -> tcp proxy
30000-65535/tcp nodeport services (if needed)
30000-65535/udp nodeport services (if needed)

K3s Installation

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC='--flannel-backend=none --disable-network-policy --disable=traefik --tls-san <your-public-domain>' sh -

Helm preparation

helm repo add dex https://charts.dexidp.io
helm repo add jetstack https://charts.jetstack.io
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo add cilium https://helm.cilium.io/
helm repo add traefik https://traefik.github.io/charts
helm repo add uptrace https://charts.uptrace.dev
helm repo update

Cilium

cat <<EOF | helm install --wait cilium cilium/cilium -n cilium --version 1.17.4 --create-namespace -f -
ipam:
  mode: kubernetes
operator:
  replicas: 1
bandwidthManager:
  enabled: true
hubble:
  relay:
    enabled: false
  ui:
    enabled: false
  tls:
    auto:
      method: cronJob
EOF

Traefik

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/experimental-install.yaml
cat <<EOF | helm install --wait traefik traefik/traefik --version v35.4.0 -n traefik --create-namespace -f -
globalArguments:
  - "--global.checknewversion=false"
  - "--global.sendanonymoususage=false"
gateway:
  enabled: true
  name: "traefik-gateway"
  listeners:
    web:
      port: 8000
      protocol: HTTP
      namespacePolicy: All
    websecure:
      port: 8443
      protocol: HTTPS
      namespacePolicy: All
      certificateRefs:
        - kind: Secret
          name: berg-gateway-tls
      mode: Terminate
    http-chall:
      port: 1337
      protocol: HTTP
      namespacePolicy: All
    https-chall:
      port: 1337
      protocol: HTTPS
      namespacePolicy: All
      certificateRefs:
        - kind: Secret
          name: berg-gateway-tls
      mode: Terminate
    tls-chall:
      port: 31337
      protocol: TLS
      namespacePolicy: All
      certificateRefs:
        - kind: Secret
          name: berg-gateway-tls
      mode: Terminate
gatewayClass:
  enabled: true
providers:
  kubernetesIngress:
    publishedService:
      enabled: true
  kubernetesGateway:
    enabled: true
    experimentalChannel: true
service:
  type: LoadBalancer
ports:
  web:
    port: 8000
    exposedPort: 80
    nodePort: 30080
  websecure:
    port: 8443
    exposedPort: 443
    nodePort: 30443
  http-chall:
    protocol: TCP
    port: 1337
    exposedPort: 1337
    nodePort: 30337
    expose:
      default: true
  tls-chall:
    protocol: TCP
    port: 31337
    exposedPort: 31337
    nodePort: 31337
    expose:
      default: true
tlsOptions:
  default:
    sniStrict: false
    alpnProtocols:
      - http/1.1
EOF

TLS Certificate

Install cert-manager to manage the creation and renewal of your gateway certificate.

Alternatively, you can also use certbot and do it manually:

certbot certonly --manual --preferred-challenges=dns -d '*.ctf.yourdomain.example' -d 'ctf.yourdomain.example'

k3s kubectl create secret tls -n traefik berg-gateway-tls --cert=/etc/letsencrypt/live/ctf.yourdomain.example/fullchain.pem --key=/etc/letsencrypt/live/ctf.yourdomain.example/privkey.pem

ArgoCD

k3s kubectl create namespace argocd
k3s kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
cat <<EOF | k3s kubectl apply -n argocd -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-gpg-keys-cm
    app.kubernetes.io/part-of: argocd
data:
  resource.exclusions: |
    - apiGroups:
        - cilium.io
      kinds:
        - CiliumIdentity
      clusters:
        - "*"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-gpg-keys-cm
    app.kubernetes.io/part-of: argocd
data:
  server.insecure: 'true'
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: argocd
  namespace: argocd
spec:
  hostnames:
    - argocd.ctf.yourdomain.example
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: traefik-gateway
      namespace: traefik
      sectionName: websecure
  rules:
    - backendRefs:
        - group: ''
          kind: Service
          name: argocd-server
          port: 80
          weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /
EOF

Adding ArgoCD Applications

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cnpg-operator
  namespace: argocd
spec:
  destination:
    namespace: cnpg-system
    server: https://kubernetes.default.svc
  project: default
  source:
    chart: cloudnative-pg
    repoURL: https://cloudnative-pg.github.io/charts
    targetRevision: 0.24.0
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: berg-db
  namespace: argocd
spec:
  destination:
    namespace: berg
    server: https://kubernetes.default.svc
  project: default
  source:
    chart: cluster
    helm:
      values: |-
        cluster:
          instances: 1
          storage:
            size: 15Gi
    repoURL: https://cloudnative-pg.github.io/charts
    targetRevision: 0.3.1
  syncPolicy:
    syncOptions:
      - CreateNamespace=true

Berg

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: berg
  namespace: argocd
spec:
  destination:
    namespace: berg
    server: https://kubernetes.default.svc
  project: default
  source:
    chart: berg
    helm:
      values: |
        gateway:
          domain: ctf.yourdomain.example
          name: "traefik-gateway"
          namespace: "traefik"
          httpListenerName: "web"
          httpsListenerName: "websecure"
          httpRouteRedirectListenerName: "http-chall"
          httpRouteListenerName: "https-chall"
          tlsRouteListenerName: "tls-chall"
        handout:
          enabled: false
        berg:
          domain: ctf.yourdomain.example
          pullSecretName: "berg-pull-secret"
          postgresql:
            existingSecret:
              name: "berg-db-cluster-app"
          discord:
            clientId: "CLIENT_ID"
            clientSecret: "CLIENT_SECRET"
            botToken: "DISCORD_BOT_TOKEN"
            notificationGuildId: "PUBLIC_DISCORD_SERVER"
            notificationChannelId: "PUBLIC_DISCORD_SOLVE_CHANNEL"
          ctf:
            eventName: "YOUR CTF"
            eventOrganiser: "YOUR ORGANIZERS"
            eventLogoUrl: https://yourdomain.example/logo.png
            challengeDomain: "ctf.yourdomain.example"
            start: "2000-01-01T00:00:00+00:00"
            end: "2099-12-31T00:00:00+00:00"
            teams: false
            allowAnonymousAccess: true
            scoring:
              numSolvesBeforeMinimum: 5
    repoURL: ghcr.io/norelect/charts
    targetRevision: 5.0.2

Pull Secret

# Either create the pull secret from an existing file
kubectl create secret docker-registry berg-pull-secret -n berg --from-file=~/.docker/config.json
# Or create a new one from scratch
kubectl create secret docker-registry berg-pull-secret -n berg --docker-server=ghcr.io --docker-username=USER --docker-password=TOKEN