Compare commits
20 Commits
f9bf59aa56
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 12d014eda9 | |||
| 1c57b865b7 | |||
| 85ad1c9311 | |||
| 38d718533c | |||
| cf1edb5d58 | |||
| 609a315c25 | |||
| bc301d6b29 | |||
| 9f89356c85 | |||
| 2721249a89 | |||
| a64247de4e | |||
| 420a7756af | |||
| 1a0252de1a | |||
| d74e913023 | |||
| 7ffbe713e9 | |||
| 1a60f94b13 | |||
| 8173b798a8 | |||
| b492ff208f | |||
| 321578a74a | |||
| 7236c8854d | |||
| 7ee27178f9 |
12
Makefile
12
Makefile
@@ -19,6 +19,12 @@ TERRAFORM_ENV := production
|
|||||||
config_dir := ./config/
|
config_dir := ./config/
|
||||||
apps_config := $(config_dir)apps.config
|
apps_config := $(config_dir)apps.config
|
||||||
|
|
||||||
|
BUTANE := butane -d ${PWD}
|
||||||
|
|
||||||
|
ifeq ($(shell command -v docker >/dev/null 2>&1 && echo yes),yes)
|
||||||
|
BUTANE := docker run --rm --volume ${PWD}:/pwd --workdir /pwd -i quay.io/coreos/butane:latest -d /pwd
|
||||||
|
endif
|
||||||
|
|
||||||
IMAGE_DEPS :=
|
IMAGE_DEPS :=
|
||||||
|
|
||||||
COMPOSE_ENVS := $(shell if [ -d app/ ]; then find app/ -type f -name '.compose-env'; fi)
|
COMPOSE_ENVS := $(shell if [ -d app/ ]; then find app/ -type f -name '.compose-env'; fi)
|
||||||
@@ -84,7 +90,7 @@ restic-scheduled-snapshot.sh \
|
|||||||
app/.env \
|
app/.env \
|
||||||
$(config_dir)ssh-keys \
|
$(config_dir)ssh-keys \
|
||||||
app.tar.gz
|
app.tar.gz
|
||||||
cat cl.yaml | docker run --rm --volume $(config_dir)/ssh-keys:/pwd/ssh-keys --volume ${PWD}:/pwd --workdir /pwd -i quay.io/coreos/butane:latest -d /pwd > ignition.json
|
cat cl.yaml | $(BUTANE) > ignition.json
|
||||||
|
|
||||||
generated.tfvars: $(apps_config) make-generated.sh
|
generated.tfvars: $(apps_config) make-generated.sh
|
||||||
./make-generated.sh $(apps_config) > generated.tfvars
|
./make-generated.sh $(apps_config) > generated.tfvars
|
||||||
@@ -105,11 +111,11 @@ preapply: app/.dirstamp
|
|||||||
|
|
||||||
apply: announce-start restic-init ignition.json $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars
|
apply: announce-start restic-init ignition.json $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars
|
||||||
echo "NASSELLA_CONFIG: end"
|
echo "NASSELLA_CONFIG: end"
|
||||||
terraform init
|
terraform init --upgrade
|
||||||
bash -c "terraform apply -auto-approve -input=false -var-file=<(cat $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars)"
|
bash -c "terraform apply -auto-approve -input=false -var-file=<(cat $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars)"
|
||||||
|
|
||||||
destroy: ignition.json $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars
|
destroy: ignition.json $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars
|
||||||
terraform init
|
terraform init --upgrade
|
||||||
bash -c "terraform destroy -auto-approve -input=false -var-file=<(cat $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars)"
|
bash -c "terraform destroy -auto-approve -input=false -var-file=<(cat $(config_dir)$(TERRAFORM_ENV).tfvars generated.tfvars)"
|
||||||
|
|
||||||
.PHONY: restic-init
|
.PHONY: restic-init
|
||||||
|
|||||||
@@ -14,8 +14,17 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with Nassella. If not, see <https://www.gnu.org/licenses/>.
|
# along with Nassella. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
{$authelia_SUBDOMAIN}.{$ROOT_DOMAIN} {
|
#{$authelia_SUBDOMAIN}.{$ROOT_DOMAIN} {
|
||||||
import maintenance_intercept
|
# route {
|
||||||
|
# @authelia path /authelia /authelia/*
|
||||||
|
# handle @authelia {
|
||||||
|
# reverse_proxy authelia:9091
|
||||||
|
# }
|
||||||
|
# forward_auth authelia:9091 {
|
||||||
|
# uri /api/authz/forward-auth
|
||||||
|
# copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||||
|
# }
|
||||||
|
# reverse_proxy authelia:9091
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
|
||||||
reverse_proxy authelia:9091
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ IMAGE_DEPS := $(IMAGE_DEPS) \
|
|||||||
app/authelia/postgres_db \
|
app/authelia/postgres_db \
|
||||||
app/authelia/postgres_user \
|
app/authelia/postgres_user \
|
||||||
app/authelia/postgres_password \
|
app/authelia/postgres_password \
|
||||||
app/authelia/authelia-config/configuration.yml
|
app/authelia/authelia-config/configuration.yml \
|
||||||
|
app/authelia/authelia-config/users.yml
|
||||||
|
|
||||||
app/authelia/postgres_db: $(apps_config)
|
app/authelia/postgres_db: $(apps_config)
|
||||||
bash -c 'source ./$(apps_config); printf "%s\n" "$$AUTHELIA_POSTGRES_DB" > $@'
|
bash -c 'source ./$(apps_config); printf "%s\n" "$$AUTHELIA_POSTGRES_DB" > $@'
|
||||||
@@ -29,3 +30,6 @@ app/authelia/postgres_password: $(apps_config)
|
|||||||
app/authelia/authelia-config/configuration.yml: $(apps_config) app/authelia/authelia-config/configuration.yml.tmpl \
|
app/authelia/authelia-config/configuration.yml: $(apps_config) app/authelia/authelia-config/configuration.yml.tmpl \
|
||||||
app/authelia/make-authelia-config.sh
|
app/authelia/make-authelia-config.sh
|
||||||
./app/authelia/make-authelia-config.sh $(apps_config)
|
./app/authelia/make-authelia-config.sh $(apps_config)
|
||||||
|
app/authelia/authelia-config/users.yml: $(apps_config) app/authelia/authelia-config/users.yml.tmpl \
|
||||||
|
app/authelia/make-authelia-users-config.sh
|
||||||
|
./app/authelia/make-authelia-users-config.sh $(apps_config)
|
||||||
|
|||||||
@@ -35,58 +35,45 @@ identity_validation:
|
|||||||
reset_password:
|
reset_password:
|
||||||
jwt_secret: '$AUTHELIA_JWT_SECRET'
|
jwt_secret: '$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:
|
authentication_backend:
|
||||||
ldap:
|
disable_reset_password: true
|
||||||
address: 'ldap://lldap:3890'
|
file:
|
||||||
implementation: 'lldap'
|
path: '/config/users.yml'
|
||||||
timeout: '5s'
|
watch: false
|
||||||
pooling:
|
search:
|
||||||
enable: false
|
email: false
|
||||||
count: 5
|
case_insensitive: false
|
||||||
retries: 2
|
extra_attributes:
|
||||||
timeout: '10 seconds'
|
extra_example:
|
||||||
base_dn: 'DC=nassella,DC=org'
|
multi_valued: false
|
||||||
# additional_users_dn: 'OU=users'
|
value_type: 'string'
|
||||||
# additional_groups_dn: 'OU=groups'
|
password:
|
||||||
# group_search_mode: 'filter'
|
algorithm: 'argon2'
|
||||||
# permit_referrals: false
|
argon2:
|
||||||
permit_unauthenticated_bind: false
|
variant: 'argon2id'
|
||||||
permit_feature_detection_failure: false
|
iterations: 3
|
||||||
user: 'uid=admin,ou=people,dc=nassella,dc=org'
|
memory: 65536
|
||||||
password: '$LLDAP_ADMIN_PASSWORD'
|
parallelism: 4
|
||||||
# attributes:
|
key_length: 32
|
||||||
# distinguished_name: 'distinguishedName'
|
salt_length: 16
|
||||||
# username: 'uid'
|
scrypt:
|
||||||
# display_name: 'displayName'
|
variant: 'scrypt'
|
||||||
# family_name: 'sn'
|
iterations: 16
|
||||||
# given_name: 'givenName'
|
block_size: 8
|
||||||
# middle_name: 'middleName'
|
parallelism: 1
|
||||||
# nickname: ''
|
key_length: 32
|
||||||
# gender: ''
|
salt_length: 16
|
||||||
# birthdate: ''
|
pbkdf2:
|
||||||
# website: 'wWWHomePage'
|
variant: 'sha512'
|
||||||
# profile: ''
|
iterations: 310000
|
||||||
# picture: ''
|
salt_length: 16
|
||||||
# zoneinfo: ''
|
sha2crypt:
|
||||||
# locale: ''
|
variant: 'sha512'
|
||||||
# phone_number: 'telephoneNumber'
|
iterations: 50000
|
||||||
# phone_extension: ''
|
salt_length: 16
|
||||||
# street_address: 'streetAddress'
|
bcrypt:
|
||||||
# locality: 'l'
|
variant: 'standard'
|
||||||
# region: 'st'
|
cost: 12
|
||||||
# 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:
|
access_control:
|
||||||
default_policy: 'deny'
|
default_policy: 'deny'
|
||||||
@@ -102,11 +89,12 @@ session:
|
|||||||
secret: '$AUTHELIA_SESSION_SECRET'
|
secret: '$AUTHELIA_SESSION_SECRET'
|
||||||
|
|
||||||
cookies:
|
cookies:
|
||||||
- name: 'authelia_session'
|
- name: 'dozzle_session'
|
||||||
domain: '$ROOT_DOMAIN' # Should match whatever your root protected domain is
|
domain: '$DOZZLE_FULL_DOMAIN' # Should match whatever your root protected domain is
|
||||||
authelia_url: 'https://$AUTHELIA_FULL_DOMAIN'
|
authelia_url: 'https://$DOZZLE_FULL_DOMAIN/authelia'
|
||||||
expiration: '1 hour' # 1 hour
|
expiration: '1 hour' # 1 hour
|
||||||
inactivity: '5 minutes' # 5 minutes
|
inactivity: '5 minutes' # 5 minutes
|
||||||
|
default_redirection_url: 'https://$DOZZLE_FULL_DOMAIN/'
|
||||||
|
|
||||||
regulation:
|
regulation:
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
|
|||||||
7
all-apps/authelia/4/authelia-config/users.yml.tmpl
Normal file
7
all-apps/authelia/4/authelia-config/users.yml.tmpl
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# yaml-language-server: $schema=https://www.authelia.com/schemas/latest/json-schema/user-database.json
|
||||||
|
users:
|
||||||
|
admin:
|
||||||
|
disabled: false
|
||||||
|
displayname: 'Admin'
|
||||||
|
password: '$HOST_ADMIN_PASSWORD'
|
||||||
|
email: '$HOST_ADMIN_USER'
|
||||||
@@ -50,13 +50,11 @@ services:
|
|||||||
image: 'authelia/authelia'
|
image: 'authelia/authelia'
|
||||||
volumes:
|
volumes:
|
||||||
- ./authelia/authelia-config/configuration.yml:/config/configuration.yml:ro
|
- ./authelia/authelia-config/configuration.yml:/config/configuration.yml:ro
|
||||||
|
- ./authelia/authelia-config/users.yml:/config/users.yml:ro
|
||||||
networks:
|
networks:
|
||||||
- lb
|
- lb
|
||||||
- authelia_internal_db
|
- authelia_internal_db
|
||||||
- lldap_internal
|
|
||||||
depends_on:
|
depends_on:
|
||||||
lldap:
|
|
||||||
condition: service_healthy
|
|
||||||
authelia_db:
|
authelia_db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
restart: 'unless-stopped'
|
restart: 'unless-stopped'
|
||||||
@@ -66,9 +64,6 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
lb:
|
lb:
|
||||||
lldap_internal:
|
|
||||||
driver: bridge
|
|
||||||
internal: true
|
|
||||||
authelia_internal_db:
|
authelia_internal_db:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
internal: true
|
internal: true
|
||||||
|
|||||||
24
all-apps/authelia/4/make-authelia-users-config.sh
Executable file
24
all-apps/authelia/4/make-authelia-users-config.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Copyright 2025-2026 Thomas Hintz
|
||||||
|
|
||||||
|
# This file is part of Nassella.
|
||||||
|
|
||||||
|
# Nassella is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
# later version.
|
||||||
|
|
||||||
|
# Nassella is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Nassella. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -a # export everything in the config for later use by envsubst
|
||||||
|
|
||||||
|
. $1 # source the apps.config file with then env vars
|
||||||
|
|
||||||
|
envsubst < app/authelia/authelia-config/users.yml.tmpl > app/authelia/authelia-config/users.yml
|
||||||
@@ -15,9 +15,15 @@
|
|||||||
# along with Nassella. If not, see <https://www.gnu.org/licenses/>.
|
# along with Nassella. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
{$dozzle_SUBDOMAIN}.{$ROOT_DOMAIN} {
|
{$dozzle_SUBDOMAIN}.{$ROOT_DOMAIN} {
|
||||||
forward_auth authelia:9091 {
|
route {
|
||||||
uri /api/authz/forward-auth
|
@authelia path /authelia /authelia/*
|
||||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
handle @authelia {
|
||||||
|
reverse_proxy authelia:9091
|
||||||
|
}
|
||||||
|
forward_auth authelia:9091 {
|
||||||
|
uri /api/authz/forward-auth
|
||||||
|
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||||
|
}
|
||||||
|
reverse_proxy http://dozzle:8080
|
||||||
}
|
}
|
||||||
reverse_proxy http://dozzle:8080
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ app/nassella/lldap_postgres_password \
|
|||||||
app/nassella/authelia_postgres_db \
|
app/nassella/authelia_postgres_db \
|
||||||
app/nassella/authelia_postgres_user \
|
app/nassella/authelia_postgres_user \
|
||||||
app/nassella/authelia_postgres_password \
|
app/nassella/authelia_postgres_password \
|
||||||
|
app/nassella/lldap_admin_password \
|
||||||
|
app/nassella/stripe_api_key \
|
||||||
app/nassella/nassella.env \
|
app/nassella/nassella.env \
|
||||||
app/nassella/authelia-config/configuration.yml \
|
app/nassella/authelia-config/configuration.yml \
|
||||||
app/nassella/lldap-config/lldap_config.toml
|
app/nassella/lldap-config/lldap_config.toml
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ access_control:
|
|||||||
policy: 'two_factor'
|
policy: 'two_factor'
|
||||||
|
|
||||||
session:
|
session:
|
||||||
secret: 'insecure_session_secret'
|
secret: 'insecure_session_secret' # TODO fix
|
||||||
|
|
||||||
cookies:
|
cookies:
|
||||||
- name: 'authelia_session'
|
- name: 'authelia_session'
|
||||||
|
|||||||
2
cl.yaml
2
cl.yaml
@@ -20,7 +20,7 @@ passwd:
|
|||||||
users:
|
users:
|
||||||
- name: core
|
- name: core
|
||||||
ssh_authorized_keys_local:
|
ssh_authorized_keys_local:
|
||||||
- /ssh-keys
|
- config/ssh-keys
|
||||||
- name: nextcloud
|
- name: nextcloud
|
||||||
uid: 1001
|
uid: 1001
|
||||||
systemd:
|
systemd:
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ if [ $? -ne 0 ]; then
|
|||||||
|
|
||||||
if [ $status -eq 10 ]; then
|
if [ $status -eq 10 ]; then
|
||||||
# restic repo is not initialized so initialize it
|
# restic repo is not initialized so initialize it
|
||||||
AWS_ACCESS_KEY_ID="$BACKBLAZE_KEY_ID" -e AWS_SECRET_ACCESS_KEY="$BACKBLAZE_APPLICATION_KEY" restic init --repo s3:$BACKBLAZE_BUCKET_URL --password-file restic-password
|
AWS_ACCESS_KEY_ID="$BACKBLAZE_KEY_ID" AWS_SECRET_ACCESS_KEY="$BACKBLAZE_APPLICATION_KEY" restic init --repo s3:$BACKBLAZE_BUCKET_URL --password-file restic-password
|
||||||
init_status=$?
|
init_status=$?
|
||||||
elif [ $status -ne 0 ]; then
|
elif [ $status -ne 0 ]; then
|
||||||
# something unexpected happened, exit
|
# something unexpected happened, exit
|
||||||
|
|||||||
28
main.tf
28
main.tf
@@ -2,23 +2,19 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
digitalocean = {
|
digitalocean = {
|
||||||
source = "digitalocean/digitalocean"
|
source = "digitalocean/digitalocean"
|
||||||
version = "2.62.0"
|
version = "2.96.0"
|
||||||
}
|
}
|
||||||
ct = {
|
ct = {
|
||||||
source = "poseidon/ct"
|
source = "poseidon/ct"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
}
|
}
|
||||||
null = {
|
null = {
|
||||||
source = "hashicorp/null"
|
source = "hashicorp/null"
|
||||||
version = "3.2.4"
|
version = "3.3.0"
|
||||||
}
|
|
||||||
time = {
|
|
||||||
source = "hashicorp/time"
|
|
||||||
version = "0.13.1"
|
|
||||||
}
|
}
|
||||||
cloudflare = {
|
cloudflare = {
|
||||||
source = "cloudflare/cloudflare"
|
source = "cloudflare/cloudflare"
|
||||||
version = "5.8.2"
|
version = "5.22.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,6 +82,10 @@ variable "digitalocean_volume_size" {
|
|||||||
|
|
||||||
provider "digitalocean" {
|
provider "digitalocean" {
|
||||||
token = var.do_token
|
token = var.do_token
|
||||||
|
|
||||||
|
http_retry_max = 10
|
||||||
|
http_retry_wait_min = 2
|
||||||
|
http_retry_wait_max = 60
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "cloudflare" {
|
provider "cloudflare" {
|
||||||
@@ -142,21 +142,11 @@ resource "digitalocean_droplet" "machine" {
|
|||||||
size = var.server_type
|
size = var.server_type
|
||||||
ssh_keys = [digitalocean_ssh_key.first.fingerprint]
|
ssh_keys = [digitalocean_ssh_key.first.fingerprint]
|
||||||
user_data = file("ignition.json")
|
user_data = file("ignition.json")
|
||||||
lifecycle {
|
|
||||||
create_before_destroy = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "time_sleep" "wait_10_seconds" {
|
|
||||||
create_duration = "10s"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "digitalocean_reserved_ip_assignment" "machine" {
|
resource "digitalocean_reserved_ip_assignment" "machine" {
|
||||||
|
depends_on = [digitalocean_volume_attachment.machine]
|
||||||
ip_address = digitalocean_reserved_ip.machine.ip_address
|
ip_address = digitalocean_reserved_ip.machine.ip_address
|
||||||
droplet_id = digitalocean_droplet.machine.id
|
droplet_id = digitalocean_droplet.machine.id
|
||||||
# sometimes digital ocean throws an error for this resource
|
|
||||||
# saying the droplet has a pending event so we delay a few seconds
|
|
||||||
depends_on = [time_sleep.wait_10_seconds]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "digitalocean_volume_attachment" "machine" {
|
resource "digitalocean_volume_attachment" "machine" {
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ set -e
|
|||||||
|
|
||||||
. $1 # source the apps.config file with then env vars
|
. $1 # source the apps.config file with then env vars
|
||||||
|
|
||||||
host_admin_password_encoded=`echo "$HOST_ADMIN_PASSWORD" | docker run --rm -i caddy:2 caddy hash-password`
|
# host_admin_password_encoded=`echo "$HOST_ADMIN_PASSWORD" | docker run --rm -i caddy:2 caddy hash-password`
|
||||||
|
host_admin_password_encoded="password" # unused
|
||||||
|
|
||||||
read -r -a APP_CONFIGS <<< "$APP_CONFIGS"
|
read -r -a APP_CONFIGS <<< "$APP_CONFIGS"
|
||||||
APP_CONFIGS+=('lb,root')
|
APP_CONFIGS+=('lb,root')
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ RUN chicken-install srfi-1 srfi-13 srfi-18 srfi-19 srfi-158 srfi-194 \
|
|||||||
sxml-transforms schematra \
|
sxml-transforms schematra \
|
||||||
uri-common http-client medea intarweb \
|
uri-common http-client medea intarweb \
|
||||||
sql-null openssl postgresql crypto-tools \
|
sql-null openssl postgresql crypto-tools \
|
||||||
hmac sha2 string-utils
|
hmac sha2 string-utils base64
|
||||||
|
|
||||||
WORKDIR /var
|
WORKDIR /var
|
||||||
RUN mkdir nassella
|
RUN mkdir nassella
|
||||||
@@ -43,10 +43,19 @@ COPY run.scm run.scm
|
|||||||
RUN csc -O3 -o nassella-run run.scm
|
RUN csc -O3 -o nassella-run run.scm
|
||||||
RUN chmod +x nassella-run
|
RUN chmod +x nassella-run
|
||||||
|
|
||||||
|
FROM quay.io/coreos/butane:release AS butane
|
||||||
|
FROM hashicorp/terraform:1.15 AS terraform
|
||||||
|
FROM authelia/authelia:latest AS authelia
|
||||||
|
|
||||||
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 ca-certificates gettext-base openssh-client restic \
|
libpq-dev ca-certificates gettext-base openssh-client restic make \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=butane /usr/local/bin/butane /usr/local/bin/butane
|
||||||
|
COPY --from=terraform /bin/terraform /usr/local/bin/terraform
|
||||||
|
COPY --from=authelia /app/authelia /usr/local/bin/authelia
|
||||||
|
|
||||||
COPY --from=buildeggs /usr/local/ /usr/local/
|
COPY --from=buildeggs /usr/local/ /usr/local/
|
||||||
|
|
||||||
WORKDIR /var
|
WORKDIR /var
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ create table user_service_configs(
|
|||||||
digitalocean_volume_size integer,
|
digitalocean_volume_size integer,
|
||||||
backblaze_application_key_enc varchar(255),
|
backblaze_application_key_enc varchar(255),
|
||||||
backblaze_key_id_enc varchar(255),
|
backblaze_key_id_enc varchar(255),
|
||||||
backblaze_bucket_url_enc varchar(255)
|
backblaze_bucket_url_enc varchar(255), -- deprecated 072526, replaced by bucket_name and bucket_id
|
||||||
|
backblaze_bucket_name_enc varchar(255),
|
||||||
|
backblaze_bucket_id_enc varchar(255)
|
||||||
);
|
);
|
||||||
create unique index user_service_configs_user_id_instance_id_idx on user_service_configs (user_id, instance_id);
|
create unique index user_service_configs_user_id_instance_id_idx on user_service_configs (user_id, instance_id);
|
||||||
|
|
||||||
@@ -62,6 +64,8 @@ insert into apps(app_name) values ('log-viewer');
|
|||||||
insert into apps(app_name) values ('ghost');
|
insert into apps(app_name) values ('ghost');
|
||||||
insert into apps(app_name) values ('instance-control');
|
insert into apps(app_name) values ('instance-control');
|
||||||
insert into apps(app_name) values ('wordpress');
|
insert into apps(app_name) values ('wordpress');
|
||||||
|
insert into apps(app_name) values ('lldap');
|
||||||
|
insert into apps(app_name) values ('authelia');
|
||||||
|
|
||||||
create table user_selected_apps(
|
create table user_selected_apps(
|
||||||
id bigserial primary key,
|
id bigserial primary key,
|
||||||
@@ -89,7 +93,7 @@ create table user_app_configs(
|
|||||||
create unique index user_app_configs_user_id_instance_id_idx on user_app_configs (user_id, instance_id);
|
create unique index user_app_configs_user_id_instance_id_idx on user_app_configs (user_id, instance_id);
|
||||||
|
|
||||||
|
|
||||||
create type deployment_status as enum ('queued', 'in-progress', 'complete', 'failed');
|
create type deployment_status as enum ('ignored', 'queued', 'in-progress', 'complete', 'failed');
|
||||||
|
|
||||||
create table deployments(
|
create table deployments(
|
||||||
id bigserial primary key,
|
id bigserial primary key,
|
||||||
|
|||||||
88
src/db.scm
88
src/db.scm
@@ -197,7 +197,6 @@ returning users.user_id;"
|
|||||||
(user-encrypt-for-db ssh-key-pub user-key user-iv user-id)
|
(user-encrypt-for-db ssh-key-pub user-key user-iv user-id)
|
||||||
(user-encrypt-for-db restic-password user-key user-iv user-id)))))
|
(user-encrypt-for-db restic-password user-key user-iv user-id)))))
|
||||||
(query conn "insert into user_service_configs(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
(query conn "insert into user_service_configs(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
||||||
(query conn "insert into user_selected_apps(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
|
||||||
(query conn "insert into user_app_configs(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
(query conn "insert into user_app_configs(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)
|
(query conn "insert into user_terraform_state(user_id, instance_id) values ($1, $2);" user-id instance-id)
|
||||||
instance-id)))
|
instance-id)))
|
||||||
@@ -249,7 +248,9 @@ returning users.user_id;"
|
|||||||
(digitalocean-volume-size . ("digitalocean_volume_size" #f))
|
(digitalocean-volume-size . ("digitalocean_volume_size" #f))
|
||||||
(backblaze-application-key . ("backblaze_application_key_enc" #t))
|
(backblaze-application-key . ("backblaze_application_key_enc" #t))
|
||||||
(backblaze-key-id . ("backblaze_key_id_enc" #t))
|
(backblaze-key-id . ("backblaze_key_id_enc" #t))
|
||||||
(backblaze-bucket-url . ("backblaze_bucket_url_enc" #t))))
|
(backblaze-bucket-url . ("backblaze_bucket_url_enc" #t))
|
||||||
|
(backblaze-bucket-name . ("backblaze_bucket_name_enc" #t))
|
||||||
|
(backblaze-bucket-id . ("backblaze_bucket_id_enc" #t))))
|
||||||
|
|
||||||
(define *user-service-configs-reverse-column-map*
|
(define *user-service-configs-reverse-column-map*
|
||||||
(map (lambda (config)
|
(map (lambda (config)
|
||||||
@@ -377,7 +378,8 @@ where usa.user_id=$1 and usa.instance_id=$2;"
|
|||||||
'((queued . "queued")
|
'((queued . "queued")
|
||||||
(in-progress . "in-progress")
|
(in-progress . "in-progress")
|
||||||
(complete . "complete")
|
(complete . "complete")
|
||||||
(failed . "failed")))
|
(failed . "failed")
|
||||||
|
(ignored . "ignored")))
|
||||||
(define (create-deployment conn user-id instance-id)
|
(define (create-deployment conn user-id instance-id)
|
||||||
(value-at
|
(value-at
|
||||||
(query conn
|
(query conn
|
||||||
@@ -415,6 +417,7 @@ where usa.user_id=$1 and usa.instance_id=$2;"
|
|||||||
(volume-create . "terraform_volume_create")
|
(volume-create . "terraform_volume_create")
|
||||||
(volume-destroy . "terraform_volume_destroy")
|
(volume-destroy . "terraform_volume_destroy")
|
||||||
(instance-backup . "instance_backup")
|
(instance-backup . "instance_backup")
|
||||||
|
(instance-up . "instance_up")
|
||||||
(status . "status")
|
(status . "status")
|
||||||
(id . "id")
|
(id . "id")
|
||||||
(instance-id . "instance_id")))
|
(instance-id . "instance_id")))
|
||||||
@@ -516,37 +519,51 @@ where usa.user_id=$1 and usa.instance_id=$2;"
|
|||||||
"select apps.app_name, usa.installed_version, usa.instance_id from user_selected_apps usa
|
"select apps.app_name, usa.installed_version, usa.instance_id from user_selected_apps usa
|
||||||
join apps on apps.id=usa.app_id
|
join apps on apps.id=usa.app_id
|
||||||
where user_id=$1;"
|
where user_id=$1;"
|
||||||
user-id))))
|
user-id)))
|
||||||
(map
|
(undeployed-res
|
||||||
(lambda (instance)
|
(query conn
|
||||||
(cons `(apps . ,(alist-ref (alist-ref 'instance-id instance) instance-apps))
|
(string-append
|
||||||
instance))
|
"select i.instance_id from instances as i "
|
||||||
|
"left join deployments d on d.instance_id = i.instance_id "
|
||||||
|
"where i.user_id=$1 and d.status is null "
|
||||||
|
"group by d.id, i.instance_id;")
|
||||||
|
user-id)))
|
||||||
|
(append
|
||||||
(map
|
(map
|
||||||
(lambda (row-num)
|
(lambda (instance)
|
||||||
(map (lambda (item)
|
(cons `(apps . ,(alist-ref (alist-ref 'instance-id instance) instance-apps))
|
||||||
(let* ((key (car item))
|
instance))
|
||||||
(value (cdr item))
|
(map
|
||||||
(config (alist-ref key `((root_domain . root-domain)
|
(lambda (row-num)
|
||||||
(config_enc . config)
|
(cons '(deployed . #t)
|
||||||
(instance_id . instance-id)
|
(map (lambda (item)
|
||||||
;; (wg_easy_version . wg-easy)
|
(let* ((key (car item))
|
||||||
;; (nextcloud_version . nextcloud)
|
(value (cdr item))
|
||||||
;; (ghost_version . ghost)
|
(config (alist-ref key `((root_domain . root-domain)
|
||||||
;; (nassella_version . nassella)
|
(config_enc . config)
|
||||||
;; (log_viewer_version . log-viewer)
|
(instance_id . instance-id)
|
||||||
;; (instance_control_version . instance-control)
|
;; (wg_easy_version . wg-easy)
|
||||||
,@*deployments-reverse-column-map*))))
|
;; (nextcloud_version . nextcloud)
|
||||||
`(,config . ,(if (sql-null? value)
|
;; (ghost_version . ghost)
|
||||||
#f
|
;; (nassella_version . nassella)
|
||||||
(if (and (string? value) (member config *deployments-column-map*))
|
;; (log_viewer_version . log-viewer)
|
||||||
(string->symbol value)
|
;; (instance_control_version . instance-control)
|
||||||
(if (eq? key 'config_enc)
|
,@*deployments-reverse-column-map*))))
|
||||||
(with-input-from-string
|
`(,config . ,(if (sql-null? value)
|
||||||
(user-decrypt-from-db value user-key user-iv user-id)
|
#f
|
||||||
read)
|
(if (and (string? value) (member config *deployments-column-map*))
|
||||||
value))))))
|
(string->symbol value)
|
||||||
(row-alist res row-num)))
|
(if (eq? key 'config_enc)
|
||||||
(iota (row-count res)))))))
|
(with-input-from-string
|
||||||
|
(user-decrypt-from-db value user-key user-iv user-id)
|
||||||
|
read)
|
||||||
|
value))))))
|
||||||
|
(row-alist res row-num))))
|
||||||
|
(iota (row-count res))))
|
||||||
|
(map (lambda (id) `((instance-id . ,id) (deployed . #f)))
|
||||||
|
(if (> (row-count undeployed-res) 0)
|
||||||
|
(row-values undeployed-res)
|
||||||
|
'()))))))
|
||||||
|
|
||||||
(define (update-user-terraform-state conn user-id instance-id state backup)
|
(define (update-user-terraform-state conn user-id instance-id state backup)
|
||||||
(receive (user-key user-iv auth-user-id)
|
(receive (user-key user-iv auth-user-id)
|
||||||
@@ -644,7 +661,10 @@ where user_id=$1;"
|
|||||||
(4 . "fixing-app-normalization")
|
(4 . "fixing-app-normalization")
|
||||||
(5 . "adding-wordpress-app")
|
(5 . "adding-wordpress-app")
|
||||||
(6 . "adding-lldap-app")
|
(6 . "adding-lldap-app")
|
||||||
(7 . "adding-authelia-app")))
|
(7 . "adding-authelia-app")
|
||||||
|
(8 . "adding-backblaze-bucket-name-and-id")
|
||||||
|
(9 . "extend-deployment-status-add-ignored")
|
||||||
|
(10 . "adding-deployments-instance-up")))
|
||||||
|
|
||||||
(define (run-pending-migrations conn)
|
(define (run-pending-migrations conn)
|
||||||
(let* ((migration-ids (sort (map car *migrations*) <))
|
(let* ((migration-ids (sort (map car *migrations*) <))
|
||||||
|
|||||||
1
src/migrations/10-adding-deployments-instance-up-up.sql
Normal file
1
src/migrations/10-adding-deployments-instance-up-up.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
alter table deployments add instance_up deployment_status not null default 'queued';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table user_service_configs add backblaze_bucket_name_enc varchar(255);
|
||||||
|
alter table user_service_configs add backblaze_bucket_id_enc varchar(255);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter type deployment_status add value 'ignored';
|
||||||
1371
src/nassella.scm
1371
src/nassella.scm
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user