[Article] Deploying FBCTF to the community grid

With the upcoming 2018 CTF and Coding challenges. We’ll be needing a tool to manage teams, scoring, and challenges. Enter FB-CTF.

What is FBCTF?

The Facebook CTF is a platform to host Jeopardy and “King of the Hill” style Capture the Flag competitions.

How do I use FBCTF?

  • Organize a competition. This can be done with as few as two participants, all the way up to several hundred. The participants can be physically present, active online, or a combination of the two.
  • Follow setup instructions below to spin up platform infrastructure.
  • Enter challenges into admin page
  • Have participants register as teams

Community Grid Dashboard (portainer)

After logging in to communitygrid.dallasmakerspace.org. Onew would navigate to stacks then fill out the web editor with the following docker-compose.yml

Include the following environment variables.

Config ENV

MYSQL_ROOT_PASSWORD (string) random string from 14-24 characters long
MYSQL_PASSWORD (string) random string from 14-24 characters long

docker-compose.yml

---
version: "3.6"

services:

  autoscale:
    image: gianarb/orbiter:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      DOCKER_HOST: unix:///var/run/docker.sock
    ports:
      - target: 8000
        protocol: "tcp"
        mode: "ingress"
    deploy:
      restart_policy:
        condition: on-failure
      mode: replicated
      replicas: 1
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        orbiter: "false"
        traefik.enable: "true"
        traefik.port: 8000
        traefik.network: public
        traefik.frontend.priority: 10
        traefik.frontend.rule: 'Host:scaler.local'
        traefik.backend: "scaler"
      placement:
        constraints: [node.role == manager]
    networks:
      - public

  watchtower:
    image: 'v2tec/watchtower:latest'
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --label-enable --interval 30 --cleanup
    deploy:
      restart_policy:
        condition: on-failure
      mode: replicated
      replicas: 1
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        traefik.enable: "false"
      placement:
        constraints: [node.role == manager]

  traefik:
    image: traefik:1.5
    command: --web --docker --docker.swarmmode --docker.watch --docker.domain=local --logLevel=DEBUG --api
    deploy:
      placement:
        constraints: [node.role==manager]
      restart_policy:
        condition: on-failure
      labels:
        traefik.port: "8080"
        traefik.docker.network: "public"
        traefik.frontend.rule: "Host:traefik.local"
        traefik.entryPoints.http.redirect: "https"
    ports:
      - target: 443
        published: 443
        protocol: "tcp"
        mode: "ingress"
      - target: 80
        published: 80
        protocol: "tcp"
        mode: "ingress"
      - target: 8080
        published: 8080
        protocol: "tcp"
        mode: "host"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /dev/null:/traefik.toml
    networks:
      public:
        aliases:
          - gateway

  portainer:
    ports:
      - target: 9000
        protocol: "tcp"
        mode: "ingress"
    image: portainer/portainer:latest
    networks:
      - public
      - default
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    deploy:
      restart_policy:
        condition: on-failure
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        traefik.frontend.rule: "HostRegexp:{catchall:.*}"
        traefik.frontend.priority: "1"
        traefik.backend: "dashboard"
        traefik.docker.network: "public"
        traefik.port: "9000"
        traefik.enable: "true"
        traefik.default.protocol: "http"

  agent:
    image: portainer/agent:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - target: 9001
        published: 9001
        protocol: tcp
        mode: host
    environment:
      AGENT_CLUSTER_ADDR: tasks.agent
    networks:
      - default
    deploy:
      mode: global
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        orbiter: "false"
        traefik.enable: "false"

  mysql:
    ports:
      - target: 3306
        protocol: tcp
        mode: "ingress"
    image: mysql:5.7
    environment:
      - MYSQL_DATABASE=ctf2018
      - MYSQL_USER=ctf2018
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
    networks:
      default:
        aliases:
          - mysql
    deploy:
      restart_policy: 
        condition: on-failure
      replicas: 1
      mode: replicated
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        orbiter: "false"
        traefik.enable: "false"
  adminer:
    image: adminer
    ports:
      - target: 8080
        protocol: tcp
        mode: "ingress"
    hostname: "db.local"
    networks:
      - public
      - default
    depends_on:
      - mysql
    deploy:
      restart_policy:
        condition: on-failure
      mode: replicated
      replicas: 1
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        orbiter: "true"
        orbiter.up: 3
        orbiter.down: 1
        traefik.enable: "true"
        traefik.port: 8080
        traefik.docker.network: "public"
        traefik.backend.loadbalancer.stickiness: "true"
        traefik.backend.loadbalancer.swarm: "true"
        traefik.frontend.rule: "Host:db.local"
        traefik.frontend.proto: "http"
        traefik.frontend.entrypoints: "http"
        traefik.frontend.priority: 10

  memcached:
    ports:
      - target: 11211
        protocol: tcp
        mode: "ingress"
    image: memcached:latest
    deploy:
      restart_policy:
       condition: on-failure
      replicas: 1
      mode: replicated
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        orbiter: "false"
        traefik.enable: "false"
    networks:
      default:
        aliases:
          - memcached

  fbctf:
    image: alexgaspar/fbctf:latest
    ports:
      - target: 80
        protocol: tcp
        mode: "ingress"
      - target: 443
        protocol: tcp
        mode: "ingress"
   environment:
      - MYSQL_HOST=mysql
      - MYSQL_PORT=3306
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=ctf2018
      - MYSQL_USER=ctf2018
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MEMCACHED_PORT=11211
      - SSL_SELF_SIGNED=true
      - CTF_URL=app.local
      - SSL_COUNTRY=US
      - SSL_CITY=Dallas
    hostname: "app.local"
    networks:
      - public
      - default
    depends_on:
      - memcached
      - mysql
    deploy:
      restart_policy:
        condition: on-failure
      mode: replicated
      replicas: 1
      labels:
        com.centurylinklabs.watchtower.enable: "true"
        orbiter: "true"
        orbiter.up: 3
        orbiter.down: 1
        traefik.enable: "true"
        traefik.port: 80
        traefik.docker.network: "public"
        traefik.backend: "app"
        traefik.backend.loadbalancer.stickiness: "true"
        traefik.backend.loadbalancer.swarm: "true"
        traefik.frontend.rule: "Host:app.local,www.app.local"
        traefik.frontend.proto: "http"
        traefik.frontend.entrypoints: "http, https"
        traefik.frontend.priority: 10
        traefik.frontend.passTLSCert: "false"
        traefik.frontend.passHostHeader: "true"
        traefik.frontend.headers.SSLProxyHeaders: "X-Forwarded-For:https"
        traefik.frontend.headers.forceSTSHeader: "true"
        traefik.frontend.headers.STSSeconds: "315360000"
        traefik.frontend.headers.STSIncludeSubdomains: "true"
        traefik.frontend.headers.STSPreload: "true"
        traefik.frontend.headers.browserXSSFilter: "true"
        traefik.frontend.headers.contentTypeNosniff: "true"
        traefik.frontend.headers.customrequestheaders: "X-Forwarded-Ssl:on"

volumes:
  portainer_data: {}

networks:
  public:
    external: true
    name: "public"

  default:
    driver: "overlay"
...
# vim: set sts=2 sw=2 ts=2 et ai:

Summary

Further details on the Community Grid can be found on talk, chat, and the wiki.

3 posts were split to a new topic: [Discussion] Deploying FBCTF to the community grid