diff --git a/files/compose/docker-compose.yaml b/files/compose/docker-compose.yaml index 7812801..617f917 100644 --- a/files/compose/docker-compose.yaml +++ b/files/compose/docker-compose.yaml @@ -14,8 +14,9 @@ volumes: name: "traefik-config-shared-volume" services: + # -- Traffic flow -- traefik: - container_name: "traefik-flow" + container_name: "traffic-flow" build: args: TRAEFIK_VERSION: "${TRAEFIK_VERSION}" @@ -53,3 +54,42 @@ services: #- type: "bind" # source: "" # target: "" + # -- Code flow -- + gitea: + container_name: "code-flow" + build: + args: + FLOW_GID: "${FLOW_GID}" + FLOW_UID: "${FLOW_UID}" + GITEA_HOME: "${GITEA_HOME}" + GITEA_WORK_DIR: "${GITEA_WORK_DIR}" + GITEA_CUSTOM: "${GITEA_CUSTOM}" + GITEA_APP_INI: "${GITEA_APP_INI}" + GITEA_BIN: "${GITEA_BIN}" + GITEA_DATA_CONTAINER_DIR: "${GITEA_DATA_CONTAINER_DIR}" + GITEA_TMP: "${GITEA_TMP}" + GITEA_VERSION: "${GITEA_VERSION}" + context: "./gitea" + expose: + - "${GITEA_SSH_PORT}" + - "${GITEA_HTTP_PORT}" + networks: + forge: + ipv4_address: "${GITEA_CONTAINER_IPV4_ADDRESS}" + 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 + # Gitea data volume + - type: "bind" + source: "${GITEA_DATA_HOST_DIR}" + target: "${GITEA_DATA_CONTAINER_DIR}" diff --git a/files/gitea/Dockerfile b/files/gitea/Dockerfile new file mode 100644 index 0000000..ef45059 --- /dev/null +++ b/files/gitea/Dockerfile @@ -0,0 +1,53 @@ +# This is a custom made Dockerfile for Gitea which is inspired from +# the official Dockerfile.rootless from https://github.com/go-gitea/gitea/ +FROM alpine:3.15.0 + +ARG FLOW_UID +ARG FLOW_GID +ARG GITEA_HOME +ARG GITEA_WORK_DIR +ARG GITEA_CUSTOM +ARG GITEA_APP_INI +ARG GITEA_BIN +ARG GITEA_DATA_CONTAINER_DIR +ARG GITEA_TMP + +RUN apk --no-cache add \ + bash \ + ca-certificates \ + gettext \ + git \ + curl \ + gnupg + +RUN addgroup -S -g ${FLOW_GID} flow && \ + adduser -S -H -D -h ${GITEA_HOME} -s /bin/bash -u ${FLOW_UID} -G flow git && \ + mkdir -p ${GITEA_DATA_CONTAINER_DIR} ${GITEA_TMP} && \ + chown git ${GITEA_DATA_CONTAINER_DIR} && chmod 0700 ${GITEA_DATA_CONTAINER_DIR} && \ + chown git ${GITEA_TMP} && chmod 0700 ${GITEA_TMP} + +ADD --chown=root:root gitea ${GITEA_BIN} +ADD app.ini ${GITEA_APP_INI} +ADD entrypoint.sh /usr/local/bin/entrypoint.sh +ADD --chown=${FLOW_UID}:${FLOW_GID} dynamic_git.yaml ${GITEA_TMP}/ + +RUN chown -R ${FLOW_UID}:${FLOW_UID} ${GITEA_APP_INI} && \ + chmod 0400 ${GITEA_APP_INI} && \ + chmod a+x ${GITEA_BIN} && \ + chmod a+rx /usr/local/bin/entrypoint.sh + +ENV GITEA_WORK_DIR=${GITEA_WORK_DIR} \ + GITEA_CUSTOM=${GITEA_CUSTOM} \ + GITEA_APP_INI=${GITEA_APP_INI} \ + GITEA_BIN=${GITEA_BIN} \ + HOME=${GITEA_HOME} + +USER ${FLOW_UID}:${FLOW_GID} + +WORKDIR /flow/gitea/data + +VOLUME ["/flow/gitea/data"] + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] + +CMD [] diff --git a/files/gitea/app.ini b/files/gitea/app.ini new file mode 100644 index 0000000..52d4a23 --- /dev/null +++ b/files/gitea/app.ini @@ -0,0 +1,103 @@ +APP_NAME = ${GITEA_APP_NAME} +RUN_USER = git +RUN_MODE = ${GITEA_RUN_MODE} + +[repository] +ROOT = ${GITEA_DATA_CONTAINER_DIR}/git/repositories +DEFAULT_BRANCH = main + +[repository.local] +LOCAL_COPY_PATH = ${GITEA_TMP}/local-repo + +[repository.upload] +TEMP_PATH = ${GITEA_TMP}/uploads + +[repository.signing] +; Gitea will sign initial commits only if the user has a public key. +INITIAL_COMMIT = pubkey + +[ui] +DEFAULT_THEME = arc-green + +[server] +APP_DATA_PATH = ${GITEA_DATA_CONTAINER_DIR}/git +DOMAIN = ${GITEA_DOMAIN} +HTTP_ADDR = ${GITEA_CONTAINER_IPV4_ADDRESS} +HTTP_PORT = ${GITEA_HTTP_PORT} +ROOT_URL = https://${GITEA_DOMAIN} +DISABLE_SSH = false +SSH_DOMAIN = ${GITEA_DOMAIN} +SSH_PORT = ${GITEA_SSH_PORT} +SSH_LISTEN_PORT = ${GITEA_SSH_PORT} +BUILTIN_SSH_SERVER_USER = git +LFS_START_SERVER = false +LFS_CONTENT_PATH = ${GITEA_DATA_CONTAINER_DIR}/git/lfs + +[ssh.minimum_key_sizes] +ED25519 = 256 +ECDSA = 256 +RSA = 4096 +DSA = -1 + +[database] +DB_TYPE = sqlite3 +PATH = ${GITEA_DATA_CONTAINER_DIR}/database/gitea.db +HOST = localhost:3306 +NAME = gitea +USER = gitea +PASSWD = + +[indexer] +ISSUE_INDEXER_PATH = ${GITEA_DATA_CONTAINER_DIR}/indexers/issues.bleve + +[session] +PROVIDER_CONFIG = ${GITEA_DATA_CONTAINER_DIR}/sessions + +[queue] +DATADIR = ${GITEA_DATA_CONTAINER_DIR}/queues + +[admin] +DISABLE_REGULAR_ORG_CREATION = true +DEFAULT_EMAIL_NOTIFICATION = disabled + +[security] +INSTALL_LOCK = true +SECRET_KEY = ${GITEA_SECRET_KEY} +INTERNAL_TOKEN = ${GITEA_INTERNAL_TOKEN} +LOGIN_REMEMBER_DAYS = 1 +MIN_PASSWORD_LENGTH = 12 +PASSWORD_COMPLEXITY = lower,upper,digit + +[service] +DISABLE_REGISTRATION = true + +[service.explore] +REQUIRE_SIGNIN_VIEW = false + +[picture] +AVATAR_UPLOAD_PATH = ${GITEA_DATA_CONTAINER_DIR}/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = ${GITEA_DATA_CONTAINER_DIR}/repo-avatars + +[attachment] +ENABLED = true +PATH = ${GITEA_DATA_CONTAINER_DIR}/attachments + +[log] +ROOT_PATH = ${GITEA_DATA_CONTAINER_DIR}/log +MODE = console +LEVEL = ${GITEA_LOG_LEVEL} + +[log.console] +STDERR = false + +[i18n] +LANGS = en-US +NAMES = English + +[other] +SHOW_FOOTER_BRANDING = true +SHOW_FOOTER_VERSION = false +SHOW_FOOTER_TEMPLATE_LOAD_TIME = false + +[oauth2] +ENABLE = false diff --git a/files/gitea/dynamic_git.yaml b/files/gitea/dynamic_git.yaml new file mode 100644 index 0000000..f880acb --- /dev/null +++ b/files/gitea/dynamic_git.yaml @@ -0,0 +1,27 @@ +--- +http: + routers: + gitea: + entryPoints: + - "https" + rule: "Host(`${GITEA_DOMAIN}`)" + service: "git" + tls: {} + services: + git: + loadBalancer: + servers: + - url: "http://${GITEA_CONTAINER_IPV4_ADDRESS}:${GITEA_HTTP_PORT}/" + +tcp: + routers: + gitSSH: + entryPoints: + - "ssh" + rule: "HostSNI(`*`)" + service: "gitSSH" + services: + gitSSH: + loadBalancer: + servers: + - address: "${GITEA_CONTAINER_IPV4_ADDRESS}:${GITEA_SSH_PORT}" diff --git a/files/gitea/entrypoint.sh b/files/gitea/entrypoint.sh new file mode 100644 index 0000000..978049c --- /dev/null +++ b/files/gitea/entrypoint.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Create the home directory. +if ! [ -d ${GITEA_HOME} ]; then + mkdir -p ${GITEA_HOME} + chmod 0700 ${GITEA_HOME} +fi + +# Create the custom directory. +if ! [ -d ${GITEA_CUSTOM} ]; then + mkdir -p ${GITEA_CUSTOM} + chmod 0500 ${GITEA_CUSTOM} +fi + +# Move the dynamic Traefik config to the shared volume. +if [ -f /flow/gitea/tmp/dynamic_git.yaml ]; then + mv /flow/gitea/tmp/dynamic_git.yaml ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic/dynamic_git.yaml +fi + +if [ $# -gt 0 ]; then + exec "$@" +else + exec ${GITEA_BIN} -c ${GITEA_APP_INI} web +fi diff --git a/files/scripts/bootstrap.sh b/files/scripts/bootstrap.sh index 6c244a1..2f34a27 100644 --- a/files/scripts/bootstrap.sh +++ b/files/scripts/bootstrap.sh @@ -33,6 +33,41 @@ while [[ $# -gt 0 ]]; do shift shift ;; + --gitea-app-name) + GITEA_APP_NAME=$2 + shift + shift + ;; + --gitea-container-ipv4-address) + GITEA_CONTAINER_IPV4_ADDRESS=$2 + shift + shift + ;; + --gitea-ssh-port) + GITEA_SSH_PORT=$2 + shift + shift + ;; + --gitea-http-port) + GITEA_HTTP_PORT=$2 + shift + shift + ;; + --gitea-run-mode) + GITEA_RUN_MODE=$2 + shift + shift + ;; + --gitea-version) + GITEA_VERSION=$2 + shift + shift + ;; + --gitea-log-level) + GITEA_LOG_LEVEL=$2 + shift + shift + ;; --traefik-container-ipv4-address) TRAEFIK_CONTAINER_IPV4_ADDRESS=$2 shift @@ -74,8 +109,28 @@ 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 ROOT_DOMAIN="${ROOT_DOMAIN:-local}" + +export GITEA_APP_NAME="${GITEA_APP_NAME:-gitea}" +export GITEA_DOCKER_DIR="${DOCKER_ROOT}/gitea" +export GITEA_DOMAIN="code.${ROOT_DOMAIN}" +export GITEA_CONTAINER_IPV4_ADDRESS="${GITEA_CONTAINER_IPV4_ADDRESS:-172.20.0.3}" +export GITEA_SSH_PORT="${GITEA_SSH_PORT:-2222}" +export GITEA_HTTP_PORT="${GITEA_HTTP_PORT:-3000}" +export GITEA_RUN_MODE="${GITEA_RUN_MODE:-prod}" +export GITEA_VERSION="${GITEA_VERSION:-1.16.6}" +export GITEA_LOG_LEVEL="${GITEA_LOG_LEVEL:-info}" +export GITEA_DATA_HOST_DIR="/mnt/flow/gitea/data" +export GITEA_DATA_CONTAINER_DIR="/flow/gitea/data" +export GITEA_HOME="${GITEA_DATA_CONTAINER_DIR}/home" +export GITEA_WORK_DIR="${GITEA_DATA_CONTAINER_DIR}" +export GITEA_CUSTOM="${GITEA_DATA_CONTAINER_DIR}/custom" +export GITEA_APP_INI="/flow/gitea/config/app.ini" +export GITEA_BIN="/usr/local/bin/gitea" +export GITEA_TMP="/flow/gitea/tmp" +export GITEA_SECRET_HOST_DIR="/mnt/flow/gitea/secret" +export GITEA_SECRET_CONTAINER_DIR="/flow/gitea/secret" -export ROOT_DOMAIN="${ROOT_DOMAIN:-localhost}" export TRAEFIK_DOCKER_DIR="${DOCKER_ROOT}/traefik" export TRAEFIK_CHECK_NEW_VERSION="${TRAEFIK_CHECK_NEW_VERSION:-true}" export TRAEFIK_EXTERNAL_SSH_PORT="${TRAEFIK_EXTERNAL_SSH_PORT:-22}" @@ -88,7 +143,7 @@ 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 +## -- Traefik setup section -- mkdir -p "${TRAEFIK_DOCKER_DIR}" cp "${ROOT_SETUP_DIRECTORY}/template/traefik/Dockerfile" "${TRAEFIK_DOCKER_DIR}/Dockerfile" @@ -97,6 +152,52 @@ for i in $(find "${ROOT_SETUP_DIRECTORY}/template/traefik" -type f -mindepth 1 - envsubst < "${ROOT_SETUP_DIRECTORY}/template/traefik/${file}" > "${TRAEFIK_DOCKER_DIR}/${file}" done +## -- Gitea setup section -- +mkdir -p "${GITEA_DOCKER_DIR}" + +if ! [ -d ${GITEA_DATA_HOST_DIR} ]; then + mkdir -p ${GITEA_DATA_HOST_DIR} + chown ${FLOW_UID}:${FLOW_UID} ${GITEA_DATA_HOST_DIR} + chmod a-rwx,u-rwx ${GITEA_DATA_HOST_DIR} +fi + +# Generate the secrets if they don't exist. +if ! [ -d ${GITEA_SECRET_HOST_DIR} ]; then + mkdir -p ${GITEA_SECRET_HOST_DIR} + chown root:root ${GITEA_SECRET_HOST_DIR} + chmod a-rwx,u+rwx ${GITEA_SECRET_HOST_DIR} +fi + +curl -L https://dl.gitea.io/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64 -o /tmp/gitea +curl -L https://dl.gitea.io/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64.asc -o /tmp/gitea.asc +gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2 +gpg --verify /tmp/gitea.asc /tmp/gitea + +chmod a+x /tmp/gitea +mv /tmp/gitea "${GITEA_DOCKER_DIR}/gitea" + +if ! [ -f ${GITEA_SECRET_HOST_DIR}/SECRET_KEY ]; then + ${GITEA_DOCKER_DIR}/gitea generate secret SECRET_KEY | base64 -w 0 | tee ${GITEA_SECRET_HOST_DIR}/SECRET_KEY + chown root:root ${GITEA_SECRET_HOST_DIR}/SECRET_KEY + chmod a-rwx,u+rw ${GITEA_SECRET_HOST_DIR}/SECRET_KEY +fi + +if ! [ -f ${GITEA_SECRET_HOST_DIR}/INTERNAL_TOKEN ]; then + ${GITEA_DOCKER_DIR}/gitea generate secret INTERNAL_TOKEN | base64 -w 0 | tee ${GITEA_SECRET_HOST_DIR}/INTERNAL_TOKEN + chown root:root ${GITEA_SECRET_HOST_DIR}/INTERNAL_TOKEN + chmod a-rwx,u+rw ${GITEA_SECRET_HOST_DIR}/INTERNAL_TOKEN +fi + +export GITEA_SECRET_KEY=$(cat ${GITEA_SECRET_HOST_DIR}/SECRET_KEY | base64 -d) +export GITEA_INTERNAL_TOKEN=$(cat ${GITEA_SECRET_HOST_DIR}/INTERNAL_TOKEN | base64 -d) + +cp "${ROOT_SETUP_DIRECTORY}/template/gitea/Dockerfile" "${GITEA_DOCKER_DIR}/Dockerfile" + +for i in $(find "${ROOT_SETUP_DIRECTORY}/template/gitea" -type f -mindepth 1 -not -name *Dockerfile); do + file=$(basename ${i}) + envsubst < "${ROOT_SETUP_DIRECTORY}/template/gitea/${file}" > "${GITEA_DOCKER_DIR}/${file}" +done + chown -R ${FLOW_USERNAME}:${FLOW_USERNAME} /home/${FLOW_USERNAME}/Docker chmod -R a-rwx,u+rwX /home/${FLOW_USERNAME}/Docker diff --git a/images/lxd/image.pkr.json b/images/lxd/image.pkr.json index 92a39a6..40bc25f 100644 --- a/images/lxd/image.pkr.json +++ b/images/lxd/image.pkr.json @@ -36,6 +36,14 @@ } }, "provisioner": { + "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" + }, "file": { "sources": [ "${path.root}/../../files/traefik/Dockerfile", @@ -46,12 +54,13 @@ "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" + "sources": [ + "${path.root}/../../files/gitea/app.ini", + "${path.root}/../../files/gitea/Dockerfile", + "${path.root}/../../files/gitea/dynamic_git.yaml", + "${path.root}/../../files/gitea/entrypoint.sh" + ], + "destination": "${var.root_setup_directory}/template/gitea/" } } } diff --git a/provisioners/shell/setup.sh b/provisioners/shell/setup.sh index 595d522..93abe2c 100644 --- a/provisioners/shell/setup.sh +++ b/provisioners/shell/setup.sh @@ -10,11 +10,13 @@ DOCKER_COMPOSE_DESTINATION="/home/${FLOW_USERNAME}/.docker/cli-plugins/docker-co apk update apk upgrade -apk add curl \ - docker \ - gettext \ - shadow \ - tzdata +apk add \ + curl \ + docker \ + gettext \ + gnupg \ + shadow \ + tzdata groupadd -g "${FLOW_GID}" "${FLOW_USERNAME}" useradd -s /bin/bash -g "${FLOW_GID}" -u "${FLOW_UID}" -m -G docker "${FLOW_USERNAME}" @@ -40,6 +42,7 @@ chmod u+x "${DOCKER_COMPOSE_DESTINATION}" cat < ${ROOT_SETUP_DIRECTORY}/env export FLOW_USERNAME=${FLOW_USERNAME} +export FLOW_UID=${FLOW_UID} export FLOW_GID=${FLOW_GID} export ROOT_SETUP_DIRECTORY=${ROOT_SETUP_DIRECTORY} EOF