Deploy app, demo, fider and listmonk on OCI

This commit is contained in:
nemunaire 2024-10-23 22:43:38 +02:00
parent b45e29c278
commit b3a1eba792
8 changed files with 1004 additions and 1 deletions

View File

@ -1,4 +1,28 @@
config:
infra-happyDomain:fider_github_clientid:
secure: AAABABgXd86uOrZsOJejnjqCeNrrI/AVHEALi/VVhoaQdkJrHA9lxu2b/ctKvaWV+XZ+7A==
infra-happyDomain:fider_github_secret:
secure: AAABALPjvMXkBjZLx4okTac8YdhBllcdnZTyNVLZHjuGAE3pcR5LjRvB8gN4XHMo+vlVXbkE5n5NBFGTz7Ka2abwliDRl2DW
infra-happyDomain:fider_jwt_secret:
secure: AAABAEGORkViwn3kMhOt2vnbrQKk8d7xcJC2xhUdc1gPang80HQBLpkB64kzvcppt04n0YziIlxfnnmeroFE3bplyja/3KrxCNxfvNuzcgEayZNQAjBte7YzGpM6iHMY
infra-happyDomain:happydomain_jwt_secret_key:
secure: AAABAO/gNZhqN+mybhh8VsATf3F0Dklc/h88qPMuieQStQQa9rv3R1qMf8lBvjnLCJKEnJnEZQpE7RP0YrX5dx5U2WMG7eKVeE4eNzaIHkxbM2NgGFEVl96dp7/A75dY
infra-happyDomain:happydomain_ovh_application_key:
secure: AAABAPc3FRKWbvBGbmBb5YtU/CJd7EUsn+++Xi49RFk59Idi1W+l8EqhvqpyUGK2
infra-happyDomain:happydomain_ovh_application_secret:
secure: AAABAP0O4tvPQzRy0fgcOiElgZzL3ON5Oos/rzr6tFubGblCF7tC8MSp06iAUgpX2J2hN0/047gAZGdW90d0sg==
infra-happyDomain:listmonk_api_password:
secure: AAABAJc7vc/Rt8azTrsPtk88omCW5koGOkD1/CL45I3lmArb2reA7JkHJb1C4uLvy0VYrDnCXNwNfNy7ompC
infra-happyDomain:listmonk_api_username:
secure: AAABAAJaF5qECuXAJQEuSQxOfKPb2vTolugGaukVmdkAdQNswPpZnVk=
infra-happyDomain:postgres_password:
secure: AAABAB/ISsauXbqLKv4BkjAI/9RdUZkIAuTxlDIepxH9qYQD00KvOTbCLVHbPEq17P6P5mZS/75XIvk6UHgb
infra-happyDomain:restic_aws_access_key_id:
secure: AAABAI89qmSeNjBH6OpF/Ym9eEDEG4Y1JbLC6uRwHMk+IllpgTVxOuBO/NG9JA3RgHgFsw==
infra-happyDomain:restic_aws_secret_access_key:
secure: AAABAM39hDfydSv0IZVLZf1fSwIJJAD94ve2wsELAJYxdtn2Pe7C5TXplaAAR8qH/56wS23n24BJGQcLgjv+Hy+vwUoIu+zs
infra-happyDomain:restic_password:
secure: AAABADIJzvbIRfqwrfVyNgjJKVW1uBH8Bhld1dKYJyMXVRPXTcenUPJ/oA9dPDqGlur0J1UdnKpaUPjIU8xf
oci:fingerprint:
secure: AAABAIF01WUHBaDO1ebReUpxMGKFtnuhenKdjdxqMmniQv091K1rG/EPfDwy2RJ2Btm6yl9U/usIM4QHpcl46sZFO9QtV4Pv7rZzdNrK/Q==
oci:privateKeyPath: terraform@p0m.fr_2024-07-30T09_19_34.293Z.pem

382
cloud-init.yaml Normal file
View File

@ -0,0 +1,382 @@
#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="<script async defer data-website-id=\"$(cloud-init query ds.metadata.UMAMI_ID)\" src=\"https://pythagore.p0m.fr/pythagore.js\"></script>"
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 <contact@happydomain.org>"
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="<script async defer data-website-id=\"$(cloud-init query ds.metadata.TRY_UMAMI_ID)\" src=\"https://pythagore.p0m.fr/pythagore.js\"></script>"
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 <<EOF
#!/bin/sh
set -e
set -u
psql -v ON_ERROR_STOP=1 --username "\$POSTGRES_USER" -d listmonk <<-EOSQL
UPDATE settings SET value = '"https://$(cloud-init query ds.metadata.LISTMONK_DOMAIN)"' WHERE key = 'app.root_url';
UPDATE settings SET value = '"$(cloud-init query ds.metadata.LISTMONK_S3_CLIENT_ID)"' WHERE key = 'upload.s3.aws_access_key_id';
UPDATE settings SET value = '"$(cloud-init query ds.metadata.LISTMONK_S3_CLIENT_SECRET)"' WHERE key = 'upload.s3.aws_secret_access_key';
UPDATE settings SET value = '"https://$(cloud-init query ds.metadata.LISTMONK_S3_HOST)/"' WHERE key = 'upload.s3.url';
UPDATE settings SET value = '"$(cloud-init query ds.metadata.LISTMONK_S3_REGION)"' WHERE key = 'upload.s3.aws_default_region';
UPDATE settings SET value = '"$(cloud-init query ds.metadata.LISTMONK_S3_BUCKET)"' WHERE key = 'upload.s3.bucket';
UPDATE settings SET value = '[{"host": "$(cloud-init query ds.metadata.EMAIL_SMTP_HOST)", "port": $(cloud-init query ds.metadata.EMAIL_SMTP_PORT), "uuid": "16aee4cf-4e54-401d-a02d-70097e44315e", "enabled": true, "password": $(cloud-init query ds.metadata.EMAIL_SMTP_PASSWORD | jq -R . | sed 's/\$/\\$/'), "tls_type": "STARTTLS", "username": "$(cloud-init query ds.metadata.EMAIL_SMTP_USERNAME)", "max_conns": 10, "idle_timeout": "1m", "wait_timeout": "5s", "auth_protocol": "plain", "email_headers": [], "hello_hostname": "", "max_msg_retries": 4, "tls_skip_verify": false}]' WHERE key = 'smtp';
EOSQL
EOF
- chmod +x /etc/pgsql-init/70-update-listmonk-settings.sh
- docker run -d --restart unless-stopped --network local --shm-size=512MB -v /var/backups/postgres/:/var/backups/postgres/ -v /etc/pgsql-init/:/docker-entrypoint-initdb.d/ -v /var/lib/postgres/data:/var/lib/postgresql/data -e POSTGRES_PASSWORD=$(cloud-init query ds.metadata.POSTGRES_PASSWORD) -e POSTGRES_MULTIPLE_DATABASES="fider,listmonk" -e POSTGRES_PASSWORD_fider -e POSTGRES_PASSWORD_listmonk --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=postgres --name postgres --pull always --name postgres postgres:alpine
# Launch web server
- docker run -d --restart unless-stopped --network local -v /etc/caddy:/etc/caddy -v /srv/:/srv/ -p 80:80 -p 443:443 --log-driver syslog --log-opt "syslog-address=unixgram:///dev/log" --log-opt syslog-facility=daemon --log-opt tag=caddy --name caddy caddy:latest
# Launch container
- /root/launch_container_app.sh
# Generate listmonk config
- |
cat <<EOF > /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

4
consts.go Normal file
View File

@ -0,0 +1,4 @@
package main
const SHAPE_AMD64 = "VM.Standard.E2.1.Micro"
const SSH_AUTHORIZED_KEYS = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILO2HHqD/MDYpPjYVMdvYI9Jn1FoyFp43IkPRzjZGvdL nemunaire@oupaout.ra.nemunai.re"

130
demo_initialize_data.sh Normal file
View File

@ -0,0 +1,130 @@
#!/bin/sh
# Configure pdns
[ -z "${PDNS_CONTAINER}" ] && PDNS_CONTAINER=pdns-demo-happydomain
docker exec ${PDNS_CONTAINER} pdnsutil create-zone happydomain.test ns1.happydomain.org
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test @ A 198.51.100.1
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test @ AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:1234
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test @ MX "10 mx1"
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test @ MX "10 mx2"
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test @ TXT '"v=spf1 +mx -all"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test mail._domainkey TXT '"v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2s==;s=email"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test _dmarc TXT '"v=DMARC1;p=none;sp=reject;rua=mailto:postmaster@happydomain.test;ruf=mailto:postmaster@happydomain.test;adkim=s;aspf=s"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test _mta-sts TXT '"v=STSv1; id=20181231172300Z"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test _smtp._tls TXT '"v=TLSRPTv1; rua=mailto:postmaster@happydomain.test"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test _matrix._tcp SRV "10 0 8448 matrix.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test augustus A 203.0.113.35
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test augustus AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:7891
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test augustus SSHFP "1 1 3A92EA036ECFAAFB7262651970E7A8A9C17AADFB"
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test augustus SSHFP "1 2 FAF1B7B2BE4AFF034C8DA465ED90B317ADDFA3EBA97914D16BF2D3B75283F3FA"
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test augustus SSHFP "4 1 CC1E38492A753DA93FCB8605393B0C6F058E829D"
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test augustus SSHFP "4 2 B03BD08AFE6DA44E879E7C15EE4D121DD64F27514866314E79E07C76DDF4A65C"
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test mx1 A 198.51.100.12
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test mx1 AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:4567
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test mx2 A 203.0.113.34
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test mx2 AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:7890
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test matrix CNAME augustus.happydomain.test.
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.test www CNAME augustus.happydomain.test.
docker exec ${PDNS_CONTAINER} pdnsutil create-zone happydomain.invalid ns1.happydomain.org
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.invalid @ A 198.51.100.1
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.invalid @ AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:1234
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.invalid @ MX "10 mx1.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.invalid @ MX "10 mx2.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.invalid @ TXT '"v=spf1 +mx -all"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record happydomain.invalid www CNAME augustus.happydomain.test.
docker exec ${PDNS_CONTAINER} pdnsutil create-zone example.com ns1.happydomain.org
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com @ A 198.51.100.1
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com @ AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:1234
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com @ MX "10 mx1.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com @ MX "10 mx2.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com @ TXT '"v=spf1 +mx -all"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com www CNAME augustus.happydomain.test.
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com _443._tcp.www TLSA "3 1 2 dc3ada2a3cf089c87f4b1fa147d77b4ee5f2c764e53dc57618f07d86c4204032f34e32746b0ff4881313d4ca0ced656863caea539f62fba427c7a677a32c5831"
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com _xmpp-client._tcp SRV "10 0 5222 augustus.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.com _xmpp-server._tcp SRV "10 0 5269 augustus.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil create-zone example.net ns1.happydomain.org
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net @ A 198.51.100.1
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net @ AAAA 2001:db8:a3f9:22dc:8301:c4b6:783e:1234
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net @ MX "10 mx1.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net @ MX "10 mx2.happydomain.test."
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net @ TXT '"v=spf1 +mx -all"'
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net www CNAME augustus.happydomain.test.
docker exec ${PDNS_CONTAINER} pdnsutil add-record example.net _443._tcp.www CNAME _443._tcp.augustus.happydomain.test.
# Configure happyDomain
[ -z "${HAPPYDOMAIN_CONTAINER}" ] && HAPPYDOMAIN_CONTAINER=demo-happydomain
USERID="AA"
USEREMAIL="_no_auth"
# Create real user
docker exec -i ${HAPPYDOMAIN_CONTAINER} hadmin /api/users -X POST -d @- <<EOF
{
"id": "${USERID}",
"email": "${USEREMAIL}",
"created_at": "2024-01-01T12:21:42Z",
"last_seen": "2024-01-01T12:21:42Z",
"settings": {
"language": "en",
"fieldhint": 2,
"zoneview": 0
}
}
EOF
# Register user's providers
PRVNYC=$(docker exec -i ${HAPPYDOMAIN_CONTAINER} hadmin /api/users/${USEREMAIL}/providers -X POST -d @- <<EOF | jq -r ._id
{
"Provider": {
"apiurl": "http://pdns-demo-happydomain:8081",
"apikey": "changeme",
"server_id": "localhost"
},
"_srctype": "PowerdnsAPI",
"_comment": "PowerDNS Paris"
}
EOF
)
# Register user's domains
## happydomain.test
docker exec -i ${HAPPYDOMAIN_CONTAINER} hadmin /api/users/${USEREMAIL}/domains -X POST -d @- <<EOF
{
"id": "hV0iXIDNUyHAMPgEXF_gFg",
"id_owner": "${USERID}",
"id_provider": "${PRVNYC}",
"domain": "happydomain.test.",
"group": "other customer",
"zone_history": []
}
EOF
## example.com
docker exec -i ${HAPPYDOMAIN_CONTAINER} hadmin /api/users/${USEREMAIL}/domains -X POST -d @- <<EOF
{
"id": "hV0iXIDNUyHAMPgEXF_gFh",
"id_owner": "${USERID}",
"id_provider": "${PRVNYC}",
"domain": "example.com.",
"group": "customer 1",
"zone_history": []
}
EOF
docker exec -i ${HAPPYDOMAIN_CONTAINER} hadmin /api/users/${USEREMAIL}/domains -X POST -d @- <<EOF
{
"id": "hV0iXIDNUyHAMPgEXF_gFi",
"id_owner": "${USERID}",
"id_provider": "${PRVNYC}",
"domain": "example.net.",
"group": "customer 1",
"zone_history": []
}
EOF

167
host.go Normal file
View File

@ -0,0 +1,167 @@
package main
import (
"encoding/base64"
"io"
"io/ioutil"
"github.com/pulumi/pulumi-oci/sdk/go/oci/core"
"github.com/pulumi/pulumi-oci/sdk/go/oci/identity"
"github.com/pulumi/pulumi-oci/sdk/go/oci/networkloadbalancer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)
func setupHostMain(ctx *pulumi.Context, ocicfg *config.Config, compartment *identity.Compartment, ns pulumi.StringOutput, subnet *core.Subnet, listmonkAuthToken *identity.CustomerSecretKey, smtpcreds *identity.SmtpCredential, pemprvkey io.Reader) error {
cfg := config.New(ctx, "")
// Setup load-balancer
nlb, err := networkloadbalancer.NewNetworkLoadBalancer(ctx, "happy-nlb", &networkloadbalancer.NetworkLoadBalancerArgs{
DisplayName: pulumi.Sprintf("%s-happy-nlb", ctx.Stack()),
SubnetId: subnet.ID(),
CompartmentId: compartment.ID(),
IsPreserveSourceDestination: pulumi.Bool(false),
IsPrivate: pulumi.Bool(false),
NlbIpVersion: pulumi.String("IPV4"),
})
if err != nil {
return err
}
ctx.Export("nlb-ip", nlb.IpAddresses)
nlbset4, err := networkloadbalancer.NewBackendSet(ctx, "happydomain-nlbset4", &networkloadbalancer.BackendSetArgs{
HealthChecker: &networkloadbalancer.BackendSetHealthCheckerArgs{
Protocol: pulumi.String("HTTPS"),
Port: pulumi.Int(443),
UrlPath: pulumi.String("/api/version"),
ReturnCode: pulumi.Int(200),
},
Name: pulumi.Sprintf("%s-happydomain-nlbset4", ctx.Stack()),
NetworkLoadBalancerId: nlb.ID(),
Policy: pulumi.String("FIVE_TUPLE"),
IpVersion: pulumi.String("IPV4"),
IsPreserveSource: pulumi.Bool(true),
IsFailOpen: pulumi.Bool(true),
})
if err != nil {
return err
}
_, err = networkloadbalancer.NewListener(ctx, "happydomain-listener4", &networkloadbalancer.ListenerArgs{
DefaultBackendSetName: nlbset4.Name,
Name: pulumi.Sprintf("%s-happydomain-nlb-listen4", ctx.Stack()),
NetworkLoadBalancerId: nlb.ID(),
Port: pulumi.Int(0),
Protocol: pulumi.String("TCP"),
IpVersion: pulumi.String("IPV4"),
})
if err != nil {
return err
}
// Get boot image
imageId := compartment.CompartmentId.ApplyT(func(id string) string {
images, _ := core.GetImages(ctx, &core.GetImagesArgs{
CompartmentId: id,
OperatingSystem: pulumi.StringRef("Canonical Ubuntu"),
OperatingSystemVersion: pulumi.StringRef("22.04 Minimal"),
SortBy: pulumi.StringRef("TIMECREATED"),
SortOrder: pulumi.StringRef("DESC"),
Shape: pulumi.StringRef(SHAPE_AMD64),
})
return images.Images[0].Id
}).(pulumi.StringOutput)
// Get availability domains
availabilityDomainName := compartment.CompartmentId.ApplyT(func(id string) string {
availabilityDomains, _ := identity.GetAvailabilityDomains(ctx, &identity.GetAvailabilityDomainsArgs{
CompartmentId: id,
})
return availabilityDomains.AvailabilityDomains[0].Name
}).(pulumi.StringOutput)
// Load cloudinit
userData, err := ioutil.ReadFile("cloud-init.yaml")
if err != nil {
return err
}
storens := ns.ApplyT(func(storageNamespace string) string {
return storageNamespace + ".compat.objectstorage." + ocicfg.Require("region") + ".oraclecloud.com"
}).(pulumi.StringOutput)
// Create an OCI instance
instance, err := core.NewInstance(ctx, "happydomain-main-1", &core.InstanceArgs{
AvailabilityDomain: availabilityDomainName,
CompartmentId: compartment.ID(),
DisplayName: pulumi.Sprintf("%s-happydomain-main", ctx.Stack()),
Shape: pulumi.String(SHAPE_AMD64),
SourceDetails: &core.InstanceSourceDetailsArgs{
SourceId: imageId,
SourceType: pulumi.String("image"),
},
CreateVnicDetails: &core.InstanceCreateVnicDetailsArgs{
AssignIpv6ip: pulumi.Bool(true),
SubnetId: subnet.ID(),
DisplayName: pulumi.Sprintf("%s-happydomain-main", ctx.Stack()),
},
ExtendedMetadata: pulumi.Map{
"EMAIL_SMTP_HOST": pulumi.String("smtp.email." + ocicfg.Require("region") + ".oci.oraclecloud.com"),
"EMAIL_SMTP_PORT": pulumi.String("587"),
"EMAIL_SMTP_USERNAME": smtpcreds.Username,
"EMAIL_SMTP_PASSWORD": smtpcreds.Password,
"FIDER_DOMAIN": pulumi.String("feedback.happydomain.org"),
"FIDER_JWT_SECRET": cfg.RequireSecret("fider_jwt_secret"),
"FIDER_GITHUB_CLIENTID": cfg.RequireSecret("fider_github_clientid"),
"FIDER_GITHUB_SECRET": cfg.RequireSecret("fider_github_secret"),
"UMAMI_ID": pulumi.String("3a9d70d8-c2d4-44e0-9fa1-46d4b2e3fca0"),
"HAPPYDOMAIN_JWT_SECRET_KEY": cfg.RequireSecret("happydomain_jwt_secret_key"),
"HAPPYDOMAIN_OVH_APPLICATION_KEY": cfg.RequireSecret("happydomain_ovh_application_key"),
"HAPPYDOMAIN_OVH_APPLICATION_SECRET": cfg.RequireSecret("happydomain_ovh_application_secret"),
"HAPPYDOMAIN_VERSION": pulumi.String("latest"),
"MY_DOMAIN": pulumi.String("app.happydomain.org"),
"LISTMONK_NEWSLETTER_ID": pulumi.String("4"),
"LISTMONK_API_USERNAME": cfg.RequireSecret("listmonk_api_username"),
"LISTMONK_API_PASSWORD": cfg.RequireSecret("listmonk_api_password"),
"LISTMONK_DOMAIN": pulumi.String("lists.happydomain.org"),
"LISTMONK_S3_BUCKET": pulumi.String(HappyListmonkBucketName),
"LISTMONK_S3_CLIENT_ID": listmonkAuthToken.ID(),
"LISTMONK_S3_CLIENT_SECRET": listmonkAuthToken.Key,
"LISTMONK_S3_HOST": storens,
"LISTMONK_S3_REGION": pulumi.String(ocicfg.Require("region")),
"POSTGRES_PASSWORD": cfg.RequireSecret("postgres_password"),
"RESTIC_REPOSITORY": pulumi.String("s3:storage.nemunai.re/zbackup-happydomain"),
"RESTIC_REPOSITORY_POSTGRES": pulumi.String("s3:storage.nemunai.re/zbackup-postgres-happydomain"),
"RESTIC_PASSWORD": cfg.RequireSecret("restic_password"),
"RESTIC_AWS_ACCESS_KEY_ID": cfg.RequireSecret("restic_aws_access_key_id"),
"RESTIC_AWS_SECRET_ACCESS_KEY": cfg.RequireSecret("restic_aws_secret_access_key"),
"TRY_DOMAIN": pulumi.String("try.happydomain.org"),
"TRY_UMAMI_ID": pulumi.String("0af0b29f-bf8a-4801-918a-01a8fb4b4312"),
},
Metadata: pulumi.Map{
"user_data": pulumi.String(base64.StdEncoding.EncodeToString(userData)),
"ssh_authorized_keys": pulumi.String(SSH_AUTHORIZED_KEYS),
},
})
if err != nil {
return err
}
// Export the public-ip
ctx.Export("instance-ip", instance.PublicIp)
// Add host to backend
_, err = networkloadbalancer.NewBackend(ctx, "happydomain-lb4", &networkloadbalancer.BackendArgs{
BackendSetName: nlbset4.Name,
NetworkLoadBalancerId: nlb.ID(),
Port: pulumi.Int(0),
IpAddress: instance.PrivateIp,
TargetId: instance.ID(),
})
if err != nil {
return err
}
return nil
}

17
main.go
View File

@ -19,7 +19,22 @@ func main() {
return err
}
_, _, err = setupEmails(ctx, ocicfg, compartment)
ns, listmonkAuthToken, err := setupListmonkStorage(ctx, ocicfg, compartment)
if err != nil {
return err
}
pemprvkey, smtpcreds, err := setupEmails(ctx, ocicfg, compartment)
if err != nil {
return err
}
subnet, err := setupNetwork(ctx, compartment)
if err != nil {
return err
}
err = setupHostMain(ctx, ocicfg, compartment, ns, subnet, listmonkAuthToken, smtpcreds, pemprvkey)
if err != nil {
return err
}

146
network.go Normal file
View File

@ -0,0 +1,146 @@
package main
import (
"strings"
"github.com/pulumi/pulumi-oci/sdk/go/oci/core"
"github.com/pulumi/pulumi-oci/sdk/go/oci/identity"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func setupNetwork(ctx *pulumi.Context, compartment *identity.Compartment) (*core.Subnet, error) {
// Create Virtual Network
vcn, err := core.NewVcn(ctx, "happydomain-vnc", &core.VcnArgs{
/*Byoipv6cidrDetails: core.VcnByoipv6cidrDetailArray{
&core.VcnByoipv6cidrDetailArgs{
Byoipv6rangeId: pulumi.String("test"),
Ipv6cidrBlock: pulumi.String("2603:c022:2:7a00::/56"),
},
},*/
CompartmentId: compartment.ID(),
CidrBlocks: pulumi.StringArray{
pulumi.String("10.0.0.0/24"),
},
DisplayName: pulumi.String("happydomain-net"),
DnsLabel: pulumi.String("thobis"),
IsIpv6enabled: pulumi.Bool(true),
IsOracleGuaAllocationEnabled: pulumi.Bool(true),
})
if err != nil {
return nil, err
}
securityList, err := core.NewSecurityList(ctx, "happydomain-security-list", &core.SecurityListArgs{
VcnId: vcn.ID(),
CompartmentId: compartment.ID(),
DisplayName: pulumi.Sprintf("%s-happydomain-sl", ctx.Stack()),
EgressSecurityRules: core.SecurityListEgressSecurityRuleArray{
core.SecurityListEgressSecurityRuleArgs{
Protocol: pulumi.String("all"),
Destination: pulumi.String("0.0.0.0/0"),
},
core.SecurityListEgressSecurityRuleArgs{
Protocol: pulumi.String("all"),
Destination: pulumi.String("::/0"),
},
},
IngressSecurityRules: core.SecurityListIngressSecurityRuleArray{
core.SecurityListIngressSecurityRuleArgs{
Protocol: pulumi.String("6"),
Source: pulumi.String("0.0.0.0/0"),
Description: pulumi.String("IPv4 SSH Port"),
TcpOptions: core.SecurityListIngressSecurityRuleTcpOptionsArgs{
Max: pulumi.Int(22),
Min: pulumi.Int(22),
},
},
core.SecurityListIngressSecurityRuleArgs{
Protocol: pulumi.String("6"),
Source: pulumi.String("0.0.0.0/0"),
Description: pulumi.String("IPv4 HTTP Port"),
TcpOptions: core.SecurityListIngressSecurityRuleTcpOptionsArgs{
Max: pulumi.Int(80),
Min: pulumi.Int(80),
},
},
core.SecurityListIngressSecurityRuleArgs{
Protocol: pulumi.String("6"),
Source: pulumi.String("0.0.0.0/0"),
Description: pulumi.String("IPv4 HTTPS Port"),
TcpOptions: core.SecurityListIngressSecurityRuleTcpOptionsArgs{
Max: pulumi.Int(443),
Min: pulumi.Int(443),
},
},
core.SecurityListIngressSecurityRuleArgs{
Protocol: pulumi.String("6"),
Source: pulumi.String("::/0"),
Description: pulumi.String("IPv6 SSH Port"),
TcpOptions: core.SecurityListIngressSecurityRuleTcpOptionsArgs{
Max: pulumi.Int(22),
Min: pulumi.Int(22),
},
},
},
})
if err != nil {
return nil, err
}
subnet, err := core.NewSubnet(ctx, "happydomain-subnet", &core.SubnetArgs{
CompartmentId: compartment.ID(),
VcnId: vcn.ID(),
CidrBlock: pulumi.String("10.0.0.0/24"),
Ipv6cidrBlocks: vcn.Ipv6cidrBlocks.ApplyT(func(blocks []string) []string {
for i := range blocks {
blocks[i] = strings.Replace(blocks[i], "/56", "/64", -1)
}
return blocks
}).(pulumi.StringArrayOutput),
SecurityListIds: pulumi.StringArray{
vcn.DefaultSecurityListId,
securityList.ID(),
},
ProhibitPublicIpOnVnic: pulumi.Bool(false),
RouteTableId: vcn.DefaultRouteTableId,
DhcpOptionsId: vcn.DefaultDhcpOptionsId,
DisplayName: pulumi.Sprintf("%s-happydomain-subnet", ctx.Stack()),
DnsLabel: pulumi.String("happysubnet"),
})
if err != nil {
return nil, err
}
internetGateway, err := core.NewInternetGateway(ctx, "happydomain-internet-gateway", &core.InternetGatewayArgs{
CompartmentId: compartment.ID(),
VcnId: vcn.ID(),
DisplayName: pulumi.Sprintf("%s-happydomain-rg", ctx.Stack()),
Enabled: pulumi.Bool(true),
})
if err != nil {
return nil, err
}
_, err = core.NewDefaultRouteTable(ctx, "happydomain-route-table", &core.DefaultRouteTableArgs{
ManageDefaultResourceId: vcn.DefaultRouteTableId,
CompartmentId: compartment.ID(),
DisplayName: pulumi.Sprintf("%s-happydomain-rt", ctx.Stack()),
RouteRules: core.DefaultRouteTableRouteRuleArray{
core.DefaultRouteTableRouteRuleArgs{
NetworkEntityId: internetGateway.ID(),
Destination: pulumi.String("0.0.0.0/0"),
DestinationType: pulumi.String("CIDR_BLOCK"),
},
core.DefaultRouteTableRouteRuleArgs{
NetworkEntityId: internetGateway.ID(),
Destination: pulumi.String("::/0"),
DestinationType: pulumi.String("CIDR_BLOCK"),
},
},
})
if err != nil {
return nil, err
}
return subnet, nil
}

135
storage.go Normal file
View File

@ -0,0 +1,135 @@
package main
import (
"fmt"
"log"
"github.com/pulumi/pulumi-oci/sdk/go/oci/identity"
"github.com/pulumi/pulumi-oci/sdk/go/oci/objectstorage"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)
const HappyListmonkBucketName = "listmonk-files"
func setupListmonkStorage(ctx *pulumi.Context, ocicfg *config.Config, compartment *identity.Compartment) (pulumi.StringOutput, *identity.CustomerSecretKey, error) {
// Retrieve current namespace
ns := compartment.CompartmentId.ApplyT(func(compartmentId string) (string, error) {
ns, err := objectstorage.GetNamespace(ctx, &objectstorage.GetNamespaceArgs{
CompartmentId: &compartmentId,
}, nil)
if err != nil {
return "", err
}
return ns.Namespace, nil
}).(pulumi.StringOutput)
// Create bucket
_, err := objectstorage.NewBucket(ctx, HappyListmonkBucketName, &objectstorage.BucketArgs{
CompartmentId: compartment.ID(),
Namespace: ns,
Name: pulumi.String(HappyListmonkBucketName),
AccessType: pulumi.String("NoPublicAccess"),
StorageTier: pulumi.String("Standard"),
})
if err != nil {
return ns, nil, err
}
// Create service account
listmonkUser, err := identity.NewUser(ctx, "listmonk-user", &identity.UserArgs{
Name: pulumi.String("listmonk"),
Description: pulumi.String("User for Listmonk"),
Email: pulumi.String("postmaster+listmonk@happydomain.fr"),
})
if err != nil {
return ns, nil, err
}
// Set user capabilities
_, err = identity.NewUserCapabilitiesManagement(ctx, "listmonk-user-caps", &identity.UserCapabilitiesManagementArgs{
UserId: listmonkUser.ID(),
CanUseApiKeys: pulumi.Bool(false),
CanUseAuthTokens: pulumi.Bool(false),
CanUseConsolePassword: pulumi.Bool(false),
CanUseCustomerSecretKeys: pulumi.Bool(true),
CanUseSmtpCredentials: pulumi.Bool(false),
})
if err != nil {
return ns, nil, err
}
// Create groups
readWriteGroup, err := identity.NewGroup(ctx, "S3ListmonkReadWriteGroup", &identity.GroupArgs{
Name: pulumi.String("s3_read_write_listmonk"),
Description: pulumi.String("Users with read and write access to Object Storage " + HappyListmonkBucketName),
})
if err != nil {
return ns, nil, err
}
// Add users to groups
_, err = identity.NewUserGroupMembership(ctx, "listmonkReadWriteGroupMembership", &identity.UserGroupMembershipArgs{
GroupId: readWriteGroup.ID(),
UserId: listmonkUser.ID(),
})
if err != nil {
return ns, nil, err
}
// Define policies for the groups
policies := []struct {
name string
description string
group pulumi.StringOutput
policy []string
}{
{
name: "readWritePolicy-listmonk",
description: "readWritePolicy to " + HappyListmonkBucketName + " bucket object storage",
group: readWriteGroup.Name,
policy: []string{
"Allow group 'Default'/'s3_read_write_listmonk' to read buckets in compartment %s",
fmt.Sprintf("Allow group 'Default'/'s3_read_write_listmonk' to manage objects in compartment %%s where all {target.bucket.name= '%s', any {request.permission= 'OBJECT_CREATE', request.permission='OBJECT_INSPECT', request.permission='OBJECT_OVERWRITE', request.permission='PAR_MANAGE'}}", HappyListmonkBucketName),
},
},
}
compartment.Name.ApplyT(func(compartmentName string) string {
compartment.CompartmentId.ApplyT(func(compartmentId string) (string, error) {
for _, p := range policies {
var statements pulumi.StringArray
for _, policy := range p.policy {
statements = append(statements, pulumi.String(fmt.Sprintf(policy, compartmentName)))
}
_, err := identity.NewPolicy(ctx, p.name, &identity.PolicyArgs{
CompartmentId: pulumi.String(compartmentId),
Name: pulumi.String(p.name),
Description: pulumi.String(p.description),
Statements: statements,
})
if err != nil {
log.Println(err.Error())
return "", err
}
}
return "", nil
})
return ""
})
listmonkAuthToken, err := identity.NewCustomerSecretKey(ctx, "listmonk-user-secret-key", &identity.CustomerSecretKeyArgs{
UserId: listmonkUser.ID(),
DisplayName: pulumi.String("Listmonk to S3"),
})
if err != nil {
return ns, nil, err
}
// Export the infos
ctx.Export("listmonk-access-key", listmonkAuthToken.ID())
ctx.Export("listmonk-secret-key", listmonkAuthToken.Key)
return ns, listmonkAuthToken, nil
}