azio7 blog

Gzctf搭建与维护

基本框架

1. 依照官网示例配置

配置各种密码和参数

  gzctf:
    image: gztime/gzctf:latest
    restart: unless-stopped
    environment:
      - "GZCTF_ADMIN_PASSWORD=${GZCTF_ADMIN_PASSWORD}"
      # choose your backend language `en_US` / `zh_CN` / `ja_JP`
      - "LC_ALL=zh_CN.UTF-8"
    # ports:
    #   - "80:8080"
    volumes:
      - "./gzctf/data/files:/app/files"
      - "./gzctf/appsettings.json:/app/appsettings.json:ro"
      # - "./kube-config.yaml:/app/kube-config.yaml:ro" # this is required for k8s deployment
      - "/var/run/docker.sock:/var/run/docker.sock" # this is required for docker deployment
    depends_on:
      - postgres-db
 
  postgres-db:
    image: postgres:alpine
    restart: unless-stopped
    environment:
      - "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}"
    volumes:
      - "./gzctf/data/postgres-db:/var/lib/postgresql/data"
    ports:
      - 5432:5432

  redis-cache:
    image: redis:4
    restart: unless-stopped
    volumes:
    - ./gzctf/data/redis:/data

2. 接入caddy

caddy提供自动签ssl证书的功能,非常好用

  caddy:
    build:
      context: ./caddy/build
      args:
        CADDY_BASE_VERSION: ${CADDY_BASE_VERSION}
    volumes:
      - ./caddy/config/Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/data:/data
      - ./wwwroot:/var/www
      - /etc/localtime:/etc/localtime:ro
    command: caddy run --config /etc/caddy/Caddyfile
    environment:
      - CLOUDFLARE_TOKEN=${CLOUDFLARE_TOKEN}
    ports:
      - 80:80
      - 443:443/udp
      - 443:443
      - 8080:8080
    restart: unless-stopped
    extra_hosts:
      - "host.docker.internal:host-gateway"
    logging:
      driver: "json-file"
      options:
        max-size: "20M"

caddy Dockfile

ARG CADDY_BASE_VERSION=caddy:2.7.5
FROM caddy:2.7.5-builder AS builder
ARG GO111MODULE=on
RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/caddy-dns/dnspod

FROM caddy:2.7.5

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Caddyfile

{
	acme_dns cloudflare {env.CLOUDFLARE_TOKEN}
}

...

ctf.guetsec.cn {
	encode gzip
	reverse_proxy * gzctf:8080
	header -Strict-Transport-Security # 让浏览器不强制使用https,因为题目容器和平台在一个域名上
}

...

3. 开启对外服务

动态容器端口映射按照官方文档设置

4. 后台显示真实ip

对于官网 appsettings.json 中 ForwardedOptions 的解释

ForwardedHeaders 枚举

All 15 进程 X-Forwarded-For、X-Forwarded-Host、X-Forwarded-Proto 和 X-forwarded-Prefix。
None 0 不处理任何转发器
XForwardedFor 1 进程 X-Forwarded-For,用于标识客户端的源 IP 地址。
XForwardedHost 2 处理 X-Forwarded-Host,它标识客户端请求的原始主机。
XForwardedPrefix 8 处理 X-Forwarded-Prefix,用于标识客户端使用的原始路径基。
XForwardedProto 4 处理 X-Forwarded-Proto,它标识用于连接的客户端) HTTP 或 HTTPS (协议。

caddy 反代默认携带 XForwardedFor XForwardedHost XForwardedProto

TrustedNetworks 很重要,TrustedNetworks是指相信来源ip发送过来的 ForwardedHeaders,在本案例中,我们需要让gzctf信任caddy,故需要指定caddy的ip

以下docker compose配置指定了容器所在的网段

networks:
  default:
    ipam:
      driver: default
      config:
        - subnet: "172.xx.0.0/24"

对应appsettings.json中

"ForwardedOptions": {
    "ForwardedHeaders": 7,
    "ForwardLimit": 1,
    "TrustedNetworks": ["172.xx.0.0/24"]
  }

5. 邮箱注册验证

使用飞书国际版企业邮箱,注册飞书国际版参照:https://51.ruyo.net/17996.html

然后开通一个公共邮箱,获取imap密码

修改 gzctf appsettings 的EmailConfig

  "EmailConfig": {
    "SendMailAddress": "info@guetsec.cn",
    "UserName": "info@guetsec.cn",
    "Password": "xxxxxxxxx",
    "Smtp": {
      "Host": "smtp.larksuite.com",
      "Port": 465
    }
  },

6. 验证码

使用 CloudflareTurnstile (太有公益了cf)

修改appsettings

"CaptchaConfig": {
    "Provider": "CloudflareTurnstile",
    "SiteKey": "xxxxxxxxxxxxxxxxxxx",
    "SecretKey": "xxxxxxxxxxxxxxxxxxxxx",