#cloud-config users: - default package_update: true packages: - ca-certificates - cron - docker.io - jq - restic - syslog-ng write_files: - content: | { #acme_ca https://acme-staging-v02.api.letsencrypt.org/directory } app.happydomain.org { reverse_proxy app-happydomain:8081 { flush_interval -1 } } try.happydomain.org { reverse_proxy demo-happydomain:8081 { flush_interval -1 } } lists.happydomain.org { tls { issuer acme { disable_http_challenge } } @adminroute path /admin /admin/* /api/* /webhooks/* basic_auth @adminroute { nemunaire "$2a$14$F937dQbs6iizd/.p8UZdKuY0O5BjKfkmshKFDH0uA4HJEIrSq2hZa" happyuser "$2a$14$QvpmUl8MBxuqB14mP393yuuQ24/lxibj3zBRzvCdovJQOPeKTzAQC" } reverse_proxy listmonk:9000 { flush_interval -1 } } feedback.happydomain.org { reverse_proxy feedback:3000 { flush_interval -1 } } path: /etc/caddy/Caddyfile - content: | @version:3.30 @include "scl.conf" # syslog-ng configuration file. # # See syslog-ng(8) and syslog-ng.conf(5) for more information. # # Note: It also sources additional configuration files (*.conf) # located in /etc/syslog-ng/conf.d/. # # Options # options { # Create destination directories if missing. create_dirs(yes); # The default action of syslog-ng is to log a MARK line to the file every # 20 minutes. That's seems high for most people so turn it down to once an # hour. Set it to zero if you don't want the functionality at all. mark_freq(3600); # The default action of syslog-ng is to log a STATS line to the file every # 10 minutes. That's pretty ugly after a while. Change it to every 12 hours # so you get a nice daily update of how many messages syslog-ng missed (0). stats_freq(43200); # Time to wait before a died connection is re-established (default is 60). time_reopen(5); # Disable DNS usage. # syslog-ng blocks on DNS queries, so enabling DNS may lead to a DoS attack. use_dns(no); dns-cache(no); # Default owner, group, and permissions for log files. owner(root); group(adm); perm(0640); # Default permissions for created directories. dir_perm(0755); }; source src { system(); internal(); }; filter f_auth { facility(auth, authpriv); }; filter f_syslog { not facility(authpriv, mail) and not message("^grsec:( From [^:]+:)? exec of.*") and not (program("named") and message("^client ")); }; filter f_cron { facility(cron); }; filter f_daemon { facility(daemon); }; filter f_kern { facility(kern); }; filter f_mail { facility(mail, news); }; filter f_user { facility(user); }; filter f_debug { not facility(auth, authpriv, news, mail); }; filter f_messages { level(info..warn) and not facility(auth, authpriv, mail, news) and not message("^grsec:( From [^:]+:)? exec of.*"); }; filter f_emergency { level(emerg); }; filter f_info { level(info); }; filter f_notice { level(notice); }; filter f_warn { level(warn); }; filter f_crit { level(crit); }; filter f_err { level(err); }; filter f_audit { message("^audit.*"); }; filter f_history { message(".*HISTORY*"); }; destination authlog { file("/var/log/auth.log"); }; destination syslog { file("/var/log/syslog"); }; destination kern { file("/var/log/kern.log"); }; destination user { file("/var/log/user.log"); }; destination mailinfo { file("/var/log/mail/mail.info"); }; destination mailwarn { file("/var/log/mail/mail.warn"); }; destination mailerr { file("/var/log/mail/mail.err"); }; destination audit { file("/var/log/audit.log"); }; destination messages { file("/var/log/messages"); }; destination emergency { file("/var/log/emergency"); }; log { source(src); filter(f_auth); destination(authlog); }; log { source(src); filter(f_mail); filter(f_err); destination(mailerr); }; #log { source(src); filter(f_messages); destination(messages); }; log { source(src); filter(f_emergency); destination(emergency); }; # Remote loghost destination loghost1 { tcp6("geb.ra.nemunai.re"); }; log { source(src); destination(loghost1); }; destination loghost2 { tcp6("jizah.masr.nemunai.re"); }; log { source(src); destination(loghost2); }; # Source additional configuration files (.conf extension only) @include "/etc/syslog-ng/conf.d/*.conf" path: /etc/syslog-ng/syslog-ng.conf - content: | # /etc/crontab: configuration file for cron # See cron(8) and crontab(5) for details. # m h dom mon dow user command 23 12 * * * root /root/launch_container_demo.sh && sleep 1 && /root/demo_initialize_data.sh 23 0 * * * root /root/launch_container_demo.sh && sleep 1 && /root/demo_initialize_data.sh path: /etc/crontab - content: | #!/bin/sh export AWS_ACCESS_KEY_ID=$(cloud-init query ds.metadata.RESTIC_AWS_ACCESS_KEY_ID) export AWS_SECRET_ACCESS_KEY=$(cloud-init query ds.metadata.RESTIC_AWS_SECRET_ACCESS_KEY) export RESTIC_REPOSITORY=$(cloud-init query ds.metadata.RESTIC_REPOSITORY) export RESTIC_PASSWORD=$(cloud-init query ds.metadata.RESTIC_PASSWORD) export RESTIC_COMPRESSION=max mkdir -p /var/backups/happydomain docker exec -i app-happydomain hadmin /api/backup.json -X POST > /var/backups/happydomain/db.json restic backup /var/backups/happydomain path: /etc/cron.daily/backup_happydomain permissions: 0o755 - content: | #!/bin/sh export AWS_ACCESS_KEY_ID=$(cloud-init query ds.metadata.RESTIC_AWS_ACCESS_KEY_ID) export AWS_SECRET_ACCESS_KEY=$(cloud-init query ds.metadata.RESTIC_AWS_SECRET_ACCESS_KEY) export RESTIC_REPOSITORY=$(cloud-init query ds.metadata.RESTIC_REPOSITORY_POSTGRES) export RESTIC_PASSWORD=$(cloud-init query ds.metadata.RESTIC_PASSWORD) export RESTIC_COMPRESSION=max mkdir -p /var/backups/postgres DBLIST=$(docker exec -i postgres psql -U postgres -d postgres -q -t -c "SELECT datname FROM pg_database WHERE datname NOT IN ('postgres', 'template0', 'template1')") for dbname in $DBLIST; do echo "Dumping database '$dbname'" docker exec -i postgres pg_dump -U postgres --no-owner --no-privileges --dbname="$dbname" > /var/backups/postgres/$dbname.sql || true # Ignore failures done restic backup /var/backups/postgres path: /etc/cron.daily/backup_postgres permissions: 0o755 - content: | #!/bin/bash set -e set -u function create_user_and_database() { local database=$1 echo " Creating user and database '$database'" PWD_VAR="POSTGRES_PASSWORD_$database" psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL CREATE USER $database WITH PASSWORD '${!PWD_VAR}'; CREATE DATABASE $database; ALTER DATABASE $database OWNER TO $database; GRANT ALL PRIVILEGES ON DATABASE $database TO $database; EOSQL [ -f "/var/backups/postgres/${database}.sql" ] && \ psql -v ON_ERROR_STOP=1 -U "${database}" -d "${database}" < "/var/backups/postgres/${database}.sql" } if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do create_user_and_database $db done echo "Multiple databases created" fi path: /etc/pgsql-init/00-create-databases.sh permissions: 0o755 - content: | #!/bin/sh export HAPPYDOMAIN_BIND="0.0.0.0:8081" export HAPPYDOMAIN_CUSTOM_HEAD_HTML="" export HAPPYDOMAIN_DEFAULT_NS="9.9.9.9:53" export HAPPYDOMAIN_EXTERNALURL="https://$(cloud-init query ds.metadata.MY_DOMAIN)" export HAPPYDOMAIN_JWT_SECRET_KEY="$(cloud-init query ds.metadata.HAPPYDOMAIN_JWT_SECRET_KEY)" export HAPPYDOMAIN_NEWSLETTER_SERVER_URL="https://$(cloud-init query ds.metadata.LISTMONK_API_USERNAME):$(cloud-init query ds.metadata.LISTMONK_API_PASSWORD)@lists.happydomain.org/" export HAPPYDOMAIN_NEWSLETTER_ID="$(cloud-init query ds.metadata.LISTMONK_NEWSLETTER_ID)" export HAPPYDOMAIN_MAIL_FROM="Fred from happyDomain " export HAPPYDOMAIN_MAIL_SMTP_HOST="$(cloud-init query ds.metadata.EMAIL_SMTP_HOST)" export HAPPYDOMAIN_MAIL_SMTP_PORT=$(cloud-init query ds.metadata.EMAIL_SMTP_PORT) export HAPPYDOMAIN_MAIL_SMTP_USERNAME="$(cloud-init query ds.metadata.EMAIL_SMTP_USERNAME)" export HAPPYDOMAIN_MAIL_SMTP_PASSWORD="$(cloud-init query ds.metadata.EMAIL_SMTP_PASSWORD)" export HAPPYDOMAIN_OVH_APPLICATION_KEY="$(cloud-init query ds.metadata.HAPPYDOMAIN_OVH_APPLICATION_KEY)" export HAPPYDOMAIN_OVH_APPLICATION_SECRET="$(cloud-init query ds.metadata.HAPPYDOMAIN_OVH_APPLICATION_SECRET)" export HAPPYDOMAIN_STORAGE_ENGINE="leveldb" [ -z "${HAPPYDOMAIN_VERSION}" ] && export HAPPYDOMAIN_VERSION=$(cloud-init query ds.metadata.HAPPYDOMAIN_VERSION) docker inspect app-happydomain > /dev/null && { docker pull happydomain/happydomain:${HAPPYDOMAIN_VERSION} docker stop app-happydomain docker rm app-happydomain } docker run -d --restart unless-stopped --network local -e HAPPYDOMAIN_BIND -e HAPPYDOMAIN_CUSTOM_HEAD_HTML -e HAPPYDOMAIN_DEFAULT_NS -e HAPPYDOMAIN_EXTERNALURL -e HAPPYDOMAIN_JWT_SECRET_KEY -e HAPPYDOMAIN_NEWSLETTER_SERVER_URL -e HAPPYDOMAIN_NEWSLETTER_ID -e HAPPYDOMAIN_MAIL_FROM -e HAPPYDOMAIN_MAIL_SMTP_HOST -e HAPPYDOMAIN_MAIL_SMTP_PORT -e HAPPYDOMAIN_MAIL_SMTP_USERNAME -e HAPPYDOMAIN_MAIL_SMTP_PASSWORD -e HAPPYDOMAIN_OVH_APPLICATION_KEY -e HAPPYDOMAIN_OVH_APPLICATION_SECRET -e HAPPYDOMAIN_STORAGE_ENGINE -p "8081:8081" --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=app-happydomain --name app-happydomain --pull always happydomain/happydomain:${HAPPYDOMAIN_VERSION} path: /root/launch_container_app.sh permissions: 0o755 - content: | #!/bin/sh # pdns docker inspect pdns-demo-happydomain > /dev/null && { docker pull nemunaire/pdns docker stop pdns-demo-happydomain docker rm pdns-demo-happydomain } docker run -d --restart unless-stopped --network local -e PDNS_AUTH_API_KEY=changeme --entrypoint /bin/sh --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=pdns-demo-happydomain --name pdns-demo-happydomain --pull always nemunaire/pdns -c "rm /var/lib/powerdns/pdns.sqlite3; sqlite3 /var/lib/powerdns/pdns.sqlite3 < /usr/share/doc/pdns/schema.sqlite3.sql && exec tini -- /usr/sbin/pdns_server-startup" # happyDomain demo export HAPPYDOMAIN_BIND="0.0.0.0:8081" export HAPPYDOMAIN_CUSTOM_HEAD_HTML="" export HAPPYDOMAIN_DEFAULT_NS="9.9.9.9:53" export HAPPYDOMAIN_EXTERNALURL="https://$(cloud-init query ds.metadata.TRY_DOMAIN)" export HAPPYDOMAIN_STORAGE_ENGINE="leveldb" [ -z "${HAPPYDOMAIN_VERSION}" ] && export HAPPYDOMAIN_VERSION=$(cloud-init query ds.metadata.HAPPYDOMAIN_VERSION) docker inspect demo-happydomain > /dev/null && { docker pull happydomain/happydomain:${HAPPYDOMAIN_VERSION} docker stop demo-happydomain docker rm demo-happydomain } docker run -d --restart unless-stopped --network local -e HAPPYDOMAIN_BIND -e HAPPYDOMAIN_CUSTOM_HEAD_HTML -e HAPPYDOMAIN_DEFAULT_NS -e HAPPYDOMAIN_EXTERNALURL -e HAPPYDOMAIN_DISABLE_PROVIDERS_EDIT=true -e HAPPYDOMAIN_NO_AUTH=1 -e HAPPYDOMAIN_MSG_HEADER_TEXT="Shared demo instance; data reset at 00:34 and 12:34 UTC" -e HAPPYDOMAIN_MSG_HEADER_COLOR="warning" -e HAPPYDOMAIN_STORAGE_ENGINE --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=demo-happydomain --name demo-happydomain --pull always happydomain/happydomain:${HAPPYDOMAIN_VERSION} path: /root/launch_container_demo.sh permissions: 0o755 - content: | #!/bin/sh [ -z "$POSTGRES_PASSWORD_fider" ] && export $(docker inspect postgres -f 'json' | jq -r '.[0].Config.Env[]' | grep ^POSTGRES_PASSWORD_fider) docker run -d --restart unless-stopped --network local -e BASE_URL="https://$(cloud-init query ds.metadata.FIDER_DOMAIN)" -e DATABASE_URL="postgres://fider:${POSTGRES_PASSWORD_fider}@postgres:5432/fider?sslmode=disable" -e JWT_SECRET="$(cloud-init query ds.metadata.FIDER_JWT_SECRET)" -e EMAIL_NOREPLY="feedback@happydomain.org" -e EMAIL_SMTP_HOST="$(cloud-init query ds.metadata.EMAIL_SMTP_HOST)" -e EMAIL_SMTP_PORT=$(cloud-init query ds.metadata.EMAIL_SMTP_PORT) -e EMAIL_SMTP_USERNAME="$(cloud-init query ds.metadata.EMAIL_SMTP_USERNAME)" -e EMAIL_SMTP_PASSWORD="$(cloud-init query ds.metadata.EMAIL_SMTP_PASSWORD)" -e EMAIL_SMTP_ENABLE_STARTTLS=true -e OAUTH_GITHUB_CLIENTID="$(cloud-init query ds.metadata.FIDER_GITHUB_CLIENTID)" -e OAUTH_GITHUB_SECRET="$(cloud-init query ds.metadata.FIDER_GITHUB_SECRET)" -p 3000:3000 --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=feedback --name feedback --pull always getfider/fider:stable docker run -d --restart unless-stopped --network local -v /etc/listmonk.toml:/listmonk/config.toml:ro -e GENERIC_TIMEZONE="Europe/Paris" -e TZ="Europe/Paris" -p "9000:9000" --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=listmonk --name listmonk --pull always ghcr.io/knadh/listmonk:latest path: /root/launch_container_other.sh permissions: 0o755 runcmd: # Allow traffic in IPv4 - sed -i '/-A INPUT -j REJECT/i-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT\n-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT' /etc/iptables/rules.v4 - iptables -I INPUT 5 -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT - iptables -I INPUT 5 -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT # Retrieve last backups - export AWS_ACCESS_KEY_ID=$(cloud-init query ds.metadata.RESTIC_AWS_ACCESS_KEY_ID) - export AWS_SECRET_ACCESS_KEY=$(cloud-init query ds.metadata.RESTIC_AWS_SECRET_ACCESS_KEY) - export RESTIC_PASSWORD=$(cloud-init query ds.metadata.RESTIC_PASSWORD) - export RESTIC_REPOSITORY=$(cloud-init query ds.metadata.RESTIC_REPOSITORY) - mkdir -p /var/backups/happydomain - restic restore latest --target / --include /var/backups/happydomain - export RESTIC_REPOSITORY=$(cloud-init query ds.metadata.RESTIC_REPOSITORY_POSTGRES) - mkdir -p /var/backups/postgres - restic restore latest --target / --include /var/backups/postgres - touch /var/backups/postgres/fider.sql - touch /var/backups/postgres/listmonk.sql # Create docker network - docker network create local # Generate database password - export POSTGRES_PASSWORD_fider=$(openssl rand -base64 30 | sed 's@/@.@g') - export POSTGRES_PASSWORD_listmonk=$(openssl rand -base64 30) # Launch database - | cat > /etc/pgsql-init/70-update-listmonk-settings.sh < /etc/listmonk.toml [app] address = "0.0.0.0:9000" [db] host = "postgres" port = 5432 user = "listmonk" password = "${POSTGRES_PASSWORD_listmonk}" database = "listmonk" ssl_mode = "disable" max_open = 25 max_idle = 25 max_lifetime = "300s" EOF # Launch others containers - /root/launch_container_other.sh # Launch demo containers - /root/launch_container_demo.sh # Restore happydomain backups - | [ -f /var/backups/happydomain/db.json ] && docker exec -i app-happydomain hadmin /api/backup.json -X PUT -d @- < /var/backups/happydomain/db.json # Apply demo data - | [ -x /root/demo_initialize_data.sh ] && /root/demo_initialize_data.sh