#!/bin/bash set -e test_ca_list=() case "$1" in --test) ln -sf test.env .env test_ca_list=(test/certs/cur-root.crt test/certs/pebble.minica.pem) ;; --production) ln -sf production.env .env ;; *) echo "usage: $0 --test|--production" >&2 exit 1 ;; esac . .env subdomains=('' mail git forum) function fatal() { echo "fatal: $*" >&2 exit 1 } function random_passwd() { tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 16 } function wait_for_server() { local i echo "waiting for server to start up..." for i in {1..30}; do sleep 1 if curl "http://$BASE_DOMAIN_NAME/" >/dev/null; then echo "server up" return fi done fatal "server failed to start up after $i attempts" } function retry_if_failed() { local retry error_args=": $*" if [[ "$1" == "-q" ]]; then error_args="" shift fi for retry in {1..3}; do if "$@"; then return fi echo "command failed (retry $retry)$error_args" >&2 sleep 2 done fatal "command failed$error_args" } function forgejo() { docker run --rm codeberg.org/forgejo/forgejo:7 forgejo "$@" } if [[ "$(id -u)" != 0 ]]; then fatal "must be ran as root" fi mkdir -p /var/lib/stalwart-mail apt-get update -y -q apt-get install jq -y -q # force using overlay2 driver so btrfs snapshots will snapshot the entire system and not miss all the docker stuff mkdir -p /etc/docker if [[ -f /etc/docker/daemon.json ]]; then [[ "$(jq -sc '[.[0]?["storage-driver"]?]' < /etc/docker/daemon.json)" == '["overlay2"]' ]] || fatal '/etc/docker/daemon.json exists but `storage-driver` is not set to overlay2' elif [[ "$(dpkg-query -W --showformat='${db:Status-Abbrev}\n' docker.io 2> /dev/null)" =~ ^$|^.[nc]' '$ ]]; then cat > /etc/docker/daemon.json < /usr/local/bin/gitea #!/bin/sh exec ssh -p 2222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@" EOF chmod +x /usr/local/bin/gitea mkdir -p /etc/forgejo rm -rf /var/www/html mkdir -p /var/www/html chown git:git /var/www/html chmod 775 /var/www/html (cd /var/www/html && sudo -u git git init --no-initial-branch) (cd /var/www/html && sudo -u git git remote add -t heads/rendered --mirror=fetch origin /data/git/repositories/libre-chip/website.git) chown root:git /etc/forgejo chmod 770 /etc/forgejo if [[ ! -f /etc/forgejo/app.ini ]]; then cat < /etc/forgejo/app.ini APP_NAME = Libre-Chip.org RUN_MODE = prod RUN_USER = git WORK_PATH = /data/gitea [repository] ROOT = /data/git/repositories [repository.local] LOCAL_COPY_PATH = /data/gitea/tmp/local-repo [repository.upload] TEMP_PATH = /data/gitea/uploads [server] APP_DATA_PATH = /data/gitea DOMAIN = git.$BASE_DOMAIN_NAME SSH_DOMAIN = git.$BASE_DOMAIN_NAME HTTP_PORT = 3000 ROOT_URL = https://git.$BASE_DOMAIN_NAME/ DISABLE_SSH = false SSH_PORT = 22 SSH_LISTEN_PORT = 22 LFS_START_SERVER = true OFFLINE_MODE = false LFS_JWT_SECRET = $(forgejo generate secret LFS_JWT_SECRET) [database] PATH = /data/gitea/gitea.db DB_TYPE = sqlite3 HOST = localhost:3306 NAME = gitea USER = root PASSWD = LOG_SQL = false SCHEMA = SSL_MODE = disable [indexer] ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve [session] PROVIDER_CONFIG = /data/gitea/sessions PROVIDER = file [picture] AVATAR_UPLOAD_PATH = /data/gitea/avatars REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars [attachment] PATH = /data/gitea/attachments [log] MODE = console LEVEL = info ROOT_PATH = /data/gitea/log [security] INSTALL_LOCK = true SECRET_KEY = $(forgejo generate secret SECRET_KEY) REVERSE_PROXY_LIMIT = 1 REVERSE_PROXY_TRUSTED_PROXIES = * PASSWORD_HASH_ALGO = pbkdf2_hi DISABLE_GIT_HOOKS = false INTERNAL_TOKEN = $(forgejo generate secret INTERNAL_TOKEN) [service] DISABLE_REGISTRATION = false REQUIRE_SIGNIN_VIEW = false REGISTER_EMAIL_CONFIRM = true ENABLE_NOTIFY_MAIL = true ALLOW_ONLY_EXTERNAL_REGISTRATION = false ENABLE_CAPTCHA = true DEFAULT_KEEP_EMAIL_PRIVATE = false DEFAULT_ALLOW_CREATE_ORGANIZATION = false DEFAULT_ENABLE_TIMETRACKING = true NO_REPLY_ADDRESS = noreply.$BASE_DOMAIN_NAME [lfs] PATH = /data/git/lfs [mailer] ENABLED = true PROTOCOL = smtps SMTP_ADDR = mail.$BASE_DOMAIN_NAME SMTP_PORT = 465 FROM = forgejo@$BASE_DOMAIN_NAME USER = forgejo PASSWD = $(random_passwd) [openid] ENABLE_OPENID_SIGNIN = true ENABLE_OPENID_SIGNUP = true [cron.update_checker] ENABLED = false [repository.pull-request] DEFAULT_MERGE_STYLE = merge [repository.signing] DEFAULT_TRUST_MODEL = committer [ssh.minimum_key_sizes] RSA = 2047 [oauth2] JWT_SECRET = $(forgejo generate secret JWT_SECRET) EOF chown root:git /etc/forgejo/app.ini chmod 640 /etc/forgejo/app.ini fi mkdir -p /var/lib/stalwart-mail/etc mail_passwd="" mail_passwd_hash="" if [[ ! -f /var/lib/stalwart-mail/etc/config.toml ]]; then mail_passwd="$(random_passwd)" cat > /var/lib/stalwart-mail/cli.sh < /var/lib/stalwart-mail/etc/config.toml < /var/discourse/containers/app.yml </dev/null; then break fi done echo "server up" certbot_args=(certonly -n --email "postmaster@$BASE_DOMAIN_NAME" "--server=$ACME_SERVER_URL" --cert-name server --agree-tos --webroot --webroot-path /var/www) certbot_args+=(--disable-hook-validation --post-hook "cd '$wd' && docker-compose -p server restart") for subdomain in "${subdomains[@]}"; do if [[ -n "$subdomain" ]]; then subdomain+=. fi certbot_args+=(-d "$subdomain$BASE_DOMAIN_NAME") certbot_args+=(-d "$subdomain$ALT_BASE_DOMAIN_NAME") done retry_if_failed certbot "${certbot_args[@]}" trap EXIT docker stop "$nginx_container" DOCKER_BUILDKIT=1 docker-compose -p server up -d sleep 1 if [[ -n "$mail_passwd_hash" ]]; then forgejo_smtp_passwd="$(crudini --get /etc/forgejo/app.ini mailer PASSWD)" stalwart-cli domain create "$BASE_DOMAIN_NAME" curl -u "admin:$mail_passwd" "https://mail.$BASE_DOMAIN_NAME/api/dkim" --data-binary '{"id":null,"algorithm":"Ed25519","domain":"'"$BASE_DOMAIN_NAME"'","selector":null}' > /dev/null curl -u "admin:$mail_passwd" "https://mail.$BASE_DOMAIN_NAME/api/dkim" --data-binary '{"id":null,"algorithm":"Rsa","domain":"'"$BASE_DOMAIN_NAME"'","selector":null}' > /dev/null stalwart-cli account create -d 'Admin Account' -i true -a "postmaster@$BASE_DOMAIN_NAME" 'admin' "$mail_passwd" stalwart-cli account create -d 'Forgejo Server' -i false -a "forgejo@$BASE_DOMAIN_NAME" 'forgejo' "$forgejo_smtp_passwd" add_postmaster=(docker-compose -p server exec -T -u git forgejo forgejo admin user create --admin --username postmaster --password "$mail_passwd" --email "postmaster@$BASE_DOMAIN_NAME") retry_if_failed -q "${add_postmaster[@]}" forum_smtp_passwd="$(sed 's/^ *DISCOURSE_SMTP_PASSWORD: "*\([^"]*\)"$/\1/p; d' < /var/discourse/containers/app.yml)" [[ -n "$forum_smtp_passwd" ]] || fatal "can't parse smtp password out of /var/discourse/containers/app.yml" stalwart-cli account create -d 'Forum Replies' -i false -a "@$BASE_DOMAIN_NAME" 'forum' "$forum_smtp_passwd" stalwart-cli account create -d 'Forum Notifications' -i false -a "forum-noreply@$BASE_DOMAIN_NAME" 'forum-noreply' "$forum_smtp_passwd" forgejo_api=(retry_if_failed -q curl --fail-with-body -u "postmaster:$mail_passwd" -H 'Accept: application/json' -H 'Content-Type: application/json') "${forgejo_api[@]}" -X 'POST' "https://git.$BASE_DOMAIN_NAME/api/v1/orgs" -d '{"username": "libre-chip"}' > /dev/null "${forgejo_api[@]}" -X 'POST' "https://git.$BASE_DOMAIN_NAME/api/v1/orgs/libre-chip/repos" -d '{"name": "website"}' > /dev/null post_receive_hook="$(jq -csR '{content:.}' <<'EOF' #!/bin/bash set -e cd /var/www/html env -i PATH="$PATH" git fetch env -i PATH="$PATH" git checkout -q --detach rendered EOF )" "${forgejo_api[@]}" -X 'PATCH' "https://git.$BASE_DOMAIN_NAME/api/v1/repos/libre-chip/website/hooks/git/post-receive" -d "$post_receive_hook" > /dev/null fi ( cd /var/discourse # must run after starting mail server since it validates POP3 ./launcher bootstrap app ./launcher start app )