name: labca
services:
  boulder:
    # The `letsencrypt/boulder-tools:latest` tag is automatically built in local
    # dev environments. In CI a specific BOULDER_TOOLS_TAG is passed, and it is
    # pulled with `docker compose pull`.
    image: ghcr.io/hakwerk/labca-boulder:${LABCA_IMAGE_VERSION:-latest}
    build:
      context: test/boulder-tools/
      # Should match one of the GO_CI_VERSIONS in test/boulder-tools/tag_and_upload.sh.
      args:
        GO_VERSION: 1.25.5
    environment:
      # To solve HTTP-01 and TLS-ALPN-01 challenges, change the IP in FAKE_DNS
      # to the IP address where your ACME client's solver is listening. This is
      # pointing at the boulder service's "public" IP, where challtestsrv is.
      FAKE_DNS: 64.112.117.122
      BOULDER_CONFIG_DIR: labca/config
      GOCACHE: /boulder/.gocache/go-build
    volumes:
      - boulder_data:/opt/boulder/labca
      - certificates:/opt/boulder/labca/certs
      - nginx_html:/var/www/html
      - softhsm:/var/lib/softhsm/tokens
    networks:
      bouldernet:
        ipv4_address: 10.77.77.77
      publicnet:
        ipv4_address: 64.112.117.122
      publicnet2:
        ipv4_address: 64.112.117.134
    # Use consul as a backup to Docker's embedded DNS server. If there's a name
    # Docker's DNS server doesn't know about, it will forward the query to this
    # IP (running consul).
    # (https://docs.docker.com/config/containers/container-networking/#dns-services).
    # This is used to look up service names via A records (like ra.service.consul) that
    # are configured via the ServerAddress field of cmd.GRPCClientConfig.
    # TODO: Remove this when ServerAddress is deprecated in favor of SRV records
    # and DNSAuthority.
    dns: 10.77.77.10
    extra_hosts:
      # Allow the boulder container to be reached as "ca.example.org", so we
      # can put that name inside our integration test certs (e.g. as a crl
      # url) and have it look like a publicly-accessible name.
      # TODO(#8215): Move s3-test-srv to a separate service.
      - "ca.example.org:64.112.117.122"
      # Allow the boulder container to be reached as "integration.trust", for
      # similar reasons, but intended for use as a SAN rather than a CRLDP.
      # TODO(#8215): Move observer's probe target to a separate service.
      - "integration.trust:64.112.117.122"
    ports:
      - 4001:4001 # ACMEv2
      - 4003:4003 # SFE
    depends_on:
      - bmysql
      - bredis
      - bconsul
      - bpkimetal
      - control
    entrypoint: labca/entrypoint.sh
    working_dir: &boulder_working_dir /opt/boulder
    logging:
      driver: "json-file"
      options:
        max-size: "500k"
        max-file: "5"
    restart: always

  bmysql:
    image: &db_image mariadb:10.11.13
    volumes:
      - dbdata:/var/lib/mysql
    networks:
      bouldernet:
        aliases:
          - boulder-mysql
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
    command: mysqld --bind-address=0.0.0.0 --log-output=TABLE
    logging:
      driver: "json-file"
      options:
        max-size: "500k"
        max-file: "5"
    restart: always

  bmysql-upgrade:
    image: *db_image
    networks:
      - bouldernet
    depends_on:
      - bmysql
    entrypoint: >
      bash -c "sleep 20 && mysql_upgrade -h bmysql"

  bredis:
    image: redis:7.0.15
    volumes:
      - ./test/:/test/:cached
      - boulder_data:/opt/boulder/labca
      - certificates:/opt/boulder/labca/certs
    command: redis-server /opt/boulder/labca/redis-ratelimits.config
    networks:
      bouldernet:
        ipv4_address: 10.77.77.4
    restart: always

  bconsul:
    image: hashicorp/consul:1.19.2
    depends_on:
      - control
    volumes:
      - boulder_data:/opt/boulder/labca
      - certificates:/opt/boulder/labca/certs
    networks:
      bouldernet:
        ipv4_address: 10.77.77.10
    command: "consul agent -dev -config-format=hcl -config-file=/opt/boulder/labca/consul/config.hcl"
    working_dir: /opt/boulder
    restart: always

  gui:
    image: ghcr.io/hakwerk/labca-gui:${LABCA_IMAGE_VERSION:-latest}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./docker-compose.yml:/opt/boulder/docker-compose.yml
      - ldata:/opt/labca/data
      - nginx_html:/var/www/html
      - backup:/opt/backup
      - boulder_data:/opt/boulder/labca
      - certificates:/opt/boulder/labca/certs
      - softhsm:/var/lib/softhsm/tokens
    networks:
      - bouldernet
    expose:
      - 3000
    depends_on:
      - bmysql
      - control
    working_dir: /opt/labca
    command: bin/labca-gui
    logging:
      driver: "json-file"
      options:
        max-size: "500k"
        max-file: "5"
    restart: always

  nginx:
    image: nginx:latest
    networks:
      - bouldernet
    ports:
      - 80:80
      - 443:443
    volumes:
      - nginx_conf:/etc/nginx/conf.d
      - nginx_ssl:/etc/nginx/ssl
      - nginx_html:/var/www/html
    depends_on:
      - control
    restart: always

  control:
    image: ghcr.io/hakwerk/labca-control:${LABCA_IMAGE_VERSION:-latest}
    networks:
      - bouldernet
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./docker-compose.yml:/opt/boulder/docker-compose.yml
      - ldata:/opt/labca/data
      - backup:/opt/backup
      - logs:/opt/logs
      - boulder_data:/opt/boulder/labca
      - certificates:/opt/boulder/labca/certs
      - softhsm:/var/lib/softhsm/tokens
      - nginx_conf:/etc/nginx/conf.d
      - nginx_ssl:/etc/nginx/ssl
      - nginx_html:/var/www/html
    expose:
      - 3030
    environment:
      LABCA_FQDN: ${LABCA_FQDN:-notset}
    working_dir: /opt/labca
    command: ./control.sh
    restart: always

  bpkimetal:
    image: ghcr.io/pkimetal/pkimetal:v1.20.0
    networks:
      - bouldernet
    restart: always

volumes:
  dbdata:
  nginx_conf:
  nginx_ssl:
  nginx_html:
  boulder_data:
  ldata:
  backup:
  logs:
  softhsm:
  certificates:

networks:
  # This network represents the data-center internal network. It is used for
  # boulder services and their infrastructure, such as consul, mariadb, and
  # redis.
  bouldernet:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 10.77.77.0/24
          # Only issue DHCP addresses in the top half of the range, to avoid
          # conflict with static addresses.
          ip_range: 10.77.77.128/25

  # This network represents the public internet. It uses a real public IP space
  # (that Let's Encrypt controls) so that our integration tests are happy to
  # validate and issue for it. It is used by challtestsrv, which binds to
  # 64.112.117.122:80 and :443 for its HTTP-01 challenge responder.
  #
  # TODO(#8215): Put s3-test-srv on this network.
  publicnet:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 64.112.117.0/25

  # This network is used for two things in the integration tests:
  #  - challtestsrv binds to 64.112.117.134:443 for its tls-alpn-01 challenge
  #    responder, to avoid interfering with the HTTPS port used for testing
  #    HTTP->HTTPS redirects during http-01 challenges. Note: this could
  #    probably be updated in the future so that challtestsrv can handle
  #    both tls-alpn-01 and HTTPS on the same port.
  #  - test/v2_integration.py has some test cases that start their own HTTP
  #    server instead of relying on challtestsrv, because they want very
  #    specific behavior. For these cases, v2_integration.py creates a Python
  #    HTTP server and binds it to 64.112.117.134:80.
  #
  # TODO(#8215): Deprecate this network, replacing it with individual IPs within
  # the existing publicnet.
  publicnet2:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 64.112.117.128/25
