add yisroelbaum web stack module
This commit is contained in:
commit
4a4de4eb0a
6 changed files with 456 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
*~
|
||||||
|
result
|
||||||
|
/.direnv/
|
||||||
41
README.md
Normal file
41
README.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# yisroelbaum.com web stack
|
||||||
|
|
||||||
|
This flake owns the production NixOS configuration for the web services under
|
||||||
|
`yisroelbaum.com`:
|
||||||
|
|
||||||
|
- the MyResume package at `yisroelbaum.com`;
|
||||||
|
- Jellyfin at `jellyfin.yisroelbaum.com`;
|
||||||
|
- Forgejo at `git.yisroelbaum.com`, including its Borg backup;
|
||||||
|
- Rabbi Gerzi at `rabbigerzi.yisroelbaum.com` and
|
||||||
|
`rabbigerziapi.yisroelbaum.com`;
|
||||||
|
- nginx and per-host ACME certificates for those endpoints.
|
||||||
|
|
||||||
|
The importing host remains responsible for provisioning secrets. Import
|
||||||
|
`nixosModules.yisroelbaum-web` or `nixosModules.default`, then pass the
|
||||||
|
resulting runtime paths:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.yisroelbaum-web = {
|
||||||
|
enable = true;
|
||||||
|
secretFiles = {
|
||||||
|
borgPassphrase = "/run/secrets/borg-passphrase";
|
||||||
|
borgPrivateKey = "/run/secrets/borg-private-key";
|
||||||
|
forgejoMailerUser = "/run/secrets/forgejo-mailer-user";
|
||||||
|
forgejoMailerPassword = "/run/secrets/forgejo-mailer-password";
|
||||||
|
rabbiGerziEnvironment = "/run/secrets/rabbi-gerzi.env";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the checks with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
## First deployment
|
||||||
|
|
||||||
|
This repository is hosted by the Forgejo instance that it configures. For the
|
||||||
|
initial migration, publish and push this flake while the old host configuration
|
||||||
|
is still active. Only then add the remote flake input to the host configuration
|
||||||
|
and update its lock file.
|
||||||
103
flake.lock
generated
Normal file
103
flake.lock
generated
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1785318670,
|
||||||
|
"narHash": "sha256-dN6Ou5x/+23FZLEpYP3IffO+NyJFzUlGumt1uu3MMaY=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "0954f7ee2f6bb3dc7d4e3d0d8bcb8fd4bde4cfc5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rabbi-gerzi": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"utils": "utils"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1784294454,
|
||||||
|
"narHash": "sha256-oPxPCk4wMf0qvarGyZqixQI44RyirQA0ZBYs7J8liv4=",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"rev": "3e02a83dd48de378d3763fb27df3a665cca6cb18",
|
||||||
|
"revCount": 379,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.yisroelbaum.com/yisroelbaum/Rabbi_Gerzi"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.yisroelbaum.com/yisroelbaum/Rabbi_Gerzi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resume": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1779106432,
|
||||||
|
"narHash": "sha256-EsVQ1SAEIbgiTDA8b5ygm904vDJd5xUQWld/KJRrJvM=",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"rev": "4ae8a76a478b5c1f3abd45ad3cbdaaa3439b4635",
|
||||||
|
"revCount": 11,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.yisroelbaum.com/yisroelbaum/MyResume"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.yisroelbaum.com/yisroelbaum/MyResume"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rabbi-gerzi": "rabbi-gerzi",
|
||||||
|
"resume": "resume"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
54
flake.nix
Normal file
54
flake.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
description = "NixOS module for the yisroelbaum.com web stack";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
resume = {
|
||||||
|
url = "git+https://git.yisroelbaum.com/yisroelbaum/MyResume";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
rabbi-gerzi = {
|
||||||
|
url = "git+https://git.yisroelbaum.com/yisroelbaum/Rabbi_Gerzi";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
resume,
|
||||||
|
rabbi-gerzi,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
module = import ./nix/module.nix { inherit resume; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixosModules.yisroelbaum-web = {
|
||||||
|
imports = [
|
||||||
|
rabbi-gerzi.nixosModules.default
|
||||||
|
module
|
||||||
|
];
|
||||||
|
};
|
||||||
|
nixosModules.default = self.nixosModules.yisroelbaum-web;
|
||||||
|
|
||||||
|
checks.${system}.module-eval = import ./nix/tests/module.nix {
|
||||||
|
inherit
|
||||||
|
nixpkgs
|
||||||
|
self
|
||||||
|
system
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
formatter.${system} = pkgs.nixfmt-tree;
|
||||||
|
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = [ pkgs.nixfmt-tree ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
173
nix/module.nix
Normal file
173
nix/module.nix
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
{ resume }:
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.services.yisroelbaum-web;
|
||||||
|
domainName = "yisroelbaum.com";
|
||||||
|
resumePackage = resume.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||||
|
secretFileOption =
|
||||||
|
description:
|
||||||
|
lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
inherit description;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.yisroelbaum-web = {
|
||||||
|
enable = lib.mkEnableOption "the yisroelbaum.com web stack";
|
||||||
|
|
||||||
|
secretFiles = {
|
||||||
|
borgPassphrase = secretFileOption "Borg repository passphrase file.";
|
||||||
|
borgPrivateKey = secretFileOption "Borg repository SSH private key file.";
|
||||||
|
forgejoMailerUser = secretFileOption "Forgejo SMTP username file.";
|
||||||
|
forgejoMailerPassword = secretFileOption "Forgejo SMTP password file.";
|
||||||
|
rabbiGerziEnvironment = secretFileOption "Rabbi Gerzi runtime dotenv file.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
users.users.${config.services.nginx.user}.extraGroups = [ "acme" ];
|
||||||
|
|
||||||
|
services = {
|
||||||
|
jellyfin.enable = true;
|
||||||
|
|
||||||
|
forgejo = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
ROOT_URL = "https://git.${domainName}/";
|
||||||
|
PROTOCOL = "http";
|
||||||
|
HTTP_ADDR = "127.0.0.1";
|
||||||
|
HTTP_PORT = 3000;
|
||||||
|
DOMAIN = "git.${domainName}";
|
||||||
|
SSH_PORT = 2222;
|
||||||
|
START_SSH_SERVER = true;
|
||||||
|
};
|
||||||
|
session.COOKIE_SECURE = true;
|
||||||
|
service.DISABLE_REGISTRATION = true;
|
||||||
|
mailer = {
|
||||||
|
ENABLED = true;
|
||||||
|
SMTP_ADDR = "in-v3.mailjet.com";
|
||||||
|
SMTP_PORT = 587;
|
||||||
|
FROM = "me@${domainName}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
secrets.mailer = {
|
||||||
|
USER = cfg.secretFiles.forgejoMailerUser;
|
||||||
|
PASSWD = cfg.secretFiles.forgejoMailerPassword;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
rabbi-gerzi = {
|
||||||
|
enable = true;
|
||||||
|
frontend = {
|
||||||
|
hostName = "rabbigerzi.${domainName}";
|
||||||
|
nginx = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
backend = {
|
||||||
|
hostName = "rabbigerziapi.${domainName}";
|
||||||
|
environmentFile = cfg.secretFiles.rabbiGerziEnvironment;
|
||||||
|
nginx = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts = {
|
||||||
|
"${domainName}" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/".root = "${resumePackage}";
|
||||||
|
};
|
||||||
|
|
||||||
|
"jellyfin.${domainName}" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/".proxyPass = "http://localhost:8096";
|
||||||
|
};
|
||||||
|
|
||||||
|
"git.${domainName}" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
extraConfig = ''
|
||||||
|
client_max_body_size 512M;
|
||||||
|
'';
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:3000";
|
||||||
|
recommendedProxySettings = false;
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
borgbackup.jobs.forgejo = {
|
||||||
|
paths = [ config.services.forgejo.stateDir ];
|
||||||
|
repo = "ssh://n5lniyfd@n5lniyfd.repo.borgbase.com/./repo";
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
encryption = {
|
||||||
|
mode = "repokey-blake2";
|
||||||
|
passCommand = "${pkgs.coreutils}/bin/cat ${cfg.secretFiles.borgPassphrase}";
|
||||||
|
};
|
||||||
|
doInit = true;
|
||||||
|
compression = "auto,zstd";
|
||||||
|
startAt = "*-*-* 03:15:00";
|
||||||
|
persistentTimer = true;
|
||||||
|
prune.keep = {
|
||||||
|
daily = 7;
|
||||||
|
weekly = 4;
|
||||||
|
monthly = 6;
|
||||||
|
};
|
||||||
|
environment.BORG_RSH = "ssh -i ${cfg.secretFiles.borgPrivateKey} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.config/borg/known_hosts";
|
||||||
|
preHook = ''
|
||||||
|
forgejoWasActive=/root/.cache/borg/forgejo-was-active
|
||||||
|
if ${pkgs.systemd}/bin/systemctl is-active --quiet forgejo.service; then
|
||||||
|
touch "$forgejoWasActive"
|
||||||
|
${pkgs.systemd}/bin/systemctl stop forgejo.service
|
||||||
|
else
|
||||||
|
rm -f "$forgejoWasActive"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
postHook = ''
|
||||||
|
forgejoWasActive=/root/.cache/borg/forgejo-was-active
|
||||||
|
if [ -e "$forgejoWasActive" ]; then
|
||||||
|
${pkgs.systemd}/bin/systemctl start forgejo.service
|
||||||
|
rm -f "$forgejoWasActive"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
security.acme = {
|
||||||
|
defaults = {
|
||||||
|
webroot = "/var/lib/acme/acme-challenge/";
|
||||||
|
email = "yisroel.d.baum@gmail.com";
|
||||||
|
};
|
||||||
|
acceptTerms = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.borgbackup-job-forgejo = {
|
||||||
|
after = [
|
||||||
|
"sops-install-secrets.service"
|
||||||
|
"network-online.target"
|
||||||
|
];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
82
nix/tests/module.nix
Normal file
82
nix/tests/module.nix
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
nixpkgs,
|
||||||
|
self,
|
||||||
|
system,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
evaluatedSystem = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
self.nixosModules.default
|
||||||
|
{
|
||||||
|
system.stateVersion = "25.11";
|
||||||
|
services.yisroelbaum-web = {
|
||||||
|
enable = true;
|
||||||
|
secretFiles = {
|
||||||
|
borgPassphrase = "/run/secrets/borg-passphrase";
|
||||||
|
borgPrivateKey = "/run/secrets/borg-private-key";
|
||||||
|
forgejoMailerUser = "/run/secrets/forgejo-mailer-user";
|
||||||
|
forgejoMailerPassword = "/run/secrets/forgejo-mailer-password";
|
||||||
|
rabbiGerziEnvironment = "/run/secrets/rabbi-gerzi.env";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
evaluatedConfig = evaluatedSystem.config;
|
||||||
|
virtualHosts = evaluatedConfig.services.nginx.virtualHosts;
|
||||||
|
certificates = evaluatedConfig.security.acme.certs;
|
||||||
|
forgejoBackup = evaluatedConfig.services.borgbackup.jobs.forgejo;
|
||||||
|
forgejoBackupService = evaluatedConfig.systemd.services.borgbackup-job-forgejo;
|
||||||
|
publicHostNames = [
|
||||||
|
"yisroelbaum.com"
|
||||||
|
"jellyfin.yisroelbaum.com"
|
||||||
|
"git.yisroelbaum.com"
|
||||||
|
"rabbigerzi.yisroelbaum.com"
|
||||||
|
"rabbigerziapi.yisroelbaum.com"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
assert evaluatedConfig.services.nginx.enable;
|
||||||
|
assert evaluatedConfig.services.jellyfin.enable;
|
||||||
|
assert evaluatedConfig.services.forgejo.enable;
|
||||||
|
assert evaluatedConfig.services.rabbi-gerzi.enable;
|
||||||
|
assert
|
||||||
|
virtualHosts."yisroelbaum.com".locations."/".root
|
||||||
|
== "${self.inputs.resume.packages.${system}.default}";
|
||||||
|
assert virtualHosts."jellyfin.yisroelbaum.com".locations."/".proxyPass == "http://localhost:8096";
|
||||||
|
assert virtualHosts."git.yisroelbaum.com".locations."/".proxyPass == "http://127.0.0.1:3000";
|
||||||
|
assert !virtualHosts."git.yisroelbaum.com".locations."/".recommendedProxySettings;
|
||||||
|
assert nixpkgs.lib.hasInfix "proxy_set_header X-Forwarded-Proto $scheme;"
|
||||||
|
virtualHosts."git.yisroelbaum.com".locations."/".extraConfig;
|
||||||
|
assert builtins.all (
|
||||||
|
hostName:
|
||||||
|
virtualHosts.${hostName}.forceSSL
|
||||||
|
&& virtualHosts.${hostName}.enableACME
|
||||||
|
&& certificates.${hostName}.domain == hostName
|
||||||
|
) publicHostNames;
|
||||||
|
assert builtins.all (certificate: !(nixpkgs.lib.hasPrefix "*." certificate.domain)) (
|
||||||
|
builtins.attrValues certificates
|
||||||
|
);
|
||||||
|
assert evaluatedConfig.services.forgejo.settings.server.ROOT_URL == "https://git.yisroelbaum.com/";
|
||||||
|
assert evaluatedConfig.services.forgejo.settings.server.DOMAIN == "git.yisroelbaum.com";
|
||||||
|
assert evaluatedConfig.services.forgejo.settings.server.SSH_PORT == 2222;
|
||||||
|
assert evaluatedConfig.services.forgejo.settings.mailer.FROM == "me@yisroelbaum.com";
|
||||||
|
assert evaluatedConfig.services.forgejo.secrets.mailer.USER == "/run/secrets/forgejo-mailer-user";
|
||||||
|
assert
|
||||||
|
evaluatedConfig.services.forgejo.secrets.mailer.PASSWD == "/run/secrets/forgejo-mailer-password";
|
||||||
|
assert evaluatedConfig.services.rabbi-gerzi.frontend.hostName == "rabbigerzi.yisroelbaum.com";
|
||||||
|
assert evaluatedConfig.services.rabbi-gerzi.backend.hostName == "rabbigerziapi.yisroelbaum.com";
|
||||||
|
assert
|
||||||
|
evaluatedConfig.services.rabbi-gerzi.backend.environmentFile == "/run/secrets/rabbi-gerzi.env";
|
||||||
|
assert forgejoBackup.repo == "ssh://n5lniyfd@n5lniyfd.repo.borgbase.com/./repo";
|
||||||
|
assert forgejoBackup.startAt == "*-*-* 03:15:00";
|
||||||
|
assert forgejoBackup.persistentTimer;
|
||||||
|
assert forgejoBackup.prune.keep.daily == 7;
|
||||||
|
assert forgejoBackup.prune.keep.weekly == 4;
|
||||||
|
assert forgejoBackup.prune.keep.monthly == 6;
|
||||||
|
assert builtins.elem evaluatedConfig.services.forgejo.stateDir forgejoBackup.paths;
|
||||||
|
assert builtins.elem "sops-install-secrets.service" forgejoBackupService.after;
|
||||||
|
pkgs.runCommand "yisroelbaum-web-module-eval" { } ''
|
||||||
|
touch "$out"
|
||||||
|
''
|
||||||
Loading…
Add table
Add a link
Reference in a new issue