diff --git a/deploy/cert.sh b/deploy/cert.sh new file mode 100755 index 0000000..8633fac --- /dev/null +++ b/deploy/cert.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +docker compose cp \ + proxy:/data/caddy/pki/authorities/local/root.crt \ + ./root.crt diff --git a/deploy/compose/caddy/Caddyfile b/deploy/compose/caddy/Caddyfile new file mode 100644 index 0000000..3d9416e --- /dev/null +++ b/deploy/compose/caddy/Caddyfile @@ -0,0 +1,7 @@ +locus.net { + reverse_proxy /self-service/* kratos:4433 + reverse_proxy /sessions/* kratos:4433 + reverse_proxy /* locus:3000 + + tls internal +} diff --git a/deploy/compose/kratos/identity.schema.json b/deploy/compose/kratos/identity.schema.json new file mode 100644 index 0000000..e7f24e2 --- /dev/null +++ b/deploy/compose/kratos/identity.schema.json @@ -0,0 +1,45 @@ +{ + "$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Person", + "type": "object", + "properties": { + "traits": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "title": "E-Mail", + "minLength": 3, + "ory.sh/kratos": { + "credentials": { + "password": { + "identifier": true + } + }, + "verification": { + "via": "email" + }, + "recovery": { + "via": "email" + } + } + }, + "name": { + "type": "object", + "properties": { + "username": { + "title": "Username", + "type": "string" + } + } + } + }, + "required": [ + "email" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/deploy/compose/kratos/kratos.yml b/deploy/compose/kratos/kratos.yml new file mode 100644 index 0000000..3fec45b --- /dev/null +++ b/deploy/compose/kratos/kratos.yml @@ -0,0 +1,104 @@ +# https://github.com/ory/kratos/blob/master/contrib/quickstart/kratos/email-password/kratos.yml + +version: v1.2.0 + +dsn: postgres://kratos:@db:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4 + +serve: + public: + base_url: https://locus.net/ + cors: + enabled: true + admin: + base_url: http://kratos:4434/ + host: 127.0.0.1 + +selfservice: + default_browser_return_url: https://locus.net/ + allowed_return_urls: + - https://locus.net + + methods: + password: + enabled: true + totp: + config: + issuer: Kratos + enabled: true + lookup_secret: + enabled: true + link: + enabled: true + code: + enabled: true + + flows: + error: + ui_url: https://locus.net/error + + settings: + ui_url: https://locus.net/settings + privileged_session_max_age: 15m + required_aal: highest_available + + recovery: + enabled: true + ui_url: https://locus.net/auth/recovery + use: code + + verification: + enabled: true + ui_url: https://locus.net/auth/verification + use: code + after: + default_browser_return_url: https://locus.net/ + + logout: + after: + default_browser_return_url: https://locus.net/ + + login: + ui_url: https://locus.net/auth/login + lifespan: 10m + + registration: + lifespan: 10m + ui_url: https://locus.net/auth/register + after: + password: + hooks: + - hook: session + - hook: show_verification_ui + +log: + level: debug + format: text + leak_sensitive_values: true + +secrets: + cookie: + - + cipher: + - + +ciphers: + algorithm: xchacha20-poly1305 + +hashers: + algorithm: bcrypt + bcrypt: + cost: 8 + +identity: + default_schema_id: default + schemas: + - id: default + url: file:///etc/config/kratos/identity.schema.json + +courier: + smtp: + connection_uri: + from_address: locus@somedomain.com + +feature_flags: + use_continue_with_transitions: true diff --git a/deploy/compose/postgres/prepare_coldmfa.sh b/deploy/compose/postgres/prepare_coldmfa.sh new file mode 100644 index 0000000..f3d3be5 --- /dev/null +++ b/deploy/compose/postgres/prepare_coldmfa.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE DATABASE coldmfa; + CREATE USER coldmfa WITH ENCRYPTED PASSWORD ''; + GRANT ALL PRIVILEGES ON DATABASE coldmfa TO coldmfa; + \c coldmfa "$POSTGRES_USER" + GRANT ALL PRIVILEGES ON SCHEMA public TO coldmfa; +EOSQL diff --git a/deploy/compose/postgres/prepare_kratos.sh b/deploy/compose/postgres/prepare_kratos.sh new file mode 100644 index 0000000..ae989ea --- /dev/null +++ b/deploy/compose/postgres/prepare_kratos.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE DATABASE kratos; + CREATE USER kratos WITH ENCRYPTED PASSWORD ''; + GRANT ALL PRIVILEGES ON DATABASE kratos TO kratos; + \c kratos "$POSTGRES_USER" + GRANT ALL PRIVILEGES ON SCHEMA public TO kratos; +EOSQL diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml new file mode 100644 index 0000000..86af7e3 --- /dev/null +++ b/deploy/docker-compose.yaml @@ -0,0 +1,86 @@ +services: + db: + image: postgres:17-alpine + restart: unless-stopped + shm_size: 128mb + environment: + POSTGRES_USER: admin + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password + POSTGRES_DB: main + POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 + POSTGRES_HOST_AUTH_METHOD: scram-sha-256 + ports: + - "5432" + networks: + - default + volumes: + - postgres_data:/var/lib/postgresql/data + - ./compose/postgres:/docker-entrypoint-initdb.d/ + secrets: + - postgres_password + kratos-migrate: + depends_on: + - db + image: oryd/kratos:v1.2.0 + command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes + restart: on-failure + environment: + - LOG_LEVEL=warning + volumes: + - ./compose/kratos/:/etc/config/kratos/ + networks: + - default + kratos: + depends_on: + - kratos-migrate + image: oryd/kratos:v1.2.0 + command: serve -c /etc/config/kratos/kratos.yml --sqa-opt-out --watch-courier + restart: unless-stopped + ports: + - '4433' # public + - '4434' # admin + environment: + - LOG_LEVEL=info + volumes: + - ./compose/kratos/:/etc/config/kratos/ + networks: + - default + locus: + depends_on: + - db + - kratos + image: ghcr.io/ephyrasoftware/locus:v0.0.8 + ports: + - '3000' + environment: + - ORY_PUBLIC_URL=http://kratos:4433 + - ORY_PUBLIC_BROWSER_URL=https://locus.net/ + - DATABASE_URL_FILE=/run/secrets/locus_database_url + secrets: + - locus_database_url + proxy: + depends_on: + - kratos + - locus + image: caddy:2-alpine + ports: + - '80:80' + - '443:443' + volumes: + - caddy_data:/data + - ./compose/caddy/Caddyfile:/etc/caddy/Caddyfile + +networks: + default: + +volumes: + postgres_data: + driver: local + caddy_data: + driver: local + +secrets: + postgres_password: + file: postgres-password.txt + locus_database_url: + file: locus-database-url.txt diff --git a/deploy/locus-database-url.txt b/deploy/locus-database-url.txt new file mode 100644 index 0000000..e81bf0a --- /dev/null +++ b/deploy/locus-database-url.txt @@ -0,0 +1 @@ +postgres://coldmfa:@db:5432/coldmfa?sslmode=disable \ No newline at end of file diff --git a/deploy/postgres-password.txt b/deploy/postgres-password.txt new file mode 100644 index 0000000..3272e83 --- /dev/null +++ b/deploy/postgres-password.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/flake.nix b/flake.nix index b39f44b..1ce379a 100644 --- a/flake.nix +++ b/flake.nix @@ -13,7 +13,7 @@ perSystem = { config, self', inputs', pkgs, system, ... }: { formatter = pkgs.nixpkgs-fmt; devShells.default = pkgs.mkShell { - packages = with pkgs; [ ory nodejs_22 ]; + packages = with pkgs; [ ory nodejs_22 caddy ]; }; }; flake = { };