GZCTF 单实例快速部署指南

Apr 22, 2026 · 9367 字

GZ::CTF 是一个基于 ASP.NET Core 的开源 CTF 平台,采用 Docker 或 K8s 作为容器部署后端,提供了可自定义的题目类型、动态容器和动态分值功能。

本文将介绍如何在单节点 k3s 集群上快速部署 GZCTF,适合初学者和小型 CTF 赛事使用。

目标环境

  • 系统:Ubuntu 22.04+ 或 CentOS 7

安装前准备

Ubuntu

sudo apt update
sudo apt install -y curl ca-certificates gnupg lsb-release
sudo swapoff -a
sudo sed -ri 's@^([^#].*\sswap\s+sw\s+.*)$@#\1@g' /etc/fstab
sudo modprobe br_netfilter
cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

CentOS 7

sudo yum install -y curl ca-certificates iptables-services
sudo swapoff -a
sudo sed -ri 's@^([^#].*\sswap\s+sw\s+.*)$@#\1@g' /etc/fstab
cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
sudo modprobe br_netfilter
cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

安装 k3s

curl -sfL https://get.k3s.io | sh -

准备部署目录

sudo mkdir -p /opt/gzctf/manifests
sudo chown -R $(id -u):$(id -g) /opt/gzctf
cd /opt/gzctf

创建基础资源

在 /opt/gzctf/manifests 下创建以下文件。

01-namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: gzctf-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gzctf-sa
  namespace: gzctf-server
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: gzctf-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: gzctf-sa
    namespace: gzctf-server

02-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: gzctf-config
  namespace: gzctf-server
data:
  appsettings.json: |
    {
     "ConnectionStrings": {
       "Database": "Host=gzctf-db:5432;Database=ctf;Username=postgres;Password=gzctf"
     },
     "ContainerProvider": {
      "Type": "Kubernetes"
     },
     "ForwardedOptions": {
       "ForwardedHeaders": 7,
       "ForwardLimit": 1,
       "ForwardedForHeaderName": "X-Forwarded-For",
       "KnownIPNetworks": [
         "10.42.0.0/16",
         "127.0.0.1/32"
       ],
       "KnownProxies": []
     },
     "XorKey": "gzctf123"
    }

创建存储

创建宿主机目录:

sudo mkdir -p /opt/gzctf/files /opt/gzctf/db
sudo chmod -R 777 /opt/gzctf/files /opt/gzctf/db

03-storage.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: gzctf-files-pv
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  hostPath:
    path: /opt/gzctf/files
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: gzctf-db-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  hostPath:
    path: /opt/gzctf/db
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gzctf-files
  namespace: gzctf-server
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  resources:
    requests:
      storage: 2Gi
  volumeName: gzctf-files-pv
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gzctf-db
  namespace: gzctf-server
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  resources:
    requests:
      storage: 1Gi
  volumeName: gzctf-db-pv

部署 GZCTF

04-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gzctf
  namespace: gzctf-server
  labels:
    app: gzctf
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: gzctf
  template:
    metadata:
      labels:
        app: gzctf
    spec:
      serviceAccountName: gzctf-sa
      containers:
        - name: gzctf
          image: registry.cn-shanghai.aliyuncs.com/gztime/gzctf:latest
          imagePullPolicy: Always
          env:
            - name: GZCTF_ADMIN_PASSWORD
              value: gzctf # 管理员密码
            - name: LC_ALL
              value: zh_CN.UTF-8
          ports:
            - containerPort: 8080
              name: http
            - containerPort: 3000
              name: metrics
          volumeMounts:
            - name: gzctf-files
              mountPath: /app/files
            - name: gzctf-config
              mountPath: /app/appsettings.json
              subPath: appsettings.json
          resources:
            requests:
              cpu: 1000m
              memory: 384Mi
      volumes:
        - name: gzctf-files
          persistentVolumeClaim:
            claimName: gzctf-files
        - name: gzctf-config
          configMap:
            name: gzctf-config
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gzctf-garnet
  namespace: gzctf-server
  labels:
    app: gzctf-garnet
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gzctf-garnet
  template:
    metadata:
      labels:
        app: gzctf-garnet
    spec:
      containers:
        - name: gzctf-garnet
          image: ghcr.io/microsoft/garnet-alpine:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 6379
              name: garnet
          args: ["--bind", "0.0.0.0"]
          resources:
            requests:
              cpu: 10m
              memory: 64Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gzctf-db
  namespace: gzctf-server
  labels:
    app: gzctf-db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gzctf-db
  template:
    metadata:
      labels:
        app: gzctf-db
    spec:
      containers:
        - name: gzctf-db
          image: postgres:alpine
          imagePullPolicy: Always
          ports:
            - containerPort: 5432
              name: postgres
          env:
            - name: POSTGRES_PASSWORD
              value: gzctf # 数据库密码,需要和 appsettings.json 中的数据库密码一致
          volumeMounts:
            - name: gzctf-db
              mountPath: /var/lib/postgresql
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
      volumes:
        - name: gzctf-db
          persistentVolumeClaim:
            claimName: gzctf-db

创建访问入口

配置 traefik

cat <<'EOF' > /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    additionalArguments:
      - "--entryPoints.web.forwardedHeaders.insecure"
      - "--entryPoints.websecure.forwardedHeaders.insecure"
      - "--entryPoints.web.proxyProtocol.insecure"
      - "--entryPoints.websecure.proxyProtocol.insecure"
    service:
      spec:
        externalTrafficPolicy: Local
    deployment:
      kind: DaemonSet
EOF

05-network.yaml

有域名

有域名时,把 ctf.example.com 改成你的真实域名,并把 DNS 解析到服务器公网 IP。

apiVersion: v1
kind: Service
metadata:
  name: gzctf
  namespace: gzctf-server
spec:
  selector:
    app: gzctf
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: gzctf-db
  namespace: gzctf-server
spec:
  selector:
    app: gzctf-db
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432
---
apiVersion: v1
kind: Service
metadata:
  name: gzctf-garnet
  namespace: gzctf-server
spec:
  selector:
    app: gzctf-garnet
  ports:
    - protocol: TCP
      port: 6379
      targetPort: 6379
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gzctf
  namespace: gzctf-server
spec:
  ingressClassName: traefik
  rules:
    - host: ctf.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: gzctf
                port:
                  number: 8080

无域名

没有域名时,不需要写 host,直接用下面这个版本覆盖同名文件。这个规则会把所有访问都路由到 gzctf:

apiVersion: v1
kind: Service
metadata:
  name: gzctf
  namespace: gzctf-server
spec:
  selector:
    app: gzctf
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: gzctf-db
  namespace: gzctf-server
spec:
  selector:
    app: gzctf-db
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432
---
apiVersion: v1
kind: Service
metadata:
  name: gzctf-garnet
  namespace: gzctf-server
spec:
  selector:
    app: gzctf-garnet
  ports:
    - protocol: TCP
      port: 6379
      targetPort: 6379
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gzctf
  namespace: gzctf-server
spec:
  ingressClassName: traefik
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: gzctf
                port:
                  number: 8080

应用资源

kubectl apply -f /opt/gzctf/manifests/

验证

kubectl -n gzctf-server get pods
kubectl -n gzctf-server get svc
kubectl -n gzctf-server get ingress
kubectl -n gzctf-server logs deploy/gzctf -f

浏览器访问 http://ctf.example.com ,使用 GZCTF_ADMIN_PASSWORD 设置的密码完成首次登录。

常见问题

root 用户找不到 kubectl

echo "export PATH=$PATH:/usr/local/bin" >> ~/.bashrc
source ~/.bashrc

操作系统不支持 cgroup v2

安装最后一个支持 cgroup v1 的 k3s 版本:

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.24.15+k3s1" sh -

粤ICP备2025414119号 粤公网安备44030002006951号

© 2026 Saurlax · Powered by Astro