Compare commits

..

21 Commits

Author SHA1 Message Date
Jonathan Dahan a32e0b201f explain how to run locally on macOS
1 year ago
Jonathan Dahan 9e0e117aa0 Specify latest version of zitadel and docker-duplicity
1 year ago
Jonathan Dahan 77298265db Remove obsolete version number in compose yaml
1 year ago
Jonathan Dahan 0cc84f1a76 cleanup roadmap
1 year ago
Jonathan Dahan 1efbd62689 Add ps script
1 year ago
Jonathan Dahan 0b8bc032ff Fix format of caddyfile
1 year ago
Jonathan Dahan 3775b3d5d6 add pull script
1 year ago
Jonathan Dahan 3e24913eca fix some mail sending work
1 year ago
Jonathan Dahan 2b9450ddd1 point docs to zitadel
1 year ago
Jonathan Dahan 0a3ada3328 specify stable versions of images
1 year ago
Jonathan Dahan 13bbf292a1 Add gitea
1 year ago
Jonathan Dahan 2faca11577 describe reasoning
2 years ago
Jonathan Dahan 0dc484b2f4 we have an identity provider
2 years ago
Jonathan Dahan 82c41039c2 add exec script
2 years ago
Jonathan Dahan 34d07b7dfd fix email for registration
2 years ago
Jonathan Dahan a239dd4ac5 try using .env
2 years ago
Jonathan Dahan ac50279901 Revert "escape env vars in container"
2 years ago
Jonathan Dahan aaf706bf77 escape env vars in container
2 years ago
Jonathan Dahan e44dca5a73 put env_file in the places that need it
2 years ago
Jonathan Dahan 55fe7de241 update env template
2 years ago
Jonathan Dahan 90d338b303 add run script
2 years ago

@ -1,5 +1,15 @@
# copy this to .env and it will be sourced by the appropriate services
# domain your services will be running on
DOMAIN=localhost DOMAIN=localhost
SMTP_USER=admin # admin user for auth
SMTP_HOST=localhost ADMIN_USER=
ADMIN_PASS=
# used for sending notifications and reset passwords
# only supports smtp+starttls
SMTP_ADDR=
SMTP_PORT=587 SMTP_PORT=587
SMTP_USER=
SMTP_PASS=

@ -4,58 +4,133 @@ Experiment in digital autonomy
Latest code is hosted on https://git.woodbine.nyc/micro/woodbine.nyc Latest code is hosted on https://git.woodbine.nyc/micro/woodbine.nyc
In general, everything is orchestrated by the compose files. If you are new to running your own websites, welcome!
Sometimes, you will see a -setup service in the compose file. Note that a "service" is a fuzzy name for software that is expected to be always running.
This usually runs a script that checks or generates secrets, and does initial configuration if needed.
A simple web server (`python3 -m http.server`) could be a service, as could something like Gmail.
## Goals ## Goals
We hope this is understandable by a single individual, after learning a bit about docker compose and caddy. Understandable
- a person should be able to adapt this to their community while learning the least amount of new concepts and technology
- the person who set it up should not be needed to maintain the services
Resiliant
- services should work even when other parts of the web are not accessible
Lean
- we prefer lightweight software, which usually require less long-term maintenance
## Decisions
There are many other kinds of digital autonomy, but most people are used to the web.
We hope to share our decision making here, so you can follow our thought process.
### Decisions made for you
These needs are required for anyone who wants to deploy **web-based** services.
#### Auth
We need a way for people to either register an account or sign in with an external account to use the services.
After trying authelia, zitadel, authentik, and keycloak, got the furthest with zitadel.
#### Web
To host a webpage, you need some software that listens for http requests. We chose Caddy.
If you would like to edit the webpage, either change the files in `./data/web/site/` directly, or you can connect via WebDAV and edit the file remotely via https://web.localhost.
#### Backup
If you will be helping a community, its important to have backups and restore. We have two helper services, `backup-files` and `backup-database`.
These use duplicity to backup to a backblaze instance, so you will need to setup that beforehand.
## setup #### Secrets
We have two helper services for making sure secrets exist (`check-secrets`), or generating unique secrets for other services that need them (`generate-secrets`).
---
## getting started
### setup
Make a backblaze B2 account for backups. Add the secrets to ./secrets/backup/. Make a backblaze B2 account for backups. Add the secrets to ./secrets/backup/.
Fill out env.template and make sure to pass it in the next command Fill out env.template and make sure to pass it in the next command
## running ### running
Helper scripts can be found in [the scripts directory](./scripts)
We have two scripts in the `scripts/` directory - up and down To start
./scripts/up ./scripts/up
To stop all the containers, you can ctrl+c, or To stop, you can press ctrl+c, or in another terminal run
./scripts/down ./scripts/down
To generate secrets for all services To generate secrets for all services ahead-of-time
./scripts/secrets ./scripts/generate-secrets
## port forwarding ### port forwarding
The caddy service expects to be able to bind to ports 80 and 443 The caddy service expects to be able to bind to ports 80 and 443
One simple way is to allow unprivileged users access to these low ports One simple way is to allow unprivileged users access to these low ports
echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee -a /etc/sysctl.conf If you are on linux, you can run
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
## alpha $ sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
$ echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee -a /etc/sysctl.conf
The first command will set privileges until reboot. The second will make those privileges permanent.
If you are on macOS, using podman, you will want to run those commands in the linux virtual machine
$ podman machine ssh
core@localhost:~$ echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee -a /etc/systctl.conf
core@localhost:~$ sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
---
## design
All the services are defined by docker compose files.
We provide `backup-files`, `backup-database`, `check-secrets`, and `generate-secrets` helper services.
We have configured Caddy to import all files found in /etc/caddy.d/, so if you want to add a new service, you will need to make a small `Proxyfile` to tell caddy what subdomain to forward to what port.
See [the services readme](./services/readme.md) for a guide on adding a new service.
---
## roadmap
### alpha
- [~] single sign-on (authelia)
- [ ] per-user webdav folders via authelia
- [ ] any OIDC service setup
- [~] file backup (duplicity)
- [ ] postgres backup (duplicity)
- [ ] decide on single postgres instance or multiple - [ ] decide on single postgres instance or multiple
- [ ] postgres backup (duplicity)
- [ ] single sign-on for webdav (one user per folder)
- [ ] single sign-on for one more service
- [x] identity provider (zitadel)
- [x] file backup (duplicity)
- [x] reverse proxy (caddy) - [x] reverse proxy (caddy)
- [x] personal home pages (caddy-webdav) - [x] personal home pages (caddy-webdav)
- [x] migrate from yaml to env for authelia config
- [x] setup notifications via smtp - [x] setup notifications via smtp
## beta ### beta
- [ ] file restore - [ ] file restore
- [ ] postgres restore - [ ] postgres restore
@ -65,10 +140,10 @@ One simple way is to allow unprivileged users access to these low ports
- [ ] mailing list (listmonk) - [ ] mailing list (listmonk)
- [ ] code forge (gitea or forgejo) - [ ] code forge (gitea or forgejo)
## 0.1 ### 0.1
- [ ] only expose 443, 587, 993 - [ ] only expose 443, 587, 993
- [ ] running on betabasement-woodbine.nyc - [ ] running on beta.woodbine.nyc
- [ ] audit on secrets management - [ ] audit on secrets management
- [ ] audit on mail server - [ ] audit on mail server
- [ ] audit on general architecture - [ ] audit on general architecture

@ -1,7 +1,8 @@
podman compose --env-file env.production \ podman compose --env-file ${ENV_FILE:-.env} \
--file services/secrets.yaml \ --file services/secrets.yaml \
--file services/backup.yaml \ --file services/backup.yaml \
--file services/proxy.yaml \ --file services/proxy.yaml \
--file services/auth.yaml \ --file services/auth.yaml \
--file services/web.yaml \ --file services/web.yaml \
--file services/git.yaml \
down --volumes down --volumes

@ -0,0 +1,8 @@
podman compose --env-file ${ENV_FILE:-.env} \
--file services/secrets.yaml \
--file services/backup.yaml \
--file services/proxy.yaml \
--file services/auth.yaml \
--file services/web.yaml \
--file services/git.yaml \
exec "$@"

@ -0,0 +1,8 @@
podman compose --env-file ${ENV_FILE:-.env} \
--file services/secrets.yaml \
--file services/backup.yaml \
--file services/proxy.yaml \
--file services/auth.yaml \
--file services/web.yaml \
--file services/git.yaml \
ps

@ -0,0 +1,8 @@
podman compose --env-file ${ENV_FILE:-.env} \
--file services/secrets.yaml \
--file services/backup.yaml \
--file services/proxy.yaml \
--file services/auth.yaml \
--file services/web.yaml \
--file services/git.yaml \
pull

@ -0,0 +1,8 @@
podman compose --env-file ${ENV_FILE:-.env} \
--file services/secrets.yaml \
--file services/backup.yaml \
--file services/proxy.yaml \
--file services/auth.yaml \
--file services/web.yaml \
--file services/git.yaml \
run "$@"

@ -1,7 +1,8 @@
podman compose --env-file env.production \ podman compose --env-file ${ENV_FILE:-.env} \
--file services/secrets.yaml \ --file services/secrets.yaml \
--file services/backup.yaml \ --file services/backup.yaml \
--file services/proxy.yaml \ --file services/proxy.yaml \
--file services/auth.yaml \ --file services/auth.yaml \
--file services/web.yaml \ --file services/web.yaml \
--file services/git.yaml \
up --build up --build

@ -1,5 +1,3 @@
version: "3.8"
secrets: secrets:
MASTER_KEY: MASTER_KEY:
file: ../secrets/auth/zitadel/MASTER_KEY file: ../secrets/auth/zitadel/MASTER_KEY
@ -15,7 +13,7 @@ services:
zitadel: zitadel:
restart: 'unless-stopped' restart: 'unless-stopped'
image: 'ghcr.io/zitadel/zitadel:latest' image: 'ghcr.io/zitadel/zitadel:v2.48.3'
environment: environment:
ZITADEL_DATABASE_COCKROACH_HOST: crdb ZITADEL_DATABASE_COCKROACH_HOST: crdb
ZITADEL_EXTERNALSECURE: true ZITADEL_EXTERNALSECURE: true
@ -25,11 +23,15 @@ services:
ZITADEL_FIRSTINSTANCE_ORG_NAME: basement ZITADEL_FIRSTINSTANCE_ORG_NAME: basement
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ADMIN_USER} ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ADMIN_USER}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ADMIN_PASS} ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ADMIN_PASS}
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROM: basement
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROMNAME: ${DOMAIN} ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_HOST: "${SMTP_ADDR}:${SMTP_PORT}"
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_HOST: "${SMTP_HOST}:${SMTP_PORT}"
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_USER: ${SMTP_USER} ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_USER: ${SMTP_USER}
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_PASSWORD: ${SMTP_PASS} ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_PASSWORD: ${SMTP_PASS}
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_SSL: true
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROM: basement@mail.${DOMAIN}
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROMNAME: basement
ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_REPLYTOADDRESS: basement@mail.${DOMAIN}
secrets: secrets:
- MASTER_KEY - MASTER_KEY
command: "start-from-init --masterkeyFile /run/secrets/MASTER_KEY --tlsMode external" command: "start-from-init --masterkeyFile /run/secrets/MASTER_KEY --tlsMode external"

@ -1,5 +1,3 @@
version: "3.8"
secrets: secrets:
B2_APPLICATION_KEY: B2_APPLICATION_KEY:
file: ../secrets/backup/duplicity/B2_APPLICATION_KEY file: ../secrets/backup/duplicity/B2_APPLICATION_KEY
@ -12,7 +10,7 @@ secrets:
services: services:
backup: backup:
image: tecnativa/docker-duplicity:latest image: ghcr.io/tecnativa/docker-duplicity:3.3.1
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
generate-secrets: generate-secrets:

@ -0,0 +1,48 @@
secrets:
DB_PASSWD:
file: ../secrets/git/gitea/DB_PASSWD
services:
caddy:
volumes:
- ./git/Proxyfile:/etc/caddy.d/git
backup:
volumes:
- ../data/git:/mnt/backup/src/git
gitea:
image: gitea/gitea:1.21.3-rootless
secrets: [ DB_PASSWD ]
environment:
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: "db:5432"
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD__FILE: /run/secrets/DB_PASSWD
GITEA__mailer__ENABLED: true
GITEA__mailer__FROM: gitea@mail.${DOMAIN}
GITEA__mailer__PROTOCOL: smtp+starttls
GITEA__mailer__SMTP_ADDR: ${SMTP_ADDR}
GITEA__mailer__SMTP_PORT: ${SMTP_PORT}
GITEA__mailer__USER: ${SMTP_USER}
GITEA__mailer__PASSWD: ${SMTP_PASS}
restart: unless-stopped
volumes:
- ../data/git/gitea/data:/data
ports:
- 3000:3000
db:
image: postgres:16.1-alpine
secrets: [ DB_PASSWD ]
environment:
POSTGRES_USER: gitea
POSTGRES_PASSWORD_FILE: /run/secrets/DB_PASSWD
POSTGRES_DB: gitea
restart: unless-stopped
volumes:
- db_data:/var/lib/postgresql/data
expose:
- 5432
volumes:
db_data:

@ -0,0 +1,3 @@
git.{$DOMAIN} {
reverse_proxy gitea:3000
}

@ -1,5 +1,3 @@
version: "3.8"
secrets: secrets:
SMTP_PASSWORD: SMTP_PASSWORD:
file: ../secrets/mail/SMTP_PASSWORD file: ../secrets/mail/SMTP_PASSWORD
@ -18,13 +16,12 @@ services:
- ./mail/Proxyfile:/etc/caddy.d/mail:ro - ./mail/Proxyfile:/etc/caddy.d/mail:ro
maddy: maddy:
image: foxcpp/maddy:latest image: foxcpp/maddy:0.7
secrets: [SMTP_PASSWORD] secrets: [SMTP_PASSWORD]
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
generate-secrets: generate-secrets:
condition: 'service_completed_successfully' condition: 'service_completed_successfully'
environment: environment:
- MADDY_HOSTNAME=mx.mail.${DOMAIN} - MADDY_HOSTNAME=mx.mail.${DOMAIN}
- MADDY_DOMAIN=mail.${DOMAIN} - MADDY_DOMAIN=mail.${DOMAIN}
@ -40,7 +37,7 @@ services:
- 993:993 - 993:993
roundcube: roundcube:
image: roundcube/roundcubemail:1.6.x-apache image: roundcube/roundcubemail:1.6.5-fpm-alpine
environment: environment:
ROUNDCUBEMAIL_DEFAULT_HOST: ssl://mx.mail.${DOMAIN} ROUNDCUBEMAIL_DEFAULT_HOST: ssl://mx.mail.${DOMAIN}
ROUNDCUBEMAIL_DEFAULT_PORT: 993 ROUNDCUBEMAIL_DEFAULT_PORT: 993

@ -1,5 +1,3 @@
version: "3.8"
services: services:
caddy: caddy:
image: caddy image: caddy

@ -16,9 +16,9 @@ we have a backup script that uses duplicity, this should be moved into a contain
caddy is the web server, and handles https certificates, and proxying to all the services. caddy is the web server, and handles https certificates, and proxying to all the services.
#### [Authelia](https://www.authelia.com/overview/prologue/introduction/) **WIP** #### [Zitadel](ihttps://zitadel.com/docs/self-hosting/deploy/overview)
authelia lets you have a single username and password to sign on to all your services. zitadel lets you have a single username and password to sign on to all your services.
### Optional Services ### Optional Services

@ -1,5 +1,3 @@
version: "3.8"
services: services:
generate-secrets: generate-secrets:
image: alpine/openssl image: alpine/openssl

@ -1,5 +1,3 @@
version: "3.8"
services: services:
web: web:
build: build:

@ -5,7 +5,7 @@ root * /site
@notget not method GET @notget not method GET
route @notget { route @notget {
webdav webdav
} }
file_server browse file_server browse

Loading…
Cancel
Save