Working nassella deployment.
This commit is contained in:
38
Makefile
38
Makefile
@@ -11,6 +11,8 @@ $(wildcard all-apps/nextcloud/*) \
|
|||||||
$(wildcard all-apps/wg-easy/*) \
|
$(wildcard all-apps/wg-easy/*) \
|
||||||
$(wildcard all-apps/ghost/*) \
|
$(wildcard all-apps/ghost/*) \
|
||||||
$(wildcard all-apps/nassella/*) \
|
$(wildcard all-apps/nassella/*) \
|
||||||
|
all-apps/nassella/authelia-config/configuration.yml \
|
||||||
|
all-apps/nassella/lldap-config/lldap_config.toml \
|
||||||
$(wildcard all-apps/dozzle/*)
|
$(wildcard all-apps/dozzle/*)
|
||||||
|
|
||||||
rm -Rf app/
|
rm -Rf app/
|
||||||
@@ -46,6 +48,32 @@ all-apps/nextcloud/redis_password: $(apps_config)
|
|||||||
all-apps/nextcloud/nextcloud.env: $(apps_config) all-apps/nextcloud/nextcloud.env.tmpl make-nextcloud-env.sh
|
all-apps/nextcloud/nextcloud.env: $(apps_config) all-apps/nextcloud/nextcloud.env.tmpl make-nextcloud-env.sh
|
||||||
./make-nextcloud-env.sh $(apps_config)
|
./make-nextcloud-env.sh $(apps_config)
|
||||||
|
|
||||||
|
# Nassella
|
||||||
|
all-apps/nassella/postgres_db: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_POSTGRES_DB" > $@'
|
||||||
|
all-apps/nassella/postgres_user: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_POSTGRES_USER" > $@'
|
||||||
|
all-apps/nassella/postgres_password: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_POSTGRES_PASSWORD" > $@'
|
||||||
|
all-apps/nassella/authelia_postgres_db: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_AUTHELIA_POSTGRES_DB" > $@'
|
||||||
|
all-apps/nassella/authelia_postgres_user: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_AUTHELIA_POSTGRES_USER" > $@'
|
||||||
|
all-apps/nassella/authelia_postgres_password: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_AUTHELIA_POSTGRES_PASSWORD" > $@'
|
||||||
|
all-apps/nassella/lldap_postgres_db: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_LLDAP_POSTGRES_DB" > $@'
|
||||||
|
all-apps/nassella/lldap_postgres_user: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_LLDAP_POSTGRES_USER" > $@'
|
||||||
|
all-apps/nassella/lldap_postgres_password: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_LLDAP_POSTGRES_PASSWORD" > $@'
|
||||||
|
all-apps/nassella/lldap_admin_password: $(apps_config)
|
||||||
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$NASSELLA_LLDAP_ADMIN_PASSWORD" > $@'
|
||||||
|
all-apps/nassella/authelia-config/configuration.yml: $(apps_config) all-apps/nassella/authelia-config/configuration.yml.tmpl make-nassella-authelia-config.sh
|
||||||
|
./make-nassella-authelia-config.sh $(apps_config)
|
||||||
|
all-apps/nassella/lldap-config/lldap_config.toml: $(apps_config) all-apps/nassella/lldap-config/lldap_config.toml.tmpl make-nassella-lldap-config.sh
|
||||||
|
./make-nassella-lldap-config.sh $(apps_config)
|
||||||
|
|
||||||
# Ghost
|
# Ghost
|
||||||
all-apps/ghost/.compose-env: $(apps_config) all-apps/ghost/.compose.env.tmpl make-ghost-env.sh
|
all-apps/ghost/.compose-env: $(apps_config) all-apps/ghost/.compose.env.tmpl make-ghost-env.sh
|
||||||
./make-ghost-env.sh $(apps_config)
|
./make-ghost-env.sh $(apps_config)
|
||||||
@@ -68,7 +96,15 @@ all-apps/nextcloud/nextcloud.env \
|
|||||||
all-apps/nassella/postgres_db \
|
all-apps/nassella/postgres_db \
|
||||||
all-apps/nassella/postgres_user \
|
all-apps/nassella/postgres_user \
|
||||||
all-apps/nassella/postgres_password \
|
all-apps/nassella/postgres_password \
|
||||||
|
all-apps/nassella/lldap_postgres_db \
|
||||||
|
all-apps/nassella/lldap_postgres_user \
|
||||||
|
all-apps/nassella/lldap_postgres_password \
|
||||||
|
all-apps/nassella/authelia_postgres_db \
|
||||||
|
all-apps/nassella/authelia_postgres_user \
|
||||||
|
all-apps/nassella/authelia_postgres_password \
|
||||||
all-apps/nassella/nassella.env \
|
all-apps/nassella/nassella.env \
|
||||||
|
all-apps/nassella/authelia-config/configuration.yml \
|
||||||
|
all-apps/nassella/lldap-config/lldap_config.toml \
|
||||||
all-apps/ghost/.compose-env \
|
all-apps/ghost/.compose-env \
|
||||||
restic-env \
|
restic-env \
|
||||||
restic-password \
|
restic-password \
|
||||||
@@ -109,7 +145,7 @@ restic-snapshots: $(apps_config) restic-password
|
|||||||
archive:
|
archive:
|
||||||
tar -cf nassella-latest.tar all-apps cl.yaml init-restic.sh main.tf make-caddyfile.sh Makefile \
|
tar -cf nassella-latest.tar all-apps cl.yaml init-restic.sh main.tf make-caddyfile.sh Makefile \
|
||||||
make-generated.sh make-nextcloud-env.sh make-ghost-env.sh make-restic-generated.sh make-restic-password.sh restic-snapshots.sh copy-apps.sh \
|
make-generated.sh make-nextcloud-env.sh make-ghost-env.sh make-restic-generated.sh make-restic-password.sh restic-snapshots.sh copy-apps.sh \
|
||||||
.terraform.lock.hcl
|
make-nassella-authelia-config.sh make-nassella-lldap-config.sh .terraform.lock.hcl
|
||||||
cp nassella-latest.tar src/
|
cp nassella-latest.tar src/
|
||||||
|
|
||||||
## to help me remember the command to run to test the config locally
|
## to help me remember the command to run to test the config locally
|
||||||
|
|||||||
119
all-apps/nassella/authelia-config/configuration.yml.tmpl
Normal file
119
all-apps/nassella/authelia-config/configuration.yml.tmpl
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
###############################################################
|
||||||
|
# Authelia configuration #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: 'tcp://:9091/authelia'
|
||||||
|
endpoints:
|
||||||
|
authz:
|
||||||
|
forward-auth:
|
||||||
|
implementation: 'ForwardAuth'
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: 'debug'
|
||||||
|
|
||||||
|
totp:
|
||||||
|
issuer: 'authelia.com'
|
||||||
|
|
||||||
|
identity_validation:
|
||||||
|
reset_password:
|
||||||
|
jwt_secret: '$NASSELLA_AUTHELIA_JWT_SECRET'
|
||||||
|
|
||||||
|
# lldap service account user should instead
|
||||||
|
# use an account with lldap_password_manager group
|
||||||
|
# since that can't be used to change an admin password
|
||||||
|
authentication_backend:
|
||||||
|
ldap:
|
||||||
|
address: 'ldap://nassella_lldap:3890'
|
||||||
|
implementation: 'lldap'
|
||||||
|
timeout: '5s'
|
||||||
|
pooling:
|
||||||
|
enable: false
|
||||||
|
count: 5
|
||||||
|
retries: 2
|
||||||
|
timeout: '10 seconds'
|
||||||
|
base_dn: 'DC=nassella,DC=org'
|
||||||
|
# additional_users_dn: 'OU=users'
|
||||||
|
# additional_groups_dn: 'OU=groups'
|
||||||
|
# group_search_mode: 'filter'
|
||||||
|
# permit_referrals: false
|
||||||
|
permit_unauthenticated_bind: false
|
||||||
|
permit_feature_detection_failure: false
|
||||||
|
user: 'uid=admin,ou=people,dc=nassella,dc=org'
|
||||||
|
password: '$NASSELLA_LLDAP_ADMIN_PASSWORD'
|
||||||
|
# attributes:
|
||||||
|
# distinguished_name: 'distinguishedName'
|
||||||
|
# username: 'uid'
|
||||||
|
# display_name: 'displayName'
|
||||||
|
# family_name: 'sn'
|
||||||
|
# given_name: 'givenName'
|
||||||
|
# middle_name: 'middleName'
|
||||||
|
# nickname: ''
|
||||||
|
# gender: ''
|
||||||
|
# birthdate: ''
|
||||||
|
# website: 'wWWHomePage'
|
||||||
|
# profile: ''
|
||||||
|
# picture: ''
|
||||||
|
# zoneinfo: ''
|
||||||
|
# locale: ''
|
||||||
|
# phone_number: 'telephoneNumber'
|
||||||
|
# phone_extension: ''
|
||||||
|
# street_address: 'streetAddress'
|
||||||
|
# locality: 'l'
|
||||||
|
# region: 'st'
|
||||||
|
# postal_code: 'postalCode'
|
||||||
|
# country: 'c'
|
||||||
|
# mail: 'mail'
|
||||||
|
# member_of: 'memberOf'
|
||||||
|
# group_name: 'cn'
|
||||||
|
# extra:
|
||||||
|
# extra_example:
|
||||||
|
# name: ''
|
||||||
|
# multi_valued: false
|
||||||
|
# value_type: 'string'
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: 'deny'
|
||||||
|
rules:
|
||||||
|
# - domain: 'public.x.localhost'
|
||||||
|
# policy: 'bypass'
|
||||||
|
# - domain: 'app.nassella.org'
|
||||||
|
# policy: 'one_factor'
|
||||||
|
- domain: '$NASSELLA_FULL_DOMAIN'
|
||||||
|
policy: 'two_factor'
|
||||||
|
|
||||||
|
session:
|
||||||
|
secret: 'insecure_session_secret'
|
||||||
|
|
||||||
|
cookies:
|
||||||
|
- name: 'authelia_session'
|
||||||
|
domain: '$NASSELLA_FULL_DOMAIN' # Should match whatever your root protected domain is
|
||||||
|
authelia_url: 'https://$NASSELLA_FULL_DOMAIN/authelia'
|
||||||
|
expiration: '1 hour' # 1 hour
|
||||||
|
inactivity: '5 minutes' # 5 minutes
|
||||||
|
default_redirection_url: 'https://$NASSELLA_FULL_DOMAIN/dashboard'
|
||||||
|
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: '2 minutes'
|
||||||
|
ban_time: '5 minutes'
|
||||||
|
|
||||||
|
storage:
|
||||||
|
encryption_key: '$NASSELLA_AUTHELIA_KEY_SEED'
|
||||||
|
postgres:
|
||||||
|
address: 'tcp://nassella_authelia_db:5432'
|
||||||
|
servers: []
|
||||||
|
database: 'authelia'
|
||||||
|
schema: 'public'
|
||||||
|
username: 'authelia'
|
||||||
|
password: '$NASSELLA_AUTHELIA_POSTGRES_PASSWORD'
|
||||||
|
timeout: '5s'
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
smtp:
|
||||||
|
address: 'submission://$SMTP_HOST:$SMTP_PORT'
|
||||||
|
username: '$SMTP_AUTH_USER'
|
||||||
|
password: '$SMTP_AUTH_PASSWORD'
|
||||||
|
sender: '$SMTP_FROM'
|
||||||
|
...
|
||||||
1
all-apps/nassella/authelia_postgres_db
Normal file
1
all-apps/nassella/authelia_postgres_db
Normal file
@@ -0,0 +1 @@
|
|||||||
|
authelia
|
||||||
0
all-apps/nassella/authelia_postgres_password
Normal file
0
all-apps/nassella/authelia_postgres_password
Normal file
1
all-apps/nassella/authelia_postgres_user
Normal file
1
all-apps/nassella/authelia_postgres_user
Normal file
@@ -0,0 +1 @@
|
|||||||
|
authelia
|
||||||
@@ -7,8 +7,100 @@ secrets:
|
|||||||
file: ./nassella/postgres_password
|
file: ./nassella/postgres_password
|
||||||
nassella_postgres_user:
|
nassella_postgres_user:
|
||||||
file: ./nassella/postgres_user
|
file: ./nassella/postgres_user
|
||||||
|
nassella_lldap_postgres_db:
|
||||||
|
file: ./nassella/lldap_postgres_db
|
||||||
|
nassella_lldap_postgres_password:
|
||||||
|
file: ./nassella/lldap_postgres_password
|
||||||
|
nassella_lldap_postgres_user:
|
||||||
|
file: ./nassella/lldap_postgres_user
|
||||||
|
nassella_authelia_postgres_db:
|
||||||
|
file: ./nassella/authelia_postgres_db
|
||||||
|
nassella_authelia_postgres_password:
|
||||||
|
file: ./nassella/authelia_postgres_password
|
||||||
|
nassella_authelia_postgres_user:
|
||||||
|
file: ./nassella/authelia_postgres_user
|
||||||
|
nassella_lldap_admin_password:
|
||||||
|
file: ./nassella/lldap_admin_password
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
nassella_lldap_db:
|
||||||
|
image: postgres:17.6-trixie
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB_FILE=/run/secrets/nassella_lldap_postgres_db
|
||||||
|
- POSTGRES_USER_FILE=/run/secrets/nassella_lldap_postgres_user
|
||||||
|
- POSTGRES_PASSWORD_FILE=/run/secrets/nassella_lldap_postgres_password
|
||||||
|
shm_size: 128mb
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- /nassella/nassella/lldap-var-lib-postgresql-data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- nassella_internal
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -d `cat $$POSTGRES_DB_FILE` -U `cat $$POSTGRES_USER_FILE`"]
|
||||||
|
start_period: 15s
|
||||||
|
interval: 30s
|
||||||
|
retries: 3
|
||||||
|
timeout: 5s
|
||||||
|
secrets:
|
||||||
|
- nassella_lldap_postgres_db
|
||||||
|
- nassella_lldap_postgres_password
|
||||||
|
- nassella_lldap_postgres_user
|
||||||
|
|
||||||
|
nassella_lldap:
|
||||||
|
image: lldap/lldap:stable
|
||||||
|
volumes:
|
||||||
|
- ./nassella/lldap-config/:/data
|
||||||
|
networks:
|
||||||
|
- lb
|
||||||
|
- nassella_internal
|
||||||
|
depends_on:
|
||||||
|
nassella_lldap_db:
|
||||||
|
condition: service_healthy
|
||||||
|
secrets:
|
||||||
|
- nassella_lldap_postgres_db
|
||||||
|
- nassella_lldap_postgres_password
|
||||||
|
- nassella_lldap_postgres_user
|
||||||
|
|
||||||
|
nassella_authelia_db:
|
||||||
|
image: postgres:17.6-trixie
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB_FILE=/run/secrets/nassella_authelia_postgres_db
|
||||||
|
- POSTGRES_USER_FILE=/run/secrets/nassella_authelia_postgres_user
|
||||||
|
- POSTGRES_PASSWORD_FILE=/run/secrets/nassella_authelia_postgres_password
|
||||||
|
shm_size: 128mb
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- /nassella/nassella/authelia-var-lib-postgresql-data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- nassella_internal
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -d `cat $$POSTGRES_DB_FILE` -U `cat $$POSTGRES_USER_FILE`"]
|
||||||
|
start_period: 15s
|
||||||
|
interval: 30s
|
||||||
|
retries: 3
|
||||||
|
timeout: 5s
|
||||||
|
secrets:
|
||||||
|
- nassella_authelia_postgres_db
|
||||||
|
- nassella_authelia_postgres_password
|
||||||
|
- nassella_authelia_postgres_user
|
||||||
|
|
||||||
|
nassella_authelia:
|
||||||
|
image: 'authelia/authelia'
|
||||||
|
volumes:
|
||||||
|
- ./nassella/authelia-config/:/config
|
||||||
|
networks:
|
||||||
|
- lb
|
||||||
|
- nassella_internal
|
||||||
|
depends_on:
|
||||||
|
nassella_lldap:
|
||||||
|
condition: service_healthy
|
||||||
|
nassella_authelia_db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: 'unless-stopped'
|
||||||
|
healthcheck:
|
||||||
|
## In production the healthcheck section should be commented.
|
||||||
|
disable: true
|
||||||
|
|
||||||
nassella_db:
|
nassella_db:
|
||||||
image: postgres:17.6-trixie
|
image: postgres:17.6-trixie
|
||||||
env_file:
|
env_file:
|
||||||
@@ -40,6 +132,7 @@ services:
|
|||||||
- nassella_postgres_db
|
- nassella_postgres_db
|
||||||
- nassella_postgres_password
|
- nassella_postgres_password
|
||||||
- nassella_postgres_user
|
- nassella_postgres_user
|
||||||
|
- nassella_lldap_admin_password
|
||||||
networks:
|
networks:
|
||||||
- lb
|
- lb
|
||||||
- nassella_internal
|
- nassella_internal
|
||||||
|
|||||||
161
all-apps/nassella/lldap-config/lldap_config.toml.tmpl
Normal file
161
all-apps/nassella/lldap-config/lldap_config.toml.tmpl
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
## Default configuration for Docker.
|
||||||
|
## All the values can be overridden through environment variables, prefixed
|
||||||
|
## with "LLDAP_". For instance, "ldap_port" can be overridden with the
|
||||||
|
## "LLDAP_LDAP_PORT" variable.
|
||||||
|
|
||||||
|
## Tune the logging to be more verbose by setting this to be true.
|
||||||
|
## You can set it with the LLDAP_VERBOSE environment variable.
|
||||||
|
# verbose=false
|
||||||
|
|
||||||
|
## The host address that the LDAP server will be bound to.
|
||||||
|
## To enable IPv6 support, simply switch "ldap_host" to "::":
|
||||||
|
## To only allow connections from localhost (if you want to restrict to local self-hosted services),
|
||||||
|
## change it to "127.0.0.1" ("::1" in case of IPv6).
|
||||||
|
## If LLDAP server is running in docker, set it to "0.0.0.0" ("::" for IPv6) to allow connections
|
||||||
|
## originating from outside the container.
|
||||||
|
ldap_host = "0.0.0.0"
|
||||||
|
|
||||||
|
## The port on which to have the LDAP server.
|
||||||
|
#ldap_port = 3890
|
||||||
|
|
||||||
|
## The host address that the HTTP server will be bound to.
|
||||||
|
## To enable IPv6 support, simply switch "http_host" to "::".
|
||||||
|
## To only allow connections from localhost (if you want to restrict to local self-hosted services),
|
||||||
|
## change it to "127.0.0.1" ("::1" in case of IPv6).
|
||||||
|
## If LLDAP server is running in docker, set it to "0.0.0.0" ("::" for IPv6) to allow connections
|
||||||
|
## originating from outside the container.
|
||||||
|
http_host = "0.0.0.0"
|
||||||
|
|
||||||
|
## The port on which to have the HTTP server, for user login and
|
||||||
|
## administration.
|
||||||
|
#http_port = 17170
|
||||||
|
|
||||||
|
## The public URL of the server, for password reset links.
|
||||||
|
http_url = "https://$NASSELLA_FULL_LLDAP_DOMAIN"
|
||||||
|
|
||||||
|
## The path to the front-end assets (relative to the working directory).
|
||||||
|
#assets_path = "./app"
|
||||||
|
|
||||||
|
## Random secret for JWT signature.
|
||||||
|
## This secret should be random, and should be shared with application
|
||||||
|
## servers that need to consume the JWTs.
|
||||||
|
## Changing this secret will invalidate all user sessions and require
|
||||||
|
## them to re-login.
|
||||||
|
## You should probably set it through the LLDAP_JWT_SECRET environment
|
||||||
|
## variable from a secret ".env" file.
|
||||||
|
## This can also be set from a file's contents by specifying the file path
|
||||||
|
## in the LLDAP_JWT_SECRET_FILE environment variable
|
||||||
|
## You can generate it with (on linux):
|
||||||
|
## LC_ALL=C tr -dc 'A-Za-z0-9!#%&'\''()*+,-./:;<=>?@[\]^_{|}~' </dev/urandom | head -c 32; echo ''
|
||||||
|
jwt_secret = "$NASSELLA_LLDAP_JWT_SECRET"
|
||||||
|
|
||||||
|
## Base DN for LDAP.
|
||||||
|
## This is usually your domain name, and is used as a
|
||||||
|
## namespace for your users. The choice is arbitrary, but will be needed
|
||||||
|
## to configure the LDAP integration with other services.
|
||||||
|
## The sample value is for "example.com", but you can extend it with as
|
||||||
|
## many "dc" as you want, and you don't actually need to own the domain
|
||||||
|
## name.
|
||||||
|
ldap_base_dn = "dc=nassella,dc=org"
|
||||||
|
|
||||||
|
## Admin username.
|
||||||
|
## For the LDAP interface, a value of "admin" here will create the LDAP
|
||||||
|
## user "cn=admin,ou=people,dc=example,dc=com" (with the base DN above).
|
||||||
|
## For the administration interface, this is the username.
|
||||||
|
#ldap_user_dn = "admin"
|
||||||
|
|
||||||
|
## Admin email.
|
||||||
|
## Email for the admin account. It is only used when initially creating
|
||||||
|
## the admin user, and can safely be omitted.
|
||||||
|
#ldap_user_email = "admin@example.com"
|
||||||
|
|
||||||
|
## Admin password.
|
||||||
|
## Password for the admin account, both for the LDAP bind and for the
|
||||||
|
## administration interface. It is only used when initially creating
|
||||||
|
## the admin user.
|
||||||
|
## It should be minimum 8 characters long.
|
||||||
|
## You can set it with the LLDAP_LDAP_USER_PASS environment variable.
|
||||||
|
## This can also be set from a file's contents by specifying the file path
|
||||||
|
## in the LLDAP_LDAP_USER_PASS_FILE environment variable
|
||||||
|
## Note: you can create another admin user for user administration, this
|
||||||
|
## is just the default one.
|
||||||
|
ldap_user_pass = "$NASSELLA_LLDAP_ADMIN_PASSWORD"
|
||||||
|
|
||||||
|
## Force reset of the admin password.
|
||||||
|
## Break glass in case of emergency: if you lost the admin password, you
|
||||||
|
## can set this to true to force a reset of the admin password to the value
|
||||||
|
## of ldap_user_pass above.
|
||||||
|
## Alternatively, you can set it to "always" to reset every time the server starts.
|
||||||
|
# force_ldap_user_pass_reset = false
|
||||||
|
|
||||||
|
## Database URL.
|
||||||
|
## This encodes the type of database (SQlite, MySQL, or PostgreSQL)
|
||||||
|
## , the path, the user, password, and sometimes the mode (when
|
||||||
|
## relevant).
|
||||||
|
## Note: SQlite should come with "?mode=rwc" to create the DB
|
||||||
|
## if not present.
|
||||||
|
## Example URLs:
|
||||||
|
## - "postgres://postgres-user:password@postgres-server/my-database"
|
||||||
|
## - "mysql://mysql-user:password@mysql-server/my-database"
|
||||||
|
##
|
||||||
|
## This can be overridden with the LLDAP_DATABASE_URL env variable.
|
||||||
|
database_url = "postgres://lldap:$NASSELLA_LLDAP_POSTGRES_PASSWORD@nassella_lldap_db/lldap"
|
||||||
|
|
||||||
|
## Private key file.
|
||||||
|
## Not recommended, use key_seed instead.
|
||||||
|
## Contains the secret private key used to store the passwords safely.
|
||||||
|
## Note that even with a database dump and the private key, an attacker
|
||||||
|
## would still have to perform an (expensive) brute force attack to find
|
||||||
|
## each password.
|
||||||
|
## Randomly generated on first run if it doesn't exist.
|
||||||
|
## Env variable: LLDAP_KEY_FILE
|
||||||
|
#key_file = "/data/private_key"
|
||||||
|
|
||||||
|
## Seed to generate the server private key, see key_file above.
|
||||||
|
## This can be any random string, the recommendation is that it's at least 12
|
||||||
|
## characters long.
|
||||||
|
## Env variable: LLDAP_KEY_SEED
|
||||||
|
key_seed = "$NASSELLA_LLDAP_KEY_SEED"
|
||||||
|
|
||||||
|
## Ignored attributes.
|
||||||
|
## Some services will request attributes that are not present in LLDAP. When it
|
||||||
|
## is the case, LLDAP will warn about the attribute being unknown. If you want
|
||||||
|
## to ignore the attribute and the service works without, you can add it to this
|
||||||
|
## list to silence the warning.
|
||||||
|
#ignored_user_attributes = [ "sAMAccountName" ]
|
||||||
|
#ignored_group_attributes = [ "mail", "userPrincipalName" ]
|
||||||
|
|
||||||
|
## Options to configure SMTP parameters, to send password reset emails.
|
||||||
|
## To set these options from environment variables, use the following format
|
||||||
|
## (example with "password"): LLDAP_SMTP_OPTIONS__PASSWORD
|
||||||
|
[smtp_options]
|
||||||
|
## Whether to enabled password reset via email, from LLDAP.
|
||||||
|
enable_password_reset=true
|
||||||
|
## The SMTP server.
|
||||||
|
server="$SMTP_HOST"
|
||||||
|
## The SMTP port.
|
||||||
|
port=$SMTP_PORT
|
||||||
|
## How the connection is encrypted, either "NONE" (no encryption), "TLS" or "STARTTLS".
|
||||||
|
smtp_encryption = "TLS"
|
||||||
|
## The SMTP user, usually your email address.
|
||||||
|
user="$SMTP_AUTH_USER"
|
||||||
|
## The SMTP password.
|
||||||
|
password="$SMTP_AUTH_PASSSWORD"
|
||||||
|
## The header field, optional: how the sender appears in the email. The first
|
||||||
|
## is a free-form name, followed by an email between <>.
|
||||||
|
from="$SMTP_FROM"
|
||||||
|
## Same for reply-to, optional.
|
||||||
|
#reply_to="Do not reply <noreply@localhost>"
|
||||||
|
|
||||||
|
## Options to configure LDAPS.
|
||||||
|
## To set these options from environment variables, use the following format
|
||||||
|
## (example with "port"): LLDAP_LDAPS_OPTIONS__PORT
|
||||||
|
[ldaps_options]
|
||||||
|
## Whether to enable LDAPS.
|
||||||
|
#enabled=true
|
||||||
|
## Port on which to listen.
|
||||||
|
#port=6360
|
||||||
|
## Certificate file.
|
||||||
|
#cert_file="/data/cert.pem"
|
||||||
|
## Certificate key file.
|
||||||
|
#key_file="/data/key.pem"
|
||||||
0
all-apps/nassella/lldap_admin_password
Normal file
0
all-apps/nassella/lldap_admin_password
Normal file
1
all-apps/nassella/lldap_postgres_db
Normal file
1
all-apps/nassella/lldap_postgres_db
Normal file
@@ -0,0 +1 @@
|
|||||||
|
lldap
|
||||||
0
all-apps/nassella/lldap_postgres_password
Normal file
0
all-apps/nassella/lldap_postgres_password
Normal file
1
all-apps/nassella/lldap_postgres_user
Normal file
1
all-apps/nassella/lldap_postgres_user
Normal file
@@ -0,0 +1 @@
|
|||||||
|
lldap
|
||||||
4
main.tf
4
main.tf
@@ -104,7 +104,7 @@ resource "digitalocean_reserved_ip" "machine" {
|
|||||||
|
|
||||||
resource "cloudflare_dns_record" "root" {
|
resource "cloudflare_dns_record" "root" {
|
||||||
zone_id = var.cloudflare_zone_id
|
zone_id = var.cloudflare_zone_id
|
||||||
name = "_nassella-instance"
|
name = "nassella-instance"
|
||||||
content = digitalocean_reserved_ip.machine.ip_address
|
content = digitalocean_reserved_ip.machine.ip_address
|
||||||
type = "A"
|
type = "A"
|
||||||
proxied = false
|
proxied = false
|
||||||
@@ -115,7 +115,7 @@ resource "cloudflare_dns_record" "subdomains" {
|
|||||||
for_each = toset(var.subdomains)
|
for_each = toset(var.subdomains)
|
||||||
zone_id = var.cloudflare_zone_id
|
zone_id = var.cloudflare_zone_id
|
||||||
name = each.key
|
name = each.key
|
||||||
content = "_nassella-instance.${var.domain}"
|
content = "nassella-instance.${var.domain}"
|
||||||
type = "CNAME"
|
type = "CNAME"
|
||||||
proxied = false
|
proxied = false
|
||||||
ttl = 300
|
ttl = 300
|
||||||
|
|||||||
@@ -26,7 +26,26 @@ declare -A bodys
|
|||||||
bodys["nextcloud"]=" reverse_proxy http://nextcloud:80"
|
bodys["nextcloud"]=" reverse_proxy http://nextcloud:80"
|
||||||
bodys["wg-easy"]=" reverse_proxy http://wg-easy:80"
|
bodys["wg-easy"]=" reverse_proxy http://wg-easy:80"
|
||||||
bodys["ghost"]=" reverse_proxy http://ghost:2368"
|
bodys["ghost"]=" reverse_proxy http://ghost:2368"
|
||||||
bodys["nassella"]=" reverse_proxy http://nassella:8080"
|
bodys["nassella"]=$(cat <<EOF
|
||||||
|
route {
|
||||||
|
@authelia path /authelia /authelia/*
|
||||||
|
handle @authelia {
|
||||||
|
reverse_proxy nassella_authelia:9091
|
||||||
|
}
|
||||||
|
|
||||||
|
handle /unsecured/* {
|
||||||
|
reverse_proxy http://nassella:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_auth nassella_authelia:9091 {
|
||||||
|
uri /api/authz/forward-auth
|
||||||
|
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_proxy http://nassella:8080
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
bodys["dozzle"]=$(cat <<EOF
|
bodys["dozzle"]=$(cat <<EOF
|
||||||
basic_auth {
|
basic_auth {
|
||||||
$HOST_ADMIN_USER $host_admin_password_encoded
|
$HOST_ADMIN_USER $host_admin_password_encoded
|
||||||
@@ -48,7 +67,16 @@ for config_string in ${APP_CONFIGS[@]}; do
|
|||||||
echo "$fulldomain {"
|
echo "$fulldomain {"
|
||||||
echo $body
|
echo $body
|
||||||
echo "}"
|
echo "}"
|
||||||
|
|
||||||
|
# this is a hack specifically for nassella
|
||||||
|
# because lldap should be on a separate domain
|
||||||
|
# for security but this was not designed for one app
|
||||||
|
# to map to multiple caddy blocks
|
||||||
|
# currently this is hardcoded to prefix the nassella
|
||||||
|
# domain with 'lldap'
|
||||||
|
if [ "$app" = "nassella" ]; then
|
||||||
|
echo "lldap.$subdomain.$ROOT_DOMAIN {"
|
||||||
|
echo " reverse_proxy nassella_lldap:17170"
|
||||||
|
echo "}"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,19 @@ for config_string in ${APP_CONFIGS[@]}; do
|
|||||||
IFS=','
|
IFS=','
|
||||||
read -r -a config <<< "$config_string"
|
read -r -a config <<< "$config_string"
|
||||||
|
|
||||||
|
app=${config[0]}
|
||||||
subdomain=${config[1]}
|
subdomain=${config[1]}
|
||||||
|
|
||||||
echo -n "$separator"
|
echo -n "$separator"
|
||||||
echo -n "\"$subdomain\""
|
echo -n "\"$subdomain\""
|
||||||
|
|
||||||
separator=', '
|
separator=', '
|
||||||
|
|
||||||
|
# see note about lldap in make-caddyfile.sh
|
||||||
|
if [ "$app" = "nassella" ]; then
|
||||||
|
echo -n "$separator"
|
||||||
|
echo -n "\"lldap.$subdomain\""
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "]"
|
echo "]"
|
||||||
|
|||||||
39
make-nassella-authelia-config.sh
Executable file
39
make-nassella-authelia-config.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -a # export everything in the config for later use by envsubst
|
||||||
|
|
||||||
|
. $1 # source the apps.config file with then env vars
|
||||||
|
|
||||||
|
read -r -a APP_CONFIGS <<< "$APP_CONFIGS"
|
||||||
|
|
||||||
|
nassella_subdomain=
|
||||||
|
|
||||||
|
for config_string in ${APP_CONFIGS[@]}; do
|
||||||
|
IFS=','
|
||||||
|
read -r -a config <<< "$config_string"
|
||||||
|
|
||||||
|
app=${config[0]}
|
||||||
|
subdomain=${config[1]}
|
||||||
|
|
||||||
|
if [ "$app" = "nassella" ]; then
|
||||||
|
nassella_subdomain="$subdomain"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
export NASSELLA_FULL_DOMAIN="$nassella_subdomain.$ROOT_DOMAIN"
|
||||||
|
export NASSELLA_FULL_LLDAP_DOMAIN="$NASSELLA_LLDAP_SUBDOMAIN.$nassella_subdomain.$ROOT_DOMAIN"
|
||||||
|
envsubst < all-apps/nassella/authelia-config/configuration.yml.tmpl > all-apps/nassella/authelia-config/configuration.yml
|
||||||
|
|
||||||
|
# write secrets
|
||||||
|
echo "$NASSELLA_POSTGRES_DB" > all-apps/nassella/postgres_db
|
||||||
|
echo "$NASSELLA_POSTGRES_USER" > all-apps/nassella/postgres_user
|
||||||
|
echo "$NASSELLA_POSTGRES_PASSWORD" > all-apps/nassella/postgres_password
|
||||||
|
|
||||||
|
echo "$NASSELLA_AUTHELIA_POSTGRES_DB" > all-apps/nassella/authelia_postgres_db
|
||||||
|
echo "$NASSELLA_AUTHELIA_POSTGRES_USER" > all-apps/nassella/authelia_postgres_user
|
||||||
|
echo "$NASSELLA_AUTHELIA_POSTGRES_PASSWORD" > all-apps/nassella/authelia_postgres_password
|
||||||
|
|
||||||
|
echo "$NASSELLA_LLDAP_POSTGRES_DB" > all-apps/nassella/lldap_postgres_db
|
||||||
|
echo "$NASSELLA_LLDAP_POSTGRES_USER" > all-apps/nassella/lldap_postgres_user
|
||||||
|
echo "$NASSELLA_LLDAP_POSTGRES_PASSWORD" > all-apps/nassella/lldap_postgres_password
|
||||||
39
make-nassella-lldap-config.sh
Executable file
39
make-nassella-lldap-config.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -a # export everything in the config for later use by envsubst
|
||||||
|
|
||||||
|
. $1 # source the apps.config file with then env vars
|
||||||
|
|
||||||
|
read -r -a APP_CONFIGS <<< "$APP_CONFIGS"
|
||||||
|
|
||||||
|
nassella_subdomain=
|
||||||
|
|
||||||
|
for config_string in ${APP_CONFIGS[@]}; do
|
||||||
|
IFS=','
|
||||||
|
read -r -a config <<< "$config_string"
|
||||||
|
|
||||||
|
app=${config[0]}
|
||||||
|
subdomain=${config[1]}
|
||||||
|
|
||||||
|
if [ "$app" = "nassella" ]; then
|
||||||
|
nassella_subdomain="$subdomain"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
export NASSELLA_FULL_DOMAIN="$nassella_subdomain.$ROOT_DOMAIN"
|
||||||
|
export NASSELLA_FULL_LLDAP_DOMAIN="$NASSELLA_LLDAP_SUBDOMAIN.$nassella_subdomain.$ROOT_DOMAIN"
|
||||||
|
envsubst < all-apps/nassella/lldap-config/lldap_config.toml.tmpl > all-apps/nassella/lldap-config/lldap_config.toml
|
||||||
|
|
||||||
|
# write secrets
|
||||||
|
echo "$NASSELLA_POSTGRES_DB" > all-apps/nassella/postgres_db
|
||||||
|
echo "$NASSELLA_POSTGRES_USER" > all-apps/nassella/postgres_user
|
||||||
|
echo "$NASSELLA_POSTGRES_PASSWORD" > all-apps/nassella/postgres_password
|
||||||
|
|
||||||
|
echo "$NASSELLA_AUTHELIA_POSTGRES_DB" > all-apps/nassella/authelia_postgres_db
|
||||||
|
echo "$NASSELLA_AUTHELIA_POSTGRES_USER" > all-apps/nassella/authelia_postgres_user
|
||||||
|
echo "$NASSELLA_AUTHELIA_POSTGRES_PASSWORD" > all-apps/nassella/authelia_postgres_password
|
||||||
|
|
||||||
|
echo "$NASSELLA_LLDAP_POSTGRES_DB" > all-apps/nassella/lldap_postgres_db
|
||||||
|
echo "$NASSELLA_LLDAP_POSTGRES_USER" > all-apps/nassella/lldap_postgres_user
|
||||||
|
echo "$NASSELLA_LLDAP_POSTGRES_PASSWORD" > all-apps/nassella/lldap_postgres_password
|
||||||
@@ -52,7 +52,7 @@ RUN chmod +x nassella-run
|
|||||||
|
|
||||||
FROM debian:trixie-slim
|
FROM debian:trixie-slim
|
||||||
RUN apt-get update && apt-get -y --no-install-recommends install \
|
RUN apt-get update && apt-get -y --no-install-recommends install \
|
||||||
libpq-dev \
|
libpq-dev ca-certificates gettext-base \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
COPY --from=buildeggs /usr/local/ /usr/local/
|
COPY --from=buildeggs /usr/local/ /usr/local/
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ drop table user_service_configs;
|
|||||||
drop index instances_user_id_instance_id_idx;
|
drop index instances_user_id_instance_id_idx;
|
||||||
drop table instances;
|
drop table instances;
|
||||||
|
|
||||||
drop index users_auth_user_id_idx;
|
drop index users_username_idx;
|
||||||
drop table users;
|
drop table users;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
create table users(
|
create table users(
|
||||||
user_id bigserial primary key,
|
user_id bigserial primary key,
|
||||||
auth_user_id int unique not null,
|
|
||||||
email varchar(255) not null,
|
email varchar(255) not null,
|
||||||
username varchar(255) not null unique,
|
username varchar(255) not null unique,
|
||||||
key_key varchar(255),
|
key_key varchar(255),
|
||||||
key_iv varchar(255)
|
key_iv varchar(255)
|
||||||
);
|
);
|
||||||
create unique index users_auth_user_id_idx on users (auth_user_id);
|
create unique index users_username_idx on users (username);
|
||||||
|
|
||||||
create table instances(
|
create table instances(
|
||||||
instance_id bigserial primary key,
|
instance_id bigserial primary key,
|
||||||
|
|||||||
48
src/db.scm
48
src/db.scm
@@ -7,7 +7,8 @@
|
|||||||
db-init db-clean
|
db-init db-clean
|
||||||
|
|
||||||
create-user delete-user
|
create-user delete-user
|
||||||
create-instance get-user-instances
|
get-user-id-by-username
|
||||||
|
create-instance destroy-instance get-user-instances
|
||||||
get-instance-ssh-pub-key get-instance-ssh-priv-key
|
get-instance-ssh-pub-key get-instance-ssh-priv-key
|
||||||
update-instance-ssh-pub-key
|
update-instance-ssh-pub-key
|
||||||
get-instance-restic-password
|
get-instance-restic-password
|
||||||
@@ -41,10 +42,17 @@
|
|||||||
crypto-tools
|
crypto-tools
|
||||||
spiffy)
|
spiffy)
|
||||||
|
|
||||||
(define connection-spec (make-parameter '((dbname . "nassella") (user . "nassella") (password . "password")
|
(define connection-spec
|
||||||
;; (host . "127.0.0.1")
|
(make-parameter
|
||||||
(host . "nassella_db")
|
(cond-expand
|
||||||
)))
|
(dev
|
||||||
|
'((dbname . "nassella") (user . "nassella") (password . "password")
|
||||||
|
(host . "127.0.0.1")))
|
||||||
|
(else
|
||||||
|
(let ((pw (string-trim-right (with-input-from-file "/run/secrets/nassella_postgres_password" read-string)))) ;; remove newline
|
||||||
|
`((dbname . "nassella") (user . "nassella") (password . ,pw)
|
||||||
|
(host . "nassella_db")))))))
|
||||||
|
|
||||||
(define db-connection (make-parameter #f))
|
(define db-connection (make-parameter #f))
|
||||||
|
|
||||||
(define (with-db proc)
|
(define (with-db proc)
|
||||||
@@ -101,7 +109,7 @@
|
|||||||
(define *root-key-key* (ensure-root-key))
|
(define *root-key-key* (ensure-root-key))
|
||||||
|
|
||||||
(define (get-user-key-and-iv conn user-id)
|
(define (get-user-key-and-iv conn user-id)
|
||||||
(row-alist (query conn "select auth_user_id, key_key, key_iv from users where user_id=$1;" user-id)))
|
(row-alist (query conn "select username, key_key, key_iv from users where user_id=$1;" user-id)))
|
||||||
|
|
||||||
(define (get-decrypted-user-key-and-iv conn user-id)
|
(define (get-decrypted-user-key-and-iv conn user-id)
|
||||||
(let* ((auth-user-id-and-user-key-and-iv (get-user-key-and-iv conn user-id))
|
(let* ((auth-user-id-and-user-key-and-iv (get-user-key-and-iv conn user-id))
|
||||||
@@ -109,9 +117,9 @@
|
|||||||
(raw-user-key (hexstring->blob (string-drop-right raw-user-key-and-tag (* tag-length 2))))
|
(raw-user-key (hexstring->blob (string-drop-right raw-user-key-and-tag (* tag-length 2))))
|
||||||
(raw-user-tag (hexstring->blob (string-take-right raw-user-key-and-tag (* tag-length 2))))
|
(raw-user-tag (hexstring->blob (string-take-right raw-user-key-and-tag (* tag-length 2))))
|
||||||
(user-key (decrypt (blob->string raw-user-key) (blob->string raw-user-tag) *root-key-key* *root-key-iv*
|
(user-key (decrypt (blob->string raw-user-key) (blob->string raw-user-tag) *root-key-key* *root-key-iv*
|
||||||
(string->blob (number->string (alist-ref 'auth_user_id auth-user-id-and-user-key-and-iv)))))
|
(string->blob (alist-ref 'username auth-user-id-and-user-key-and-iv))))
|
||||||
(user-iv (alist-ref 'key_iv auth-user-id-and-user-key-and-iv))
|
(user-iv (alist-ref 'key_iv auth-user-id-and-user-key-and-iv))
|
||||||
(auth-user-id (alist-ref 'auth_user_id auth-user-id-and-user-key-and-iv)))
|
(auth-user-id (alist-ref 'username auth-user-id-and-user-key-and-iv)))
|
||||||
(values (hexstring->blob user-key) (hexstring->blob user-iv) auth-user-id)))
|
(values (hexstring->blob user-key) (hexstring->blob user-iv) auth-user-id)))
|
||||||
|
|
||||||
(define (user-encrypt message user-key user-iv user-id)
|
(define (user-encrypt message user-key user-iv user-id)
|
||||||
@@ -131,17 +139,17 @@
|
|||||||
(raw-tag (hexstring->blob (string-take-right message-and-tag (* tag-length 2)))))
|
(raw-tag (hexstring->blob (string-take-right message-and-tag (* tag-length 2)))))
|
||||||
(user-decrypt (blob->string raw-message) (blob->string raw-tag) user-key user-iv user-id)))
|
(user-decrypt (blob->string raw-message) (blob->string raw-tag) user-key user-iv user-id)))
|
||||||
|
|
||||||
(define (create-user conn auth-user-id email username)
|
(define (create-user conn email username)
|
||||||
(let ((user-key (blob->hexstring/uppercase (generate-key)))
|
(let ((user-key (blob->hexstring/uppercase (generate-key)))
|
||||||
(user-iv (blob->hexstring/uppercase (generate-iv))))
|
(user-iv (blob->hexstring/uppercase (generate-iv))))
|
||||||
(receive (enc-user-key tag)
|
(receive (enc-user-key tag)
|
||||||
(encrypt user-key *root-key-key* *root-key-iv* (string->blob (number->string auth-user-id)))
|
(encrypt user-key *root-key-key* *root-key-iv* (string->blob username))
|
||||||
(let ((user-id
|
(let ((user-id
|
||||||
(value-at
|
(value-at
|
||||||
(query conn
|
(query conn
|
||||||
"insert into users(auth_user_id, email, username, key_key, key_iv) values ($1, $2, $3, $4, $5)
|
"insert into users(email, username, key_key, key_iv) values ($1, $2, $3, $4)
|
||||||
returning users.user_id;"
|
returning users.user_id;"
|
||||||
auth-user-id email username
|
email username
|
||||||
(string-append (blob->hexstring/uppercase (string->blob enc-user-key))
|
(string-append (blob->hexstring/uppercase (string->blob enc-user-key))
|
||||||
(blob->hexstring/uppercase (string->blob tag)))
|
(blob->hexstring/uppercase (string->blob tag)))
|
||||||
user-iv))))
|
user-iv))))
|
||||||
@@ -150,6 +158,12 @@ returning users.user_id;"
|
|||||||
(define (delete-user conn user-id)
|
(define (delete-user conn user-id)
|
||||||
(query conn "delete from users where user_id=$1;" user-id))
|
(query conn "delete from users where user_id=$1;" user-id))
|
||||||
|
|
||||||
|
(define (get-user-id-by-username conn username)
|
||||||
|
(let ((res (query conn "select user_id from users where username=$1;" username)))
|
||||||
|
(if (> (row-count res) 0)
|
||||||
|
(value-at res)
|
||||||
|
#f)))
|
||||||
|
|
||||||
;; We also encrypt the ssh pub key not to hide it but to make it
|
;; We also encrypt the ssh pub key not to hide it but to make it
|
||||||
;; more difficult for someone to tamper with it which could allow
|
;; more difficult for someone to tamper with it which could allow
|
||||||
;; an attacker to poison an instance with an ssh key that they have
|
;; an attacker to poison an instance with an ssh key that they have
|
||||||
@@ -171,6 +185,9 @@ returning users.user_id;"
|
|||||||
(query conn "insert into user_terraform_state(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
(query conn "insert into user_terraform_state(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
||||||
instance-id)))
|
instance-id)))
|
||||||
|
|
||||||
|
(define (destroy-instance conn instance-id)
|
||||||
|
(query conn "delete from instances where instance_id=$1;" instance-id))
|
||||||
|
|
||||||
(define (get-instance-ssh-priv-key conn user-id instance-id)
|
(define (get-instance-ssh-priv-key conn user-id instance-id)
|
||||||
(receive (user-key user-iv auth-user-id)
|
(receive (user-key user-iv auth-user-id)
|
||||||
(get-decrypted-user-key-and-iv conn user-id)
|
(get-decrypted-user-key-and-iv conn user-id)
|
||||||
@@ -386,7 +403,10 @@ returning users.user_id;"
|
|||||||
(value-at (query conn "select status from deployments where id=$1;" deployment-id)))
|
(value-at (query conn "select status from deployments where id=$1;" deployment-id)))
|
||||||
|
|
||||||
(define (get-most-recent-deployment-status conn user-id instance-id)
|
(define (get-most-recent-deployment-status conn user-id instance-id)
|
||||||
(value-at (query conn "select status from deployments where user_id=$1 and instance_id=$2 order by id DESC limit 1;" user-id instance-id)))
|
(let ((res (query conn "select status from deployments where user_id=$1 and instance_id=$2 order by id DESC limit 1;" user-id instance-id)))
|
||||||
|
(if (> (row-count res) 0)
|
||||||
|
(value-at res)
|
||||||
|
#f)))
|
||||||
|
|
||||||
(define *deployments-column-map*
|
(define *deployments-column-map*
|
||||||
'((generate-configs . "generate_configs")
|
'((generate-configs . "generate_configs")
|
||||||
@@ -550,7 +570,7 @@ returning users.user_id;"
|
|||||||
(string-split (with-input-from-file "db-init.sql" read-string) ";"))
|
(string-split (with-input-from-file "db-init.sql" read-string) ";"))
|
||||||
(log-to (debug-log) "table creation finished")
|
(log-to (debug-log) "table creation finished")
|
||||||
(log-to (debug-log) "creating test user")
|
(log-to (debug-log) "creating test user")
|
||||||
(create-user db 1 "me@example.com" "username")
|
(create-user db "me@example.com" "username")
|
||||||
(log-to (debug-log) "test user creation finished"))))))
|
(log-to (debug-log) "test user creation finished"))))))
|
||||||
|
|
||||||
(define (db-clean)
|
(define (db-clean)
|
||||||
|
|||||||
328
src/nassella.scm
328
src/nassella.scm
@@ -298,7 +298,11 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
|
|
||||||
(define test-user-id (make-parameter 1))
|
(define test-user-id (make-parameter 1))
|
||||||
(define (session-user-id)
|
(define (session-user-id)
|
||||||
(or (session-get "user-id") (test-user-id)))
|
(cond-expand
|
||||||
|
(dev
|
||||||
|
(or (session-get "user-id") (test-user-id)))
|
||||||
|
(else
|
||||||
|
(session-get "user-id"))))
|
||||||
|
|
||||||
(define-syntax get/widgets
|
(define-syntax get/widgets
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
@@ -314,9 +318,14 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
headers)
|
headers)
|
||||||
;; `((meta (@ (name "viewport") (content "width=device-width"))))
|
;; `((meta (@ (name "viewport") (content "width=device-width"))))
|
||||||
(begin
|
(begin
|
||||||
;; TODO remove once sessions are integrated
|
(cond-expand
|
||||||
(session-set! "user-id" (test-user-id))
|
(dev
|
||||||
(session-set! "username" "me")
|
(session-set! "user-id" (test-user-id))
|
||||||
|
(session-set! "username" "me"))
|
||||||
|
(else
|
||||||
|
(let ((user-id (with-db/transaction (lambda (db) (get-user-id-by-username db (header-value 'remote-user (request-headers (current-request))))))))
|
||||||
|
(when user-id (session-set! "user-id" user-id))
|
||||||
|
(session-set! "username" (header-value 'remote-user (request-headers (current-request)))))))
|
||||||
body ...))))))))
|
body ...))))))))
|
||||||
|
|
||||||
(define-widget (Container ((max-width ($ 'width.main.max)) (style '())) contents)
|
(define-widget (Container ((max-width ($ 'width.main.max)) (style '())) contents)
|
||||||
@@ -524,6 +533,57 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
|
|
||||||
(json-parsers (cons array-as-list-parser (json-parsers)))
|
(json-parsers (cons array-as-list-parser (json-parsers)))
|
||||||
|
|
||||||
|
;; TODO change username to to a prod API key that has read access
|
||||||
|
;; to the checkout session
|
||||||
|
(define (send-stripe-request #!key (method 'GET) endpoint (body #f) (username ""))
|
||||||
|
(define api-endpoint "https://api.stripe.com/")
|
||||||
|
(define api-version "/v1")
|
||||||
|
|
||||||
|
(with-input-from-request
|
||||||
|
(make-request method: method
|
||||||
|
uri: (uri-reference (string-append api-endpoint api-version endpoint))
|
||||||
|
headers: (headers `((authorization . (#(basic ((username . ,username) (password . ""))))))))
|
||||||
|
body
|
||||||
|
read-json))
|
||||||
|
|
||||||
|
(define (stripe-session-email sid)
|
||||||
|
(alist-ref
|
||||||
|
'email
|
||||||
|
(alist-ref
|
||||||
|
'customer_details
|
||||||
|
(send-stripe-request endpoint: (string-append "/checkout/sessions/" sid)))))
|
||||||
|
|
||||||
|
|
||||||
|
(define (create-lldap-user username email)
|
||||||
|
;; query = mutation createUser($user:CreateUserInput!){createUser(user:$user){id email displayName firstName lastName avatar}}
|
||||||
|
;; variables = {\"user\":{\"id\":\"${id}\",\"email\":\"${email}\",\"displayName\":\"${name}\",\"firstName\":\"${firstName}\",\"lastName\":\"${lastName}\",\"avatar\":\"
|
||||||
|
;; data="{\"query\":\"${query}\",\"variables\":${variables}"
|
||||||
|
;; http://localhost:17170/api/graphql
|
||||||
|
;; -H 'Content-Type: application/json' \
|
||||||
|
;; -H "Authorization: Bearer $token" \
|
||||||
|
(let ((api-token
|
||||||
|
(alist-ref
|
||||||
|
'token
|
||||||
|
(with-input-from-request
|
||||||
|
(make-request method: 'POST
|
||||||
|
uri: (uri-reference "http://nassella_lldap:17170/auth/simple/login")
|
||||||
|
headers: (headers `((content-type application/json))))
|
||||||
|
(lambda ()
|
||||||
|
(write-json
|
||||||
|
`((username . "admin") (password . ,(string-trim-right (with-input-from-file "/run/secrets/nassella_lldap_admin_password" read-string)))))) ;; trim to remove newline
|
||||||
|
read-json))))
|
||||||
|
(with-input-from-request
|
||||||
|
(make-request method: 'POST
|
||||||
|
uri: (uri-reference "http://nassella_lldap:17170/api/graphql")
|
||||||
|
headers: (headers `((content-type application/json)
|
||||||
|
(authorization #(,(string-append "Bearer " api-token) raw)))))
|
||||||
|
(lambda ()
|
||||||
|
(write-json
|
||||||
|
`((query . "mutation createUser($user:CreateUserInput!){createUser(user:$user){id email displayName firstName lastName avatar}}")
|
||||||
|
(variables . ((user . ((id . ,username)
|
||||||
|
(email . ,email))))))))
|
||||||
|
read-json)))
|
||||||
|
|
||||||
(define (get-digital-ocean-regions api-token)
|
(define (get-digital-ocean-regions api-token)
|
||||||
(filter
|
(filter
|
||||||
(lambda (r)
|
(lambda (r)
|
||||||
@@ -733,6 +793,21 @@ chmod -R 777 /opt/keys"))
|
|||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+={}[]|<>,.?")
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+={}[]|<>,.?")
|
||||||
30)))
|
30)))
|
||||||
|
|
||||||
|
(define (generate-jwt-secret)
|
||||||
|
(generator->string (gtake (make-random-char-generator
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+={}[]|<>,.?")
|
||||||
|
32)))
|
||||||
|
|
||||||
|
(define (generate-key-seed)
|
||||||
|
(generator->string (gtake (make-random-char-generator
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+={}[]|<>,.?")
|
||||||
|
32)))
|
||||||
|
|
||||||
|
(define (generate-authelia-key-seed)
|
||||||
|
(generator->string (gtake (make-random-char-generator
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||||||
|
64)))
|
||||||
|
|
||||||
(define (generate-postgres-password)
|
(define (generate-postgres-password)
|
||||||
(generator->string (gtake (make-random-char-generator
|
(generator->string (gtake (make-random-char-generator
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||||||
@@ -746,13 +821,34 @@ chmod -R 777 /opt/keys"))
|
|||||||
(with-schematra-app app
|
(with-schematra-app app
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
|
||||||
|
;;; UNSECURED PAGES
|
||||||
|
(get/widgets
|
||||||
|
("/unsecured/account/create")
|
||||||
|
`(App
|
||||||
|
(form
|
||||||
|
(@ (action "/unsecured/account/create-submit") (method POST))
|
||||||
|
(VStack
|
||||||
|
(Fieldset
|
||||||
|
(@ (title "Account Details"))
|
||||||
|
(Field (@ (name "username") (label ("Username"))))
|
||||||
|
(input (@ (type "hidden") (name "sid") (value ,(alist-ref 'sid (current-params) equal?))))
|
||||||
|
(Button (@ (type "submit")) "Create Account"))))))
|
||||||
|
|
||||||
|
(post "/unsecured/account/create-submit"
|
||||||
|
(let ((email (stripe-session-email (alist-ref 'sid (current-params))))
|
||||||
|
(username (alist-ref 'username (current-params))))
|
||||||
|
(create-lldap-user username email)
|
||||||
|
(with-db/transaction (lambda (db) (create-user db email username))))
|
||||||
|
(redirect "/authelia/reset-password"))
|
||||||
|
|
||||||
|
;;; REQUIRES AUTHED USER
|
||||||
(post "/config/wizard/create-instance"
|
(post "/config/wizard/create-instance"
|
||||||
(let* ((ssh-keys (generate-ssh-key (session-user-id)))
|
(let* ((ssh-keys (generate-ssh-key (session-user-id)))
|
||||||
(instance-id (with-db/transaction
|
(instance-id (with-db/transaction
|
||||||
(lambda (db)
|
(lambda (db)
|
||||||
(create-instance db (session-user-id) (first ssh-keys) (second ssh-keys)
|
(create-instance db (session-user-id) (first ssh-keys) (second ssh-keys)
|
||||||
(generate-restic-password))))))
|
(generate-restic-password))))))
|
||||||
(redirect (conc "/config/wizard/services/" instance-id))))
|
(redirect (conc "/config/wizard/services/" instance-id))))
|
||||||
|
|
||||||
;; TODO should all these key related form fields be of type password
|
;; TODO should all these key related form fields be of type password
|
||||||
;; so the browser doesn't save them???
|
;; so the browser doesn't save them???
|
||||||
@@ -957,7 +1053,11 @@ chmod -R 777 /opt/keys"))
|
|||||||
,@(if (member 'nassella selected-apps)
|
,@(if (member 'nassella selected-apps)
|
||||||
`((Fieldset
|
`((Fieldset
|
||||||
(@ (title "Nassella"))
|
(@ (title "Nassella"))
|
||||||
(Field (@ (name "nassella-subdomain") (label ("Subdomain")) (value ,(alist-ref 'subdomain (alist-ref 'nassella app-config eq? '()) eq? "nassella"))))))
|
(Field (@ (name "nassella-subdomain") (label ("Subdomain")) (value ,(alist-ref 'subdomain (alist-ref 'nassella app-config eq? '()) eq? "app"))))
|
||||||
|
(Field (@ (name "nassella-lldap-subdomain") (label ("LLDAP Subdomain"))
|
||||||
|
(value ,(alist-ref 'lldap-subdomain (alist-ref 'nassella app-config eq? '()) eq? "lldap"))))
|
||||||
|
(Field (@ (name "nassella-lldap-admin-password") (label ("Admin Password")) (type "password")
|
||||||
|
(value ,(alist-ref 'lldap-admin-password (alist-ref 'nassella app-config eq? '()) eq? ""))))))
|
||||||
'())
|
'())
|
||||||
(Fieldset
|
(Fieldset
|
||||||
(@ (title "Log Viewer"))
|
(@ (title "Log Viewer"))
|
||||||
@@ -967,7 +1067,7 @@ chmod -R 777 /opt/keys"))
|
|||||||
(value ,(alist-ref 'user (alist-ref 'log-viewer app-config eq? '()) eq? ""))))
|
(value ,(alist-ref 'user (alist-ref 'log-viewer app-config eq? '()) eq? ""))))
|
||||||
(Field (@ (name "log-viewer-password") (label ("Password")) (type "password")
|
(Field (@ (name "log-viewer-password") (label ("Password")) (type "password")
|
||||||
(value ,(alist-ref 'password (alist-ref 'log-viewer app-config eq? '()) eq? "")))))
|
(value ,(alist-ref 'password (alist-ref 'log-viewer app-config eq? '()) eq? "")))))
|
||||||
,@(if (or (member 'nextcloud selected-apps) (member 'ghost selected-apps))
|
,@(if (or (member 'nextcloud selected-apps) (member 'ghost selected-apps) (member 'nassella selected-apps))
|
||||||
`((Fieldset
|
`((Fieldset
|
||||||
(@ (title "All Apps - Email - SMTP"))
|
(@ (title "All Apps - Email - SMTP"))
|
||||||
(Field (@ (name "smtp-host") (label ("Host"))
|
(Field (@ (name "smtp-host") (label ("Host"))
|
||||||
@@ -1009,7 +1109,30 @@ chmod -R 777 /opt/keys"))
|
|||||||
(redis-password . ,(or (alist-ref 'redis-password
|
(redis-password . ,(or (alist-ref 'redis-password
|
||||||
(alist-ref 'nextcloud config eq? '()))
|
(alist-ref 'nextcloud config eq? '()))
|
||||||
(generate-redis-password)))))
|
(generate-redis-password)))))
|
||||||
(nassella . ((subdomain . ,(alist-ref 'nassella-subdomain (current-params)))))
|
(nassella . ((subdomain . ,(alist-ref 'nassella-subdomain (current-params)))
|
||||||
|
(postgres-password . ,(or (alist-ref 'postgres-password
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-postgres-password)))
|
||||||
|
(authelia-postgres-password . ,(or (alist-ref 'authelia-postgres-password
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-postgres-password)))
|
||||||
|
(lldap-postgres-password . ,(or (alist-ref 'lldap-postgres-password
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-postgres-password)))
|
||||||
|
(lldap-jwt-secret . ,(or (alist-ref 'lldap-jwt-secret
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-jwt-secret)))
|
||||||
|
(lldap-key-seed . ,(or (alist-ref 'lldap-key-seed
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-key-seed)))
|
||||||
|
(lldap-subdomain . ,(alist-ref 'nassella-lldap-subdomain (current-params)))
|
||||||
|
(lldap-admin-password . ,(alist-ref 'nassella-lldap-admin-password (current-params)))
|
||||||
|
(authelia-jwt-secret . ,(or (alist-ref 'authelia-jwt-secret
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-jwt-secret)))
|
||||||
|
(authelia-key-seed . ,(or (alist-ref 'authelia-key-seed
|
||||||
|
(alist-ref 'nassella config eq? '()))
|
||||||
|
(generate-authelia-key-seed)))))
|
||||||
(log-viewer . ((subdomain . ,(alist-ref 'log-viewer-subdomain (current-params)))
|
(log-viewer . ((subdomain . ,(alist-ref 'log-viewer-subdomain (current-params)))
|
||||||
(user . ,(alist-ref 'log-viewer-user (current-params)))
|
(user . ,(alist-ref 'log-viewer-user (current-params)))
|
||||||
(password . ,(alist-ref 'log-viewer-password (current-params)))))
|
(password . ,(alist-ref 'log-viewer-password (current-params)))))
|
||||||
@@ -1117,7 +1240,8 @@ chmod -R 777 /opt/keys"))
|
|||||||
(@ (step "Review"))
|
(@ (step "Review"))
|
||||||
(h2 "Root Domain")
|
(h2 "Root Domain")
|
||||||
,root-domain
|
,root-domain
|
||||||
(h2 "Apps")
|
(h2 "Apps") ;; TODO if an app that was previously selected is now unselected we need to somehow delete its data
|
||||||
|
;; so that if the user then re-deploys the app later we don't have key conflicts
|
||||||
(ul ,@(map (lambda (app) `(li ,app " @ "
|
(ul ,@(map (lambda (app) `(li ,app " @ "
|
||||||
,(alist-ref 'subdomain (alist-ref app config))
|
,(alist-ref 'subdomain (alist-ref app config))
|
||||||
"."
|
"."
|
||||||
@@ -1182,6 +1306,21 @@ chmod -R 777 /opt/keys"))
|
|||||||
("NEXTCLOUD_REDIS_PASSWORD" . ,(alist-ref 'redis-password (alist-ref 'nextcloud config)))
|
("NEXTCLOUD_REDIS_PASSWORD" . ,(alist-ref 'redis-password (alist-ref 'nextcloud config)))
|
||||||
("GHOST_DATABASE_ROOT_PASSWORD" . ,(alist-ref 'postgres-root-password (alist-ref 'ghost config)))
|
("GHOST_DATABASE_ROOT_PASSWORD" . ,(alist-ref 'postgres-root-password (alist-ref 'ghost config)))
|
||||||
("GHOST_DATABASE_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'ghost config)))
|
("GHOST_DATABASE_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'ghost config)))
|
||||||
|
("NASSELLA_LLDAP_SUBDOMAIN" . ,(alist-ref 'lldap-subdomain (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_POSTGRES_DB" . "nassella")
|
||||||
|
("NASSELLA_POSTGRES_USER" . "nassella")
|
||||||
|
("NASSELLA_POSTGRES_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_AUTHELIA_POSTGRES_DB" . "authelia")
|
||||||
|
("NASSELLA_AUTHELIA_POSTGRES_USER" . "authelia")
|
||||||
|
("NASSELLA_AUTHELIA_POSTGRES_PASSWORD" . ,(alist-ref 'authelia-postgres-password (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_LLDAP_POSTGRES_DB" . "lldap")
|
||||||
|
("NASSELLA_LLDAP_POSTGRES_USER" . "lldap")
|
||||||
|
("NASSELLA_LLDAP_POSTGRES_PASSWORD" . ,(alist-ref 'lldap-postgres-password (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_LLDAP_JWT_SECRET" . ,(alist-ref 'lldap-jwt-secret (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_LLDAP_KEY_SEED" . ,(alist-ref 'lldap-key-seed (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_LLDAP_ADMIN_PASSWORD" . ,(alist-ref 'lldap-admin-password (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_AUTHELIA_JWT_SECRET" . ,(alist-ref 'authelia-jwt-secret (alist-ref 'nassella config)))
|
||||||
|
("NASSELLA_AUTHELIA_KEY_SEED" . ,(alist-ref 'authelia-key-seed (alist-ref 'nassella config)))
|
||||||
("SMTP_HOST" . ,(alist-ref 'smtp-host (alist-ref 'all-apps config)))
|
("SMTP_HOST" . ,(alist-ref 'smtp-host (alist-ref 'all-apps config)))
|
||||||
("SMTP_PORT" . ,(alist-ref 'smtp-port (alist-ref 'all-apps config)))
|
("SMTP_PORT" . ,(alist-ref 'smtp-port (alist-ref 'all-apps config)))
|
||||||
("SMTP_AUTH_USER" . ,(alist-ref 'smtp-auth-user (alist-ref 'all-apps config)))
|
("SMTP_AUTH_USER" . ,(alist-ref 'smtp-auth-user (alist-ref 'all-apps config)))
|
||||||
@@ -1200,10 +1339,10 @@ chmod -R 777 /opt/keys"))
|
|||||||
("cloudflare_api_token" . ,(alist-ref 'cloudflare-api-token service-config))
|
("cloudflare_api_token" . ,(alist-ref 'cloudflare-api-token service-config))
|
||||||
("cloudflare_zone_id" . ,(alist-ref 'cloudflare-zone-id service-config))
|
("cloudflare_zone_id" . ,(alist-ref 'cloudflare-zone-id service-config))
|
||||||
("cloudflare_account_id" . ,(alist-ref 'cloudflare-account-id service-config))
|
("cloudflare_account_id" . ,(alist-ref 'cloudflare-account-id service-config))
|
||||||
("cluster_name" . "mycluster")
|
("cluster_name" . "nassella")
|
||||||
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
||||||
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
||||||
("flatcar_stable_version" . "4459.2.3")))
|
("flatcar_stable_version" . "4459.2.4")))
|
||||||
;; remove the newline that generating the ssh key adds
|
;; remove the newline that generating the ssh key adds
|
||||||
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]"))))
|
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]"))))
|
||||||
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
||||||
@@ -1346,7 +1485,9 @@ chmod -R 777 /opt/keys"))
|
|||||||
(li "Upgrade Now (pending automatic upgrades scheduled for: )")
|
(li "Upgrade Now (pending automatic upgrades scheduled for: )")
|
||||||
(li "Manage Backups")
|
(li "Manage Backups")
|
||||||
(li (a (@ (href "/destroy/" ,(alist-ref 'instance-id instance)))
|
(li (a (@ (href "/destroy/" ,(alist-ref 'instance-id instance)))
|
||||||
"Destroy (confirmation required)")))))))
|
"Destroy - deletes data and configuration (confirmation required)"))
|
||||||
|
(li (a (@ (href "/reset/" ,(alist-ref 'instance-id instance)))
|
||||||
|
"Reset - deletes data (confirmation required)")))))))
|
||||||
(with-db/transaction
|
(with-db/transaction
|
||||||
(lambda (db)
|
(lambda (db)
|
||||||
(get-dashboard db (session-user-id))))))))))
|
(get-dashboard db (session-user-id))))))))))
|
||||||
@@ -1361,7 +1502,7 @@ chmod -R 777 /opt/keys"))
|
|||||||
`(App
|
`(App
|
||||||
(h2 "Destroy Instance")
|
(h2 "Destroy Instance")
|
||||||
,root-domain
|
,root-domain
|
||||||
(h2 "This action is NOT reversible")
|
(h2 "This action is NOT reversible. All data will be lost!")
|
||||||
(form
|
(form
|
||||||
(@ (action ,(conc "/destroy-submit/" instance-id)) (method POST))
|
(@ (action ,(conc "/destroy-submit/" instance-id)) (method POST))
|
||||||
(VStack
|
(VStack
|
||||||
@@ -1399,50 +1540,66 @@ chmod -R 777 /opt/keys"))
|
|||||||
(begin
|
(begin
|
||||||
(setup-deploy-files dir (alist-ref 'state terraform-state) (alist-ref 'backup terraform-state))
|
(setup-deploy-files dir (alist-ref 'state terraform-state) (alist-ref 'backup terraform-state))
|
||||||
(with-output-to-file (string-append dir "/config/apps.config")
|
(with-output-to-file (string-append dir "/config/apps.config")
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(map (lambda (e)
|
(map (lambda (e)
|
||||||
(write-config-entry (car e) (cdr e)))
|
(write-config-entry (car e) (cdr e)))
|
||||||
`(("ROOT_DOMAIN" . ,root-domain)
|
`(("ROOT_DOMAIN" . ,root-domain)
|
||||||
("APP_CONFIGS" . ,(string-intersperse
|
("APP_CONFIGS" . ,(string-intersperse
|
||||||
(map (lambda (app)
|
(map (lambda (app)
|
||||||
(conc (if (eq? app 'log-viewer) 'dozzle app)
|
(conc (if (eq? app 'log-viewer) 'dozzle app)
|
||||||
","
|
","
|
||||||
(alist-ref 'subdomain (alist-ref app config))))
|
(alist-ref 'subdomain (alist-ref app config))))
|
||||||
selected-apps)
|
selected-apps)
|
||||||
" "))
|
" "))
|
||||||
("HOST_ADMIN_USER" . ,(alist-ref 'user (alist-ref 'log-viewer config)))
|
("HOST_ADMIN_USER" . ,(alist-ref 'user (alist-ref 'log-viewer config)))
|
||||||
("HOST_ADMIN_PASSWORD" . ,(alist-ref 'password (alist-ref 'log-viewer config)))
|
("HOST_ADMIN_PASSWORD" . ,(alist-ref 'password (alist-ref 'log-viewer config)))
|
||||||
("NEXTCLOUD_ADMIN_USER" . ,(alist-ref 'admin-user (alist-ref 'nextcloud config)))
|
("NEXTCLOUD_ADMIN_USER" . ,(alist-ref 'admin-user (alist-ref 'nextcloud config)))
|
||||||
("NEXTCLOUD_ADMIN_PASSWORD" . ,(alist-ref 'admin-password (alist-ref 'nextcloud config)))
|
("NEXTCLOUD_ADMIN_PASSWORD" . ,(alist-ref 'admin-password (alist-ref 'nextcloud config)))
|
||||||
("NEXTCLOUD_POSTGRES_DB" . "nextcloud")
|
("NEXTCLOUD_POSTGRES_DB" . "nextcloud")
|
||||||
("NEXTCLOUD_POSTGRES_USER" . "nextcloud")
|
("NEXTCLOUD_POSTGRES_USER" . "nextcloud")
|
||||||
("NEXTCLOUD_POSTGRES_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'nextcloud config)))
|
("NEXTCLOUD_POSTGRES_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'nextcloud config)))
|
||||||
("NEXTCLOUD_REDIS_PASSWORD" . ,(alist-ref 'redis-password (alist-ref 'nextcloud config)))
|
("NEXTCLOUD_REDIS_PASSWORD" . ,(alist-ref 'redis-password (alist-ref 'nextcloud config)))
|
||||||
("GHOST_DATABASE_ROOT_PASSWORD" . ,(alist-ref 'postgres-root-password (alist-ref 'ghost config)))
|
("GHOST_DATABASE_ROOT_PASSWORD" . ,(alist-ref 'postgres-root-password (alist-ref 'ghost config)))
|
||||||
("GHOST_DATABASE_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'ghost config)))
|
("GHOST_DATABASE_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'ghost config)))
|
||||||
("SMTP_HOST" . ,(alist-ref 'smtp-host (alist-ref 'all-apps config)))
|
("NASSELLA_LLDAP_SUBDOMAIN" . ,(alist-ref 'lldap-subdomain (alist-ref 'nassella config)))
|
||||||
("SMTP_PORT" . ,(alist-ref 'smtp-port (alist-ref 'all-apps config)))
|
("NASSELLA_POSTGRES_DB" . "nassella")
|
||||||
("SMTP_AUTH_USER" . ,(alist-ref 'smtp-auth-user (alist-ref 'all-apps config)))
|
("NASSELLA_POSTGRES_USER" . "nassella")
|
||||||
("SMTP_AUTH_PASSWORD" . ,(alist-ref 'smtp-auth-password (alist-ref 'all-apps config)))
|
("NASSELLA_POSTGRES_PASSWORD" . ,(alist-ref 'postgres-password (alist-ref 'nassella config)))
|
||||||
("SMTP_FROM" . ,(alist-ref 'smtp-from (alist-ref 'all-apps config)))
|
("NASSELLA_AUTHELIA_POSTGRES_DB" . "authelia")
|
||||||
("BACKBLAZE_KEY_ID" . ,(alist-ref 'backblaze-key-id service-config))
|
("NASSELLA_AUTHELIA_POSTGRES_USER" . "authelia")
|
||||||
("BACKBLAZE_APPLICATION_KEY" . ,(alist-ref 'backblaze-application-key service-config))
|
("NASSELLA_AUTHELIA_POSTGRES_PASSWORD" . ,(alist-ref 'authelia-postgres-password (alist-ref 'nassella config)))
|
||||||
("BACKBLAZE_BUCKET_URL" . ,(alist-ref 'backblaze-bucket-url service-config))
|
("NASSELLA_LLDAP_POSTGRES_DB" . "lldap")
|
||||||
("RESTIC_PASSWORD" . ,restic-password)))))
|
("NASSELLA_LLDAP_POSTGRES_USER" . "lldap")
|
||||||
(with-output-to-file (string-append dir "/config/production.tfvars")
|
("NASSELLA_LLDAP_POSTGRES_PASSWORD" . ,(alist-ref 'lldap-postgres-password (alist-ref 'nassella config)))
|
||||||
(lambda ()
|
("NASSELLA_LLDAP_JWT_SECRET" . ,(alist-ref 'lldap-jwt-secret (alist-ref 'nassella config)))
|
||||||
(map (lambda (e)
|
("NASSELLA_LLDAP_KEY_SEED" . ,(alist-ref 'lldap-key-seed (alist-ref 'nassella config)))
|
||||||
(write-config-entry (car e) (cdr e)))
|
("NASSELLA_LLDAP_ADMIN_PASSWORD" . ,(alist-ref 'lldap-admin-password (alist-ref 'nassella config)))
|
||||||
`(("server_type" . ,(alist-ref 'digitalocean-size service-config))
|
("NASSELLA_AUTHELIA_JWT_SECRET" . ,(alist-ref 'authelia-jwt-secret (alist-ref 'nassella config)))
|
||||||
("do_token" . ,(alist-ref 'digitalocean-api-token service-config))
|
("NASSELLA_AUTHELIA_KEY_SEED" . ,(alist-ref 'authelia-key-seed (alist-ref 'nassella config)))
|
||||||
("cloudflare_api_token" . ,(alist-ref 'cloudflare-api-token service-config))
|
("SMTP_HOST" . ,(alist-ref 'smtp-host (alist-ref 'all-apps config)))
|
||||||
("cloudflare_zone_id" . ,(alist-ref 'cloudflare-zone-id service-config))
|
("SMTP_PORT" . ,(alist-ref 'smtp-port (alist-ref 'all-apps config)))
|
||||||
("cloudflare_account_id" . ,(alist-ref 'cloudflare-account-id service-config))
|
("SMTP_AUTH_USER" . ,(alist-ref 'smtp-auth-user (alist-ref 'all-apps config)))
|
||||||
("cluster_name" . "mycluster")
|
("SMTP_AUTH_PASSWORD" . ,(alist-ref 'smtp-auth-password (alist-ref 'all-apps config)))
|
||||||
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
("SMTP_FROM" . ,(alist-ref 'smtp-from (alist-ref 'all-apps config)))
|
||||||
("flatcar_stable_version" . "4459.2.1")))
|
("BACKBLAZE_KEY_ID" . ,(alist-ref 'backblaze-key-id service-config))
|
||||||
;; remove the newline that generating the ssh key adds
|
("BACKBLAZE_APPLICATION_KEY" . ,(alist-ref 'backblaze-application-key service-config))
|
||||||
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]")))
|
("BACKBLAZE_BUCKET_URL" . ,(alist-ref 'backblaze-bucket-url service-config))
|
||||||
|
("RESTIC_PASSWORD" . ,restic-password)))))
|
||||||
|
(with-output-to-file (string-append dir "/config/production.tfvars")
|
||||||
|
(lambda ()
|
||||||
|
(map (lambda (e)
|
||||||
|
(write-config-entry (car e) (cdr e)))
|
||||||
|
`(("server_type" . ,(alist-ref 'digitalocean-size service-config))
|
||||||
|
("do_token" . ,(alist-ref 'digitalocean-api-token service-config))
|
||||||
|
("cloudflare_api_token" . ,(alist-ref 'cloudflare-api-token service-config))
|
||||||
|
("cloudflare_zone_id" . ,(alist-ref 'cloudflare-zone-id service-config))
|
||||||
|
("cloudflare_account_id" . ,(alist-ref 'cloudflare-account-id service-config))
|
||||||
|
("cluster_name" . "nassella")
|
||||||
|
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
||||||
|
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
||||||
|
("flatcar_stable_version" . "4459.2.4")))
|
||||||
|
;; remove the newline that generating the ssh key adds
|
||||||
|
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]")))
|
||||||
;; TODO need a new table to track destroying?
|
;; TODO need a new table to track destroying?
|
||||||
;; as this is creating a new "deployment"
|
;; as this is creating a new "deployment"
|
||||||
;; to attach state to
|
;; to attach state to
|
||||||
@@ -1495,9 +1652,54 @@ chmod -R 777 /opt/keys"))
|
|||||||
(with-input-from-file (string-append dir "/make-out") read-string))
|
(with-input-from-file (string-append dir "/make-out") read-string))
|
||||||
(update-user-terraform-state db user-id instance-id
|
(update-user-terraform-state db user-id instance-id
|
||||||
(if (eof-object? tf-state) "" tf-state)
|
(if (eof-object? tf-state) "" tf-state)
|
||||||
(if (eof-object? tf-state-backup) "" tf-state-backup))))))))))))
|
(if (eof-object? tf-state-backup) "" tf-state-backup))
|
||||||
|
(when exit-normal
|
||||||
|
(destroy-instance db instance-id))))))))))))
|
||||||
(redirect (conc "/destroy-success/" (alist-ref "id" (current-params) equal?)))))))
|
(redirect (conc "/destroy-success/" (alist-ref "id" (current-params) equal?)))))))
|
||||||
|
|
||||||
|
(get/widgets
|
||||||
|
("/destroy-success/:id"
|
||||||
|
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
||||||
|
(res (with-db/transaction
|
||||||
|
(lambda (db)
|
||||||
|
`((status . ,(get-most-recent-deployment-status db (session-user-id) instance-id))))))
|
||||||
|
(status (or (and (alist-ref 'status res) (string->symbol (alist-ref 'status res))) 'destroyed)))
|
||||||
|
(if (or (eq? status 'complete) (eq? status 'failed) (eq? status 'destroyed))
|
||||||
|
'()
|
||||||
|
'((meta (@ (http-equiv "refresh") (content "5")))))))
|
||||||
|
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
||||||
|
(res (with-db/transaction
|
||||||
|
(lambda (db)
|
||||||
|
`((status . ,(get-most-recent-deployment-status db (session-user-id) instance-id))
|
||||||
|
(progress . ,(get-most-recent-deployment-progress db (session-user-id) instance-id))))))
|
||||||
|
(output (with-input-from-file (string-append (deployment-directory (session-user-id)) "/make-out") read-string))
|
||||||
|
(progress (alist-ref 'progress res))
|
||||||
|
(status (alist-ref 'status res)))
|
||||||
|
`(App
|
||||||
|
(Main-Container
|
||||||
|
(VStack
|
||||||
|
(h1
|
||||||
|
,(case (string->symbol status)
|
||||||
|
((queued) "Destroy queued")
|
||||||
|
((in-progress) "Destroy in progress")
|
||||||
|
((destroyed) "Destroy complete!")
|
||||||
|
((failed) "Destroy failed")))
|
||||||
|
,@(if (eq? status 'destroyed)
|
||||||
|
'((a (@ (href "/dashboard")) "Dashboard"))
|
||||||
|
`((ul (li "generate configs: " ,(progress-status->text (alist-ref 'generate-configs progress)))
|
||||||
|
(li "custom flatcar image: " ,(progress-status->text (alist-ref 'custom-image progress)))
|
||||||
|
(li "machine create: " ,(progress-status->text (alist-ref 'machine-create progress)))
|
||||||
|
(li "cleanup previous machine: " ,(progress-status->text (alist-ref 'machine-destroy progress))))
|
||||||
|
(div
|
||||||
|
(a (@ (href "/dashboard")) "Dashboard")
|
||||||
|
,@(if (or (eq? (string->symbol status) 'complete) (eq? (string->symbol status) 'failed))
|
||||||
|
'()
|
||||||
|
" (deployment will continue in the background if you leave this page)"))
|
||||||
|
(hr)
|
||||||
|
(pre (@ (style ((overflow-x "scroll"))))
|
||||||
|
,output)))
|
||||||
|
)))))
|
||||||
|
|
||||||
(schematra-install)
|
(schematra-install)
|
||||||
|
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user