Merge branch 'refactor/tide-discourse'
This commit is contained in:
commit
5406f8c991
11 changed files with 59 additions and 419 deletions
|
|
@ -1,139 +0,0 @@
|
|||
# Discourse State and Database Recovery
|
||||
|
||||
This restores Discourse from the BorgBase backup configured in `discourse.nix`.
|
||||
It replaces the current Discourse state directory and PostgreSQL database with
|
||||
the selected Borg archive.
|
||||
|
||||
The backup contains:
|
||||
|
||||
- `/var/lib/discourse`
|
||||
- `/var/backup/discourse/discourse.dump`
|
||||
|
||||
## Requirements
|
||||
|
||||
- BorgBase repo URL:
|
||||
`ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo`
|
||||
- Decrypted Borg SSH private key from the flash drive
|
||||
- Borg repository passphrase from the flash drive
|
||||
- A target server that has already been switched to this NixOS config
|
||||
- A shell with `borg`, `openssh`, `postgresql`, and `rsync`
|
||||
|
||||
On NixOS or another machine with Nix:
|
||||
|
||||
```sh
|
||||
nix-shell -p borgbackup openssh postgresql rsync
|
||||
```
|
||||
|
||||
## Prepare Borg Access
|
||||
|
||||
Copy the Borg SSH key into a local recovery directory:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/borg-recovery/discourse
|
||||
cp /path/to/flash/borg-private-key ~/borg-recovery/discourse/borg-private-key
|
||||
chmod 600 ~/borg-recovery/discourse/borg-private-key
|
||||
```
|
||||
|
||||
Set the Borg connection environment:
|
||||
|
||||
```sh
|
||||
export BORG_REPO='ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo'
|
||||
export BORG_RSH='ssh -i ~/borg-recovery/discourse/borg-private-key -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new'
|
||||
```
|
||||
|
||||
Read the Borg passphrase without showing it on screen:
|
||||
|
||||
```sh
|
||||
read -rsp 'Borg passphrase: ' BORG_PASSPHRASE
|
||||
export BORG_PASSPHRASE
|
||||
echo
|
||||
```
|
||||
|
||||
## Extract a Backup
|
||||
|
||||
List available archives:
|
||||
|
||||
```sh
|
||||
borg list
|
||||
```
|
||||
|
||||
Choose an archive name from the list, then extract only the Discourse state and
|
||||
database dump into a temporary directory. Do not extract directly into `/`.
|
||||
|
||||
```sh
|
||||
export ARCHIVE='ARCHIVE_NAME_FROM_BORG_LIST'
|
||||
|
||||
mkdir -p ~/borg-recovery/discourse/extract
|
||||
cd ~/borg-recovery/discourse/extract
|
||||
|
||||
borg extract ::$ARCHIVE var/lib/discourse var/backup/discourse/discourse.dump
|
||||
```
|
||||
|
||||
## Restore on the Server
|
||||
|
||||
Run these commands from `~/borg-recovery/discourse/extract` on the target
|
||||
server. They assume the NixOS config has already created the `discourse` user,
|
||||
`postgresql.service`, and `discourse-postgresql.service`.
|
||||
|
||||
Capture absolute tool paths so they still work through `sudo`:
|
||||
|
||||
```sh
|
||||
RSYNC="$(command -v rsync)"
|
||||
PSQL="$(command -v psql)"
|
||||
DROPDB="$(command -v dropdb)"
|
||||
PG_RESTORE="$(command -v pg_restore)"
|
||||
```
|
||||
|
||||
Stop Discourse and make sure PostgreSQL is running:
|
||||
|
||||
```sh
|
||||
sudo systemctl stop discourse.service
|
||||
sudo systemctl start postgresql.service
|
||||
```
|
||||
|
||||
Restore `/var/lib/discourse`:
|
||||
|
||||
```sh
|
||||
sudo "$RSYNC" -a --delete ./var/lib/discourse/ /var/lib/discourse/
|
||||
sudo chown -R discourse:discourse /var/lib/discourse
|
||||
```
|
||||
|
||||
Replace the `discourse` PostgreSQL database:
|
||||
|
||||
```sh
|
||||
sudo -u postgres "$PSQL" -d postgres -v ON_ERROR_STOP=1 \
|
||||
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'discourse';"
|
||||
|
||||
sudo -u postgres "$DROPDB" --if-exists discourse
|
||||
sudo systemctl restart discourse-postgresql.service
|
||||
|
||||
sudo -u postgres "$PG_RESTORE" \
|
||||
--exit-on-error \
|
||||
--no-owner \
|
||||
--role=discourse \
|
||||
--dbname=discourse \
|
||||
./var/backup/discourse/discourse.dump
|
||||
|
||||
sudo systemctl restart discourse-postgresql.service
|
||||
```
|
||||
|
||||
Start Discourse:
|
||||
|
||||
```sh
|
||||
sudo systemctl start discourse.service
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
Check the service and recent logs:
|
||||
|
||||
```sh
|
||||
sudo systemctl status discourse.service --no-pager
|
||||
sudo journalctl -u discourse.service -b --no-pager -n 100
|
||||
```
|
||||
|
||||
If networking and DNS are already restored, open:
|
||||
|
||||
```text
|
||||
https://discourse.torahimderecheretz.com
|
||||
```
|
||||
131
discourse.nix
131
discourse.nix
|
|
@ -1,131 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.discourse = {
|
||||
enable = true;
|
||||
admin = {
|
||||
email = "yisroel.d.baum@gmail.com";
|
||||
fullName = "Yisroel Baum";
|
||||
passwordFile = config.sops.secrets."discourse-admin-password".path;
|
||||
username = "yisroeldbaum";
|
||||
};
|
||||
database.ignorePostgresqlVersion = true;
|
||||
hostname = "discourse.torahimderecheretz.com";
|
||||
mail = {
|
||||
notificationEmailAddress = "system@torahimderecheretz.com";
|
||||
contactEmailAddress = "system@torahimderecheretz.com";
|
||||
|
||||
outgoing = {
|
||||
serverAddress = "in-v3.mailjet.com";
|
||||
port = 587;
|
||||
username = "1ebdd73d944240c1a9c3201e52a1e8c6";
|
||||
passwordFile = config.sops.secrets."discourse-mail-key".path;
|
||||
authentication = "login";
|
||||
};
|
||||
};
|
||||
secretKeyBaseFile = config.sops.secrets."discourse-secret-key".path;
|
||||
siteSettings = {
|
||||
required = {
|
||||
title = "Torah Im Derech Eretz";
|
||||
site_description = "A forum to discuss the ideas of Rav Shamshon Refael Hirsch";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.borgbackup.jobs.discourse = {
|
||||
paths = [
|
||||
"/var/lib/discourse"
|
||||
"/var/backup/discourse/discourse.dump"
|
||||
];
|
||||
repo = "ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo";
|
||||
user = "root";
|
||||
group = "root";
|
||||
encryption = {
|
||||
mode = "repokey-blake2";
|
||||
passCommand = "${pkgs.coreutils}/bin/cat ${config.sops.secrets."borg-passphrase".path}";
|
||||
};
|
||||
doInit = true;
|
||||
compression = "auto,zstd";
|
||||
startAt = "*-*-* 07:15:00";
|
||||
persistentTimer = true;
|
||||
prune.keep = {
|
||||
daily = 7;
|
||||
weekly = 4;
|
||||
monthly = 6;
|
||||
};
|
||||
readWritePaths = [ "/var/backup/discourse" ];
|
||||
environment.BORG_RSH = "ssh -i ${config.sops.secrets."borg-private-key".path} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.config/borg/known_hosts";
|
||||
preHook = ''
|
||||
discourseWasActive=/root/.cache/borg/discourse-was-active
|
||||
dumpFile=/var/backup/discourse/discourse.dump
|
||||
|
||||
if ${pkgs.systemd}/bin/systemctl is-active --quiet discourse.service; then
|
||||
touch "$discourseWasActive"
|
||||
${pkgs.systemd}/bin/systemctl stop discourse.service
|
||||
else
|
||||
rm -f "$discourseWasActive"
|
||||
fi
|
||||
|
||||
rm -f "$dumpFile"
|
||||
umask 077
|
||||
${lib.getExe' pkgs.su "su"} -s ${pkgs.runtimeShell} ${config.services.postgresql.superUser} -c '${config.services.postgresql.package}/bin/pg_dump --format=custom --dbname=${config.services.discourse.database.name}' > "$dumpFile"
|
||||
'';
|
||||
postHook = ''
|
||||
discourseWasActive=/root/.cache/borg/discourse-was-active
|
||||
dumpFile=/var/backup/discourse/discourse.dump
|
||||
|
||||
rm -f "$dumpFile"
|
||||
|
||||
if [ -e "$discourseWasActive" ]; then
|
||||
${pkgs.systemd}/bin/systemctl start discourse.service
|
||||
rm -f "$discourseWasActive"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/backup/discourse 0700 root root - -"
|
||||
];
|
||||
|
||||
systemd.services.borgbackup-job-discourse = {
|
||||
after = [
|
||||
"sops-install-secrets.service"
|
||||
"network-online.target"
|
||||
"postgresql.target"
|
||||
"discourse-postgresql.service"
|
||||
];
|
||||
wants = [
|
||||
"network-online.target"
|
||||
"postgresql.target"
|
||||
"discourse-postgresql.service"
|
||||
];
|
||||
};
|
||||
|
||||
sops.secrets = {
|
||||
"discourse-admin-password" = {
|
||||
sopsFile = ./secrets/discourseAdminPassword.yaml;
|
||||
mode = "0400";
|
||||
key = "password";
|
||||
owner = "discourse";
|
||||
group = "discourse";
|
||||
};
|
||||
"discourse-secret-key" = {
|
||||
sopsFile = ./secrets/discourseSecretKey.yaml;
|
||||
mode = "0400";
|
||||
key = "key";
|
||||
owner = "discourse";
|
||||
group = "discourse";
|
||||
};
|
||||
"discourse-mail-key" = {
|
||||
sopsFile = ./secrets/discourseMailKey.yaml;
|
||||
mode = "0400";
|
||||
key = "key";
|
||||
owner = "discourse";
|
||||
group = "discourse";
|
||||
};
|
||||
};
|
||||
}
|
||||
44
flake.lock
generated
44
flake.lock
generated
|
|
@ -122,34 +122,18 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"tide": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"utils": "utils_2"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1778485449,
|
||||
"narHash": "sha256-Y8p1JKfU7I1T33qI7F6zXobtQqLqry4NVBb2eAlnBqc=",
|
||||
"lastModified": 1784446675,
|
||||
"narHash": "sha256-r7X6qZeRLdOskfw3Ktz9VbatkHu/4Tb0wjpYx8AVZdc=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "ae9dd787a6ac64c300251cf047274dd76af6d2d6",
|
||||
"revCount": 137,
|
||||
"rev": "21cca99e9e06f7aec0d14ba782cecad5e56ab098",
|
||||
"revCount": 140,
|
||||
"type": "git",
|
||||
"url": "https://git.yisroelbaum.com/yisroelbaum/TIDE"
|
||||
},
|
||||
|
|
@ -175,24 +159,6 @@
|
|||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@
|
|||
nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
|
||||
modules = [
|
||||
./configuration.nix
|
||||
./discourse.nix
|
||||
./tide.nix
|
||||
./rabbi-gerzi.nix
|
||||
home-manager.nixosModules.home-manager
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ host's age key.
|
|||
```
|
||||
sudo grep "public key" /var/lib/sops-nix/key.txt
|
||||
```
|
||||
3. On a workstation, put that public key into `.sops.yaml` at
|
||||
the repo root and encrypt `tide.yaml.example` into
|
||||
`tide.yaml`.
|
||||
3. On a workstation, put that public key into `.sops.yaml` at the repository
|
||||
root and encrypt the required secret files.
|
||||
|
||||
`tide.yaml` is encrypted and committed. `tide.yaml.example` is
|
||||
the plaintext template.
|
||||
## TIDE
|
||||
|
||||
Create `tide.yaml` from `tide.yaml.example` and encrypt it with sops. It must
|
||||
contain `admin-password`, `mail-key`, and `secret-key`. The encrypted file is
|
||||
committed; the example contains placeholders only.
|
||||
|
||||
## Rabbi Gerzi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
password: ENC[AES256_GCM,data:6/a9U7I2joMSkfiysYgMrBN3AE3DRUjQsZw=,iv:EDeN/IClY/yNkVi/FLiTeeOjkj5Df/1l4XXzf5STot0=,tag:8uoXPXJuL4JlMGzgtK6D+w==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvYWtUZ2JYU3VNaHQ1bXVL
|
||||
V0ZmRVIxVDg2SE5GcU5UMTZjcnVOUlBZOHlzCjc5elV3bk54ak05WStvNGUvNCtP
|
||||
aVNJT0tFTVFMb2NKTlpValJ6YWFVcEEKLS0tIDlEUjRncWFsUGptZTBmSm1idm0z
|
||||
ZUgxUHhvejMreHUrbDVwTkxwOE1DSkUK62qiTlgQvKruDj3Bh/8nJH+5CBl+fzbJ
|
||||
q5ryzSfTpEcSfBwRENqIOYMOTjzr+y+JGymjokc9rCYb0NPQUFpHzg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-02T18:00:59Z"
|
||||
mac: ENC[AES256_GCM,data:H/+voyLLpsGxgTNjN96PL+I1tpW8Ylo+mWVTbsaUMnnOZIkfA81MzEjtdWOs0Pg4AysAeMbmJUob+gTtSfcqnVvhv/FBVHuPji+Vz0cj0TM8k7oLlaIgeL/MAZ1mhrxDIUK5pQYSVnaaWrQ43Z4DeqaboDY7feUOp8n6emICo5I=,iv:dCPOGBC80ZtM0ObeRRMAGH2f4K5zFtckaxKAJZll8yI=,tag:xwBJzExJXAw0T39hPdmgRw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.2
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
key: ENC[AES256_GCM,data:Flvbs7sRCQ3BtSJXWurmVA+QQmvSo/fxrOVJNalBjj8=,iv:ddmk1XCjvNRmmqGhZwOWDlYSkdKz3ryyQwrav/T6Oz8=,tag:DvSjwdK01UAvBL1mYCe7kA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVUGFmM010b3NhdCtPME5O
|
||||
RWRkUjF0akVEZVFtNkhZeUVSeWpWL3pKNG1nCllObGlPOTcrSyt1a2NCc0U4TVZM
|
||||
cWVQbjJocEUySHFYaDFmNzllOGhuQWcKLS0tIHFJeFdUWENxK1RRSlhvSEI4NXlO
|
||||
TG1wM1FTWjBBWHdHNnBUUE1kNWE4akUK1Ehv5kYNZrbf3m3vH5Oyy/g2mz8kIX8T
|
||||
l2k1LbhUo3cNzIjkNM05RSrLZEBPItoo2nc9oTAPZiF1U/K76SMP6g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-03T07:59:50Z"
|
||||
mac: ENC[AES256_GCM,data:E4WwhOqUCmOBXwYUR5nFzth59F50KE7k88NkOBc6ERnX4b5joy0lmExenprH019UVJ/HSXOCWphUMwTat70uzwhaKBtlfa7AWmWyMa6zhIzxTsFagI4h1nWQP00uGb0MM3ULR3mcLtxXyTtZFWuY/d8ttdYBuv2oe29ikJTX610=,iv:gAZPLiYcNGq8LSEpIxDzDqdan+CJKyQ+SnQXHmponns=,tag:3tI0it3h6foT9W7zK3rmxg==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.2
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
key: ENC[AES256_GCM,data:lZXbXrorH/GGGg93WgnG6GlzNs0NEy0BlForhgKlwllsf2uJWnBukQDDxxI6gUWMp+Uzc3e9plrmGbM7HQfsccDoQux7kJjGdt5CJgou0eh4J8WGw51M+beixVdF6UuCFFiuLERstzpIP1jhmyDfLZjMVTwfoRe0/YP52Ljgl2Y=,iv:pShAMIXtbEuV7Y2o+3NgTIbPPaoZvx9qF7lj60LMabw=,tag:P6vnrqWzJXXhUl+sJrY2gw==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlTHF2cGFIQXE2UDJhaVZX
|
||||
KzhXcWNPbW0zYjBPbTVReUJKOW1ReEt5ZFFNCjFnU25UVVY5eXFyaFFMbkI1NHZ5
|
||||
WHRIZ1pYT1ZVWUFJc2hrcGtYTndPdVEKLS0tIHZUSzkxUjEvU0kzRG93Vnc1ZGQz
|
||||
WGZ3U2dvYmFIVlYvZ3c1Q2JsQkNGek0KGYlQEPskcDN2VxkHskctCTIjPjse1wLL
|
||||
6VcEuidu69gkMqvDXR4bmnt8hxSqdH6OS3qEuDfLpqndjfCh//4CDw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-02T18:08:12Z"
|
||||
mac: ENC[AES256_GCM,data:jYec4f5lr8jYCVvzpM2a3lpIILOTKauz1DNuDIclL7kGA2QXJhW1njXKhmp/VCVbZLfoHCJk6SHyMcxUcx6HXtmc7FAVpu5+Vc/9N+D3gKlyXewDeC41CFdZ7GU0vtoVQK3KQe82rWl+6SRCWXeqLfmRlh7/pLwZy+bj20kG0XY=,iv:sIYEumkyDVrWZRVZpFPtrM96p82rtxW870LRkpHFsPY=,tag:Ey9L21LN+2HjCReuoqJtKQ==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.2
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
tide-env: ENC[AES256_GCM,data:O4B6jatKVbbVoO53QNgh1ra0Ij9cxo64j+wmmeTZWgX7Us3z0FT3hsOsmYWBsCrKFIGpA2meO4QXlowbJOw43sO7CiCFKuRN7rv58XbGk/hevNhUUTrqU8Seha18ZeoOfmUhD1ScJtyC5RkJb1xFnsFWmFtofebyabhcSLXmJz4UC7Vl2BsVZWULjp9LIkAIOcr2Y6Dm6SoXQa3n6p5yt5rKlY/Xfg/zQoczfi0TG+x+P4SzPPv2U8PJQGG1OqQ0bA82wFNcna+mIb6QIvWz27PeBK4pDfDYnA0SqzKEZsol3WgF5LWPXL5zQpsh11oy7YBWdx8omo3KNBx3+tl2/ACYLuTKRgrxP3UIE38hAm4j95IrL88jFWeNtfZ/fMdTckuQKR2vgbN6Q5HYqnYW72uxCYGZGWD+Jq7MAUduAbvzSlurLoHnKCrA+4Pf8Tu/yJBVDN+urhxnWJy8VpGDXFAGA+reXLtNjZXg1y2KNJOijmzEYfwZ8t53MvCdsJnF562mvCag+DXGex6DZ2qqKeXGa3Lw/YTPBXCHjHJfr+0l91NoOjelA33wS+h+RisHw/iCPlt3NHQgJtCXN7JRWM1Hi3p1ADgovvqJ7yBsT3OXD91QIy5NPSergGsj2vvj+9VBEzSTUuHA4KzYgxrjLUgLeohypr4veFs3qnC3/iTx9gkRfcFX0SMPgj1DWy9+pS42Wi+YDgILAQVSSOgrjhnMv2E0nYNEgZcTPS6SKj+Li4V3/OUVjbMj4N6BLsXxbhyFrKH6MF7ZOXU=,iv:yj0aUCReke2gI3uk/Qer6QBSCQgTwZmCaK7aPBtUwDo=,tag:e6NcaEL3f4EFsaGQKIH+BA==,type:str]
|
||||
admin-password: ENC[AES256_GCM,data:yPOLYZ9oMvZrXcfgJQkGL83qPpHrJe7i4Gw=,iv:WmPO6hDNkB7nckFuJjnZX5/8xXpZkUxP8FkjmYmQxzs=,tag:g1dLh009OOBYYL9UkPz3uw==,type:str]
|
||||
mail-key: ENC[AES256_GCM,data:/9bT8jECSyMqz45LLbWSV15OAJ4wA/ZiCvi9uYrSZK8=,iv:ZN9taTVcIHxqz63pxir7RWTp4CpjVxUBTH+HO92CvPU=,tag:ufVCA36rVBE2ghbI63QXfA==,type:str]
|
||||
secret-key: ENC[AES256_GCM,data:pFsKF3A9RZSxeV9FwNKIzp+Avu6YWCCHj9zIeex7nHqhuFNuL8D4blQWPujms3wRTUj/OdCbgh56ZXdnEJ6l0vYBKBnP7tS7v7jBhZWtF03SqukCpu+c2HCO6wNvP0tw9zVVSVp0RRmvLq7KhmL6Y9748/TA08MPCVNVP46Xa2E=,iv:yH4vU6kjM+49ssWVAULJcZ9ny264zWsixGs/et6G1Lk=,tag:UWH3Epqn4yGqeTYeBcrDPg==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v
|
||||
enc: |
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnQ2RzU0NBUTBhQTBERnBm
|
||||
RFRPdWlKRWtWMW8zaktGVyt5L3VPTENMakhVCjFsdStSbHFzdmFYcWxuU294VW5Q
|
||||
MWVQMzlJdmdqTW93Q1MwaVRFaVIvY2sKLS0tIHJxUk96VjRLeWpIanBlNktESDhI
|
||||
dS9VREordndFSVhnbXFCdUVFN0Z3aG8KN4h2ptJxttY/02FcmRqZa+ujom0LIS11
|
||||
cS+qkrk7FnrTbSCNETtjc/FApLTxphptd93zCPJkQtulmo2d9soJlA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZEtYbEhoNW5FVnRlQXIr
|
||||
TWJaVk5RUmdaUnNKcHRwbElWWCtLQ3l6cm00ClVzNWJZYzRpS21Wckd0dFlEano2
|
||||
c1k3TlNZNjU2WGVqUkExblRUZmJpbHcKLS0tIGsvWWhrREoxV2FEREFWQUNpMEZR
|
||||
MlRTRk13YTljdi9LOW1talBpeDBQdmMKeDvwaLwwYjw1lLFf+/ZRmVGP+chYxa1Y
|
||||
d/NjatKGm8M/hEKw/MpEKSAMMNAJPUHVwDAeaQTro19TzYerV8e9BQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-11T07:36:34Z"
|
||||
mac: ENC[AES256_GCM,data:VDSB1iKcP9AY4HGHPlQxINlc2wB03EkCd8TcHLfFqE0zNBPYlX0Buf6kqy89/0M9UmRYGS7IlYMEDlFGJ61cidD0Kxk2n9R7P32S75nS//lorX/28Slnwlfe9K4w7eS5p+zwO0QiPY3X87or6KTyi95QBYz6ALp9cpetrRn3nJk=,iv:PPqdmFbXCFmZgfXsEUvuQy1PDde2dD/EnHAFE2TGZIk=,tag:Hv+QMEM8wkjXPUt4TGhvCQ==,type:str]
|
||||
recipient: age1haj8v88kjna6ttkdufjpyjcf478kyvclnpdc8jwh97ewhqcc9eqsgrku4v
|
||||
lastmodified: "2026-07-19T07:04:49Z"
|
||||
mac: ENC[AES256_GCM,data:UvfEnQuGSrXNe9Zyel2UEyfcAgh03UgBu0VzSg0lzOgYuK0qPwvezZNXRyfFkHo0AdpIJAmN8pwtavExUWqL8OOUgIcooK7hZJ9S5aXFfLeZ0YhpAZ1TpUmHTNRL7VMOJtLU/phvObggYTHJfLLE92RkwnKxqJ8hIli8VVIa59w=,iv:NYcAEyoNC8cygEczLGUXzuhY/OXTXCpP3BDQJJKQwQU=,tag:PgkGZwmkiCAkO/WPZJyDYQ==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.2
|
||||
version: 3.13.2
|
||||
|
|
|
|||
|
|
@ -1,20 +1,5 @@
|
|||
# Encrypt this with sops to produce ./tide.yaml:
|
||||
#
|
||||
# sops --encrypt --age $(cat ~/.config/sops/age/keys.txt | grep public | cut -d: -f2 | tr -d ' ') \
|
||||
# secrets/tide.yaml.example > secrets/tide.yaml
|
||||
#
|
||||
# Or set up .sops.yaml with the host's age public key and run
|
||||
# `sops secrets/tide.yaml`.
|
||||
#
|
||||
# Generate APP_KEY with:
|
||||
# php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;"
|
||||
# Encrypt this file with sops to produce ./tide.yaml.
|
||||
|
||||
tide-env: |
|
||||
APP_KEY=base64:REPLACE_ME
|
||||
DB_PASSWORD=REPLACE_ME
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_FROM_ADDRESS=noreply@tide.yisroelbaum.com
|
||||
MAIL_FROM_NAME=TIDE
|
||||
admin-password: REPLACE_ME
|
||||
mail-key: REPLACE_ME
|
||||
secret-key: REPLACE_ME
|
||||
|
|
|
|||
56
tide.nix
56
tide.nix
|
|
@ -1,37 +1,41 @@
|
|||
{
|
||||
domainName,
|
||||
...
|
||||
}:
|
||||
{ config, ... }:
|
||||
{
|
||||
services.tide = {
|
||||
enable = true;
|
||||
domain = "tide.${domainName}";
|
||||
apiDomain = "apitide.${domainName}";
|
||||
secretsFile = "/run/secrets/tide-env";
|
||||
# Reuse the wildcard cert already issued for *.${domainName}
|
||||
# in configuration.nix instead of requesting a new one per
|
||||
# subdomain.
|
||||
nginx.useACMEHost = null;
|
||||
secretFiles = {
|
||||
adminPassword = config.sops.secrets."tide-admin-password".path;
|
||||
mailPassword = config.sops.secrets."tide-mail-password".path;
|
||||
secretKeyBase = config.sops.secrets."tide-secret-key-base".path;
|
||||
borgPassphrase = config.sops.secrets."borg-passphrase".path;
|
||||
borgPrivateKey = config.sops.secrets."borg-private-key".path;
|
||||
};
|
||||
};
|
||||
|
||||
# Don't fail evaluation when secrets/tide.yaml is missing (e.g.
|
||||
# before the operator has encrypted it on a fresh checkout).
|
||||
# sops-install-secrets will still error at activation time if
|
||||
# the file is absent, which is the right place for that failure.
|
||||
sops.validateSopsFiles = false;
|
||||
|
||||
# Use the host's age key generated with age-keygen, not the
|
||||
# SSH-host-key-derived identity sops-nix falls back to by default.
|
||||
# The encrypted file's recipient is the public key paired with
|
||||
# this private key.
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.sshKeyPaths = [ ];
|
||||
sops.gnupg.sshKeyPaths = [ ];
|
||||
|
||||
sops.secrets."tide-env" = {
|
||||
sopsFile = ./secrets/tide.yaml;
|
||||
# phpfpm reads this via EnvironmentFile, which runs as root
|
||||
# before dropping to the tide user, so root readable is enough.
|
||||
mode = "0400";
|
||||
sops.secrets = {
|
||||
"tide-admin-password" = {
|
||||
sopsFile = ./secrets/tide.yaml;
|
||||
key = "admin-password";
|
||||
mode = "0400";
|
||||
owner = "discourse";
|
||||
group = "discourse";
|
||||
};
|
||||
"tide-mail-password" = {
|
||||
sopsFile = ./secrets/tide.yaml;
|
||||
key = "mail-key";
|
||||
mode = "0400";
|
||||
owner = "discourse";
|
||||
group = "discourse";
|
||||
};
|
||||
"tide-secret-key-base" = {
|
||||
sopsFile = ./secrets/tide.yaml;
|
||||
key = "secret-key";
|
||||
mode = "0400";
|
||||
owner = "discourse";
|
||||
group = "discourse";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue