diff --git a/files/compose/docker-compose.yaml b/files/compose/docker-compose.yaml new file mode 100644 index 0000000..7812801 --- /dev/null +++ b/files/compose/docker-compose.yaml @@ -0,0 +1,55 @@ +--- +version: "3.8" + +networks: + forge: + name: "forge-flow" + ipam: + driver: "default" + config: + - subnet: "${NETWORK_FORGE_FLOW_SUBNET}" + +volumes: + traefik-shared: + name: "traefik-config-shared-volume" + +services: + traefik: + container_name: "traefik-flow" + build: + args: + TRAEFIK_VERSION: "${TRAEFIK_VERSION}" + context: "./traefik" + networks: + forge: + ipv4_address: "${TRAEFIK_CONTAINER_IPV4_ADDRESS}" + ports: + - target: 80 + published: 80 + protocol: "tcp" + mode: "host" + - target: 443 + published: 443 + protocol: "tcp" + mode: "host" + - target: 22 + published: ${TRAEFIK_EXTERNAL_SSH_PORT} + protocol: "tcp" + mode: "host" + restart: "always" + volumes: + - type: "volume" + source: "traefik-shared" + target: "${TRAEFIK_SHARED_MOUNT_POINT}" + - type: "bind" + source: "/etc/timezone" + target: "/etc/timezone" + read_only: true + - type: "bind" + source: "/etc/localtime" + target: "/etc/localtime" + read_only: true + # For TLS certificate + #- type: "bind" + # source: "" + # target: "" diff --git a/files/scripts/bootstrap.sh b/files/scripts/bootstrap.sh new file mode 100644 index 0000000..7fe03f1 --- /dev/null +++ b/files/scripts/bootstrap.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +function usage() { + echo "usage: $0 [options]" + echo "Bootstraps the flow instance" + echo "" + echo "-h,--help: print this help message" + echo "--network-forge-flow-subnet: The subnet for the forge flow docker network. (default: 172.20.0.0/24)" + echo "--traefik-container-ipv4-address: The IPv4 address of the traefik container. (default: 172.20.0.2)" + echo "--traefik-check-new-version: Set to true to enable automatic checks for new Traefik versions. (default: true)" + echo "--traefik-domain: The root domain of the traefik. (default: localhost)" + echo "--traefik-external-ssh-port: The external SSH port to expose for Gitea. (default: 22)" +} + +while [[ $# -gt 0 ]]; do + arg="$1" + case $arg in + -h|--help) + usage + exit 0 + ;; + --network-forge-flow-subnet) + NETWORK_FORGE_FLOW_SUBNET=$2 + shift + shift + ;; + --traefik-container-ipv4-address) + TRAEFIK_CONTAINER_IPV4_ADDRESS=$2 + shift + shift + ;; + --traefik-check-new-version) + TRAEFIK_CHECK_NEW_VERSION=$2 + shift + shift + ;; + --traefik-domain) + TRAEFIK_DOMAIN=$2 + shift + shift + ;; + --traefik-external-ssh-port) + TRAEFIK_EXTERNAL_SSH_PORT=$2 + shift + shift + ;; + --traefik-log-level) + TRAEFIK_LOG_LEVEL=$2 + shift + shift + ;; + --traefik-send-anonymous-usage) + TRAEFIK_SEND_ANONYMOUS_USAGE=$2 + shift + shift + ;; + --traefik-version) + TRAEFIK_VERSION=$2 + shift + shift + ;; + *) + # unknown argument + shift + ;; + esac +done + +source /etc/flow/setup/env +DOCKER_ROOT="/home/${FLOW_USERNAME}/Docker/flow" + +export NETWORK_FORGE_FLOW_SUBNET="${NETWORK_FORGE_FLOW_SUBNET:-172.20.0.0/24}" + +export TRAEFIK_DOCKER_DIR="${DOCKER_ROOT}/traefik" +export TRAEFIK_CHECK_NEW_VERSION="${TRAEFIK_CHECK_NEW_VERSION:-true}" +export TRAEFIK_DOMAIN="${TRAEFIK_DOMAIN:-localhost}" +export TRAEFIK_EXTERNAL_SSH_PORT="${TRAEFIK_EXTERNAL_SSH_PORT:-22}" +export TRAEFIK_LOG_LEVEL="${TRAEFIK_LOG_LEVEL:-info}" +export TRAEFIK_SEND_ANONYMOUS_USAGE="${TRAEFIK_SEND_ANONYMOUS_USAGE:-false}" +export TRAEFIK_VERSION="${TRAEFIK_VERSION:-latest}" +export TRAEFIK_CONTAINER_IPV4_ADDRESS="${TRAEFIK_CONTAINER_IPV4_ADDRESS:-172.20.0.2}" +export TRAEFIK_SHARED_MOUNT_POINT="/flow/shared/traefik" + +mkdir -p "${DOCKER_ROOT}" +envsubst < "${ROOT_SETUP_DIRECTORY}/template/compose/docker-compose.yaml" > "${DOCKER_ROOT}/docker-compose.yaml" + +# Traefik setup section +mkdir -p "${TRAEFIK_DOCKER_DIR}" +cp "${ROOT_SETUP_DIRECTORY}/template/traefik/Dockerfile" "${TRAEFIK_DOCKER_DIR}/Dockerfile" + +for i in $(find "${ROOT_SETUP_DIRECTORY}/template/traefik" -type f -mindepth 1 -not -name *Dockerfile); do + file=$(basename ${i}) + envsubst < "${ROOT_SETUP_DIRECTORY}/template/traefik/${file}" > "${TRAEFIK_DOCKER_DIR}/${file}" +done + +chown -R ${FLOW_USERNAME}:${FLOW_USERNAME} /home/${FLOW_USERNAME}/Docker +chmod -R a-rwx,u+rwX /home/${FLOW_USERNAME}/Docker + +rc-service docker start diff --git a/files/traefik/Dockerfile b/files/traefik/Dockerfile new file mode 100644 index 0000000..399bb49 --- /dev/null +++ b/files/traefik/Dockerfile @@ -0,0 +1,15 @@ +ARG TRAEFIK_VERSION + +FROM traefik:${TRAEFIK_VERSION} + +ADD traefik.yaml /flow/traefik/ + +ADD entrypoint.sh / + +ADD dynamic_dashboard.yaml /tmp/ + +RUN chmod +x /entrypoint.sh + +EXPOSE 22 80 443 + +CMD ["--configfile=/flow/traefik/traefik.yaml"] diff --git a/files/traefik/dynamic_dashboard.yaml b/files/traefik/dynamic_dashboard.yaml new file mode 100644 index 0000000..6e08b3c --- /dev/null +++ b/files/traefik/dynamic_dashboard.yaml @@ -0,0 +1,9 @@ +--- +http: + routers: + dashboard: + entryPoints: + - "https" + rule: "Host(`${TRAEFIK_DOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + service: "api@internal" + tls: {} diff --git a/files/traefik/entrypoint.sh b/files/traefik/entrypoint.sh new file mode 100644 index 0000000..1e986c6 --- /dev/null +++ b/files/traefik/entrypoint.sh @@ -0,0 +1,28 @@ +#!/bin/sh +set -e + +# Create the dynamic config directory in the shared volume. +mkdir -p ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic +chgrp ${FLOW_GID} ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic +chmod a-rwx,u+rwx,g+rwx ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic + +# Move the dashboard config to the new directory. +if [ -f /tmp/dynamic_dashboard.yaml ]; then + mv /tmp/dynamic_dashboard.yaml ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic/dynamic_dashboard.yaml +fi + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- traefik "$@" +fi + +# if our command is a valid Traefik subcommand, let's invoke it through Traefik instead +# (this allows for "docker run traefik version", etc) +if traefik "$1" --help >/dev/null 2>&1 +then + set -- traefik "$@" +else + echo "= '$1' is not a Traefik command: assuming shell execution." 1>&2 +fi + +exec "$@" diff --git a/files/traefik/traefik.yaml b/files/traefik/traefik.yaml new file mode 100644 index 0000000..47612b8 --- /dev/null +++ b/files/traefik/traefik.yaml @@ -0,0 +1,27 @@ +--- +global: + checkNewVersion: ${TRAEFIK_CHECK_NEW_VERSION} + sendAnonymousUsage: ${TRAEFIK_SEND_ANONYMOUS_USAGE} +api: + insecure: false + dashboard: true + debug: false +entryPoints: + http: + address: "${TRAEFIK_CONTAINER_IP}:80" + http: + redirections: + entryPoint: + to: "https" + scheme: "https" + permanent: true + https: + address: "${TRAEFIK_CONTAINER_IP}:443" + ssh: + address: "${TRAEFIK_CONTAINER_IP}:22" +providers: + file: + watch: true + directory: "${TRAEFIK_SHARED_MOUNT_POINT}/dynamic" +log: + level: "${TRAEFIK_LOG_LEVEL}" diff --git a/images/lxd/image.pkr.json b/images/lxd/image.pkr.json new file mode 100644 index 0000000..92a39a6 --- /dev/null +++ b/images/lxd/image.pkr.json @@ -0,0 +1,58 @@ +{ + "source": { + "lxd": { + "flow_infra": { + "container_name": "${var.lxd_container_name}", + "image": "${var.lxd_base_image}", + "publish_properties": { + "description": "LXD image for Flow Infra Dev" + }, + "virtual_machine": false + } + } + }, + + "build": { + "source": { + "lxd.flow_infra": { + "name": "flow-infra", + "output_image": "${var.lxd_output_image_name}" + } + }, + "provisioner": { + "shell": { + "inline": ["apk add bash"] + } + }, + "provisioner": { + "shell": { + "environment_vars": [ + "FLOW_USERNAME=${var.flow_username}", + "FLOW_GID=${var.flow_gid}", + "FLOW_UID=${var.flow_uid}", + "ROOT_SETUP_DIRECTORY=${var.root_setup_directory}" + ], + "script": "${path.root}/../../provisioners/shell/setup.sh" + } + }, + "provisioner": { + "file": { + "sources": [ + "${path.root}/../../files/traefik/Dockerfile", + "${path.root}/../../files/traefik/dynamic_dashboard.yaml", + "${path.root}/../../files/traefik/entrypoint.sh", + "${path.root}/../../files/traefik/traefik.yaml" + ], + "destination": "${var.root_setup_directory}/template/traefik/" + }, + "file": { + "source": "${path.root}/../../files/compose/docker-compose.yaml", + "destination": "${var.root_setup_directory}/template/compose/" + }, + "file": { + "source": "${path.root}/../../files/scripts/bootstrap.sh", + "destination": "${var.root_setup_directory}/bootstrap.sh" + } + } + } +} diff --git a/images/lxd/variables.auto.pkr.json b/images/lxd/variables.auto.pkr.json new file mode 100644 index 0000000..da0dc3b --- /dev/null +++ b/images/lxd/variables.auto.pkr.json @@ -0,0 +1,13 @@ +{ + "variables": { + "lxd_base_image": "images:alpine/3.15", + "lxd_container_name": "flow-infra-lxd-packer-builder", + "lxd_output_image_name": "flow-infra", + + "flow_username": "flow", + "flow_gid": 22379, + "flow_uid": 22379, + + "root_setup_directory": "/etc/flow/setup" + } +} diff --git a/provisioners/shell/setup.sh b/provisioners/shell/setup.sh new file mode 100644 index 0000000..595d522 --- /dev/null +++ b/provisioners/shell/setup.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +set -euo pipefail + +DOCKER_COMPOSE_VERSION="v2.2.3" +DOCKER_COMPOSE_SOURCE="https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64" +DOCKER_COMPOSE_DESTINATION="/home/${FLOW_USERNAME}/.docker/cli-plugins/docker-compose" + +# Upgrade system and install required packages +apk update +apk upgrade + +apk add curl \ + docker \ + gettext \ + shadow \ + tzdata + +groupadd -g "${FLOW_GID}" "${FLOW_USERNAME}" +useradd -s /bin/bash -g "${FLOW_GID}" -u "${FLOW_UID}" -m -G docker "${FLOW_USERNAME}" + +# Set the timezone and local time +mkdir -p /etc/zoneinfo/Europe +chmod -R 0755 /etc/zoneinfo +cp /usr/share/zoneinfo/Europe/London /etc/zoneinfo/Europe/ +ln -fs /etc/zoneinfo/Europe/London /etc/localtime +echo "Europe/London" > /etc/timezone +apk del tzdata + +mkdir -p \ + "/home/${FLOW_USERNAME}/.docker/cli-plugins" \ + "${ROOT_SETUP_DIRECTORY}/template/compose" \ + "${ROOT_SETUP_DIRECTORY}/template/traefik" \ + "${ROOT_SETUP_DIRECTORY}/template/gitea" + +curl -SL "${DOCKER_COMPOSE_SOURCE}" -o "${DOCKER_COMPOSE_DESTINATION}" + +chown "${FLOW_USERNAME}":"${FLOW_USERNAME}" "${DOCKER_COMPOSE_DESTINATION}" +chmod u+x "${DOCKER_COMPOSE_DESTINATION}" + +cat < ${ROOT_SETUP_DIRECTORY}/env +export FLOW_USERNAME=${FLOW_USERNAME} +export FLOW_GID=${FLOW_GID} +export ROOT_SETUP_DIRECTORY=${ROOT_SETUP_DIRECTORY} +EOF