yep, more dotfiles

server: split large server file into weird-row-server host

wiro.world 124dc64d bc427963

verified
+1 -1
README.md
···
- `fragments`: Home Manager configuration fragments
- `options`: Home Manager configuration flags
- `profiles`: Base Home Manager configurations to build upon (e.g. `desktop`, `minimal`)
-
- `lib`: Additional custom lib and flake helpers
+
- `lib`: Additional custom lib and flake helpers
- `modules`: modules that fill a missing feature of NixOS or Home Manager
- `nixos`: NixOS related config
- `hardware/<hostname>.nix`: Device-specific settings like settings generated by `nixos-generate-config`
+1 -1
apps/flash-installer.nix
···
fi
echo "Flashing to $dev"
-
+
# Format selected disk
pv -tpreb "${isoPath}" | sudo dd bs=4M of="$dev" iflag=fullblock conv=notrunc,noerror oflag=sync
'';
+2 -1
configurations.nix
···
# Servers
"weird-row-server" = createSystem pkgs [
-
(system "weird-row-server" "server")
+
(host "weird-row-server")
(managedDiskLayout "ext4-hetzner" { device = "sda"; swapSize = 2; })
+
# TODO: should we keep a real user there?
(user "milomoisson" { description = "Milo Moisson"; profile = "server"; keys = keys.users; })
];
};
+3 -1
home-manager/fragments/git.nix
···
};
git = {
-
pagers.externalDiffCommand = "difft --color=always";
+
pagers = [
+
{ externalDiffCommand = "difft --color=always"; }
+
];
};
# to be declarative or not to be declarative?
+1 -1
home-manager/fragments/shell.nix
···
'';
# Quickly explore a derivation (using registry syntax)
-
# e.g. `cdd nixpkgs#fontforge` or `cdd unixpkgs#fontforge`
+
# e.g. `cdd nixpkgs#fontforge` or `cdd unixpkgs#fontforge`
cdd = "cd (nix build --no-link --print-out-paths $argv | ${lib.getExe pkgs.fzf})";
} // lib.optionalAttrs (!flags.onlyCached) {
# Quickly get outta here to test something
+5 -4
home-manager/fragments/stylix.nix
···
cfg = config.local.fragment.stylix;
in
{
-
imports = [ stylix.homeModules.stylix ];
+
imports = [
+
stylix.homeModules.stylix
+
# issues a warning because we use `useGlobalPkgs`
+
{ config.stylix.overlays.enable = false; }
+
];
options.local.fragment.stylix.enable = lib.mkEnableOption ''
Stylix related
···
stylix = {
enable = true;
base16Scheme = lib.mkDefault "${pkgs.base16-schemes}/share/themes/onedark-dark.yaml";
-
-
# issues a warning because we use `useGlobalPkgs`
-
overlays.enable = false;
image = ../../assets/wallpaper-binary-cloud.png;
+1 -1
home-manager/fragments/waybar.nix
···
.modules-right widget .module {
padding: 0 1rem;
-
color: @base07;
+
color: @base07;
}
/* Round first and last child of left, right and center modules. Disable rounding on the sides*/
+163
hosts/weird-row-server/authelia.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
authelia-port = 3008;
+
authelia-hostname = "auth.wiro.world";
+
+
authelia-metrics-port = 9004;
+
headscale-hostname = "headscale.wiro.world";
+
grafana-hostname = "console.wiro.world";
+
miniflux-hostname = "news.wiro.world";
+
in
+
{
+
config = {
+
age.secrets.authelia-jwt-secret = { file = secrets/authelia-jwt-secret.age; owner = config.services.authelia.instances.main.user; };
+
age.secrets.authelia-issuer-private-key = { file = secrets/authelia-issuer-private-key.age; owner = config.services.authelia.instances.main.user; };
+
age.secrets.authelia-storage-key = { file = secrets/authelia-storage-key.age; owner = config.services.authelia.instances.main.user; };
+
age.secrets.authelia-ldap-password = { file = secrets/authelia-ldap-password.age; owner = config.services.authelia.instances.main.user; };
+
age.secrets.authelia-smtp-password = { file = secrets/authelia-smtp-password.age; owner = config.services.authelia.instances.main.user; };
+
services.authelia.instances.main = {
+
enable = true;
+
+
secrets = {
+
jwtSecretFile = config.age.secrets.authelia-jwt-secret.path;
+
oidcIssuerPrivateKeyFile = config.age.secrets.authelia-issuer-private-key.path;
+
storageEncryptionKeyFile = config.age.secrets.authelia-storage-key.path;
+
};
+
environmentVariables = {
+
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = config.age.secrets.authelia-ldap-password.path;
+
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = config.age.secrets.authelia-smtp-password.path;
+
};
+
settings = {
+
server.address = "localhost:${toString authelia-port}";
+
storage.local.path = "/var/lib/authelia-main/db.sqlite3";
+
telemetry.metrics = {
+
enabled = true;
+
address = "tcp://:${toString authelia-metrics-port}/metrics";
+
};
+
+
session = {
+
cookies = [{
+
domain = "wiro.world";
+
authelia_url = "https://${authelia-hostname}";
+
default_redirection_url = "https://wiro.world";
+
}];
+
};
+
+
authentication_backend.ldap = {
+
address = "ldap://localhost:3890";
+
timeout = "5m"; # replace with systemd dependency
+
+
user = "uid=authelia,ou=people,dc=wiro,dc=world";
+
# Set in `AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE`.
+
# password = "";
+
+
base_dn = "dc=wiro,dc=world";
+
users_filter = "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))";
+
additional_users_dn = "ou=people";
+
groups_filter = "(&(member={dn})(objectClass=groupOfNames))";
+
additional_groups_dn = "ou=groups";
+
+
attributes = {
+
username = "uid";
+
display_name = "cn";
+
given_name = "givenname";
+
family_name = "last_name";
+
mail = "mail";
+
picture = "avatar";
+
+
group_name = "cn";
+
};
+
};
+
+
access_control = {
+
default_policy = "deny";
+
# Rules are sequential and do not apply to OIDC
+
rules = [
+
{
+
domain = "headscale.wiro.world";
+
policy = "two_factor";
+
+
}
+
{
+
domain = "news.wiro.world";
+
policy = "one_factor";
+
+
subject = [ [ "group:miniflux" "oauth2:client:miniflux" ] ];
+
}
+
{
+
domain = "*.wiro.world";
+
policy = "two_factor";
+
}
+
];
+
};
+
+
identity_providers.oidc = {
+
enforce_pkce = "always";
+
+
authorization_policies =
+
let
+
mkStrictPolicy = policy: subject:
+
{ default_policy = "deny"; rules = [{ inherit policy subject; }]; };
+
in
+
{
+
headscale = mkStrictPolicy "two_factor" [ "group:headscale" ];
+
tailscale = mkStrictPolicy "two_factor" [ "group:headscale" ];
+
grafana = mkStrictPolicy "one_factor" [ "group:grafana" ];
+
miniflux = mkStrictPolicy "one_factor" [ "group:miniflux" ];
+
};
+
+
claims_policies.headscale = { id_token = [ "email" "name" "preferred_username" "picture" "groups" ]; };
+
+
clients = [
+
{
+
client_name = "Headscale";
+
client_id = "headscale";
+
client_secret = "$pbkdf2-sha256$310000$XY680D9gkSoWhD0UtYHNFg$ptWB3exOYCga6uq1N.oimuV3ILjK3F8lBWBpsBpibos";
+
redirect_uris = [ "https://${headscale-hostname}/oidc/callback" ];
+
authorization_policy = "headscale";
+
claims_policy = "headscale";
+
}
+
{
+
client_name = "Tailscale";
+
client_id = "tailscale";
+
client_secret = "$pbkdf2-sha256$310000$PcUaup9aWKI9ZLeCF6.avw$FpsTxkDaxcoQlBi8aIacegXpjEDiCI6nXcaHyZ2Sxyc";
+
redirect_uris = [ "https://login.tailscale.com/a/oauth_response" ];
+
authorization_policy = "tailscale";
+
}
+
{
+
client_name = "Grafana Console";
+
client_id = "grafana";
+
client_secret = "$pbkdf2-sha256$310000$UkwrqxTZodGMs9.Ca2cXAA$HCWFgQbFHGXZpuz.I3HHdkTZLUevRVGlhKEFaOlPmKs";
+
redirect_uris = [ "https://${grafana-hostname}/login/generic_oauth" ];
+
authorization_policy = "grafana";
+
}
+
{
+
client_name = "Miniflux";
+
client_id = "miniflux";
+
client_secret = "$pbkdf2-sha256$310000$uPqbWfCOBXDY6nV1vsx3uA$HOWG2hL.c/bs9Dwaee3b9DxjH7KFO.SaZMbasXV9Vdw";
+
redirect_uris = [ "https://${miniflux-hostname}/oauth2/oidc/callback" ];
+
authorization_policy = "miniflux";
+
}
+
];
+
};
+
+
notifier.smtp = {
+
address = "smtp://smtp.resend.com:2587";
+
username = "resend";
+
# Set in `AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE`.
+
# password = "";
+
sender = "authelia@wiro.world";
+
};
+
};
+
};
+
+
services.caddy = {
+
virtualHosts.${authelia-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString authelia-port}
+
'';
+
};
+
};
+
}
+137
hosts/weird-row-server/default.nix
···
+
{ self
+
, config
+
, pkgs
+
, ...
+
}:
+
+
let
+
inherit (self.inputs) srvos;
+
+
ext-if = "eth0";
+
external-ip = "91.99.55.74";
+
external-netmask = 27;
+
external-gw = "144.x.x.255";
+
external-ip6 = "2a01:4f8:c2c:76d2::1";
+
external-netmask6 = 64;
+
external-gw6 = "fe80::1";
+
+
website-hostname = "wiro.world";
+
+
static-hostname = "static.wiro.world";
+
in
+
{
+
imports = [
+
srvos.nixosModules.server
+
srvos.nixosModules.hardware-hetzner-cloud
+
srvos.nixosModules.mixins-terminfo
+
+
./authelia.nix
+
./goatcounter.nix
+
./grafana.nix
+
./headscale.nix
+
./hypixel-bank-tracker.nix
+
./lldap.nix
+
./miniflux.nix
+
./pds.nix
+
./tangled.nix
+
./thelounge.nix
+
./tuwunel.nix
+
./vaultwarden.nix
+
./warrior.nix
+
./webfinger.nix
+
];
+
+
config = {
+
boot.loader.grub.enable = true;
+
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" "ext4" ];
+
+
# Single network card is `eth0`
+
networking.usePredictableInterfaceNames = false;
+
+
networking.nameservers = [ "2001:4860:4860::8888" "2001:4860:4860::8844" ];
+
+
networking = {
+
interfaces.${ext-if} = {
+
ipv4.addresses = [{ address = external-ip; prefixLength = external-netmask; }];
+
ipv6.addresses = [{ address = external-ip6; prefixLength = external-netmask6; }];
+
};
+
defaultGateway = { interface = ext-if; address = external-gw; };
+
defaultGateway6 = { interface = ext-if; address = external-gw6; };
+
+
# Reflect firewall configuration on Hetzner
+
firewall.allowedTCPPorts = [ 22 80 443 ];
+
};
+
+
services.qemuGuest.enable = true;
+
+
services.openssh.enable = true;
+
+
# age.secrets.tailscale-authkey.file = secrets/tailscale-authkey.age;
+
services.tailscale = {
+
enable = true;
+
extraSetFlags = [ "--advertise-exit-node" ];
+
# authKeyFile = config.age.secrets.tailscale-authkey.path;
+
authKeyParameters = {
+
baseURL = "https://headscale.wiro.world";
+
ephemeral = true;
+
preauthorized = true;
+
};
+
};
+
+
security.sudo.wheelNeedsPassword = false;
+
+
local.fragment.nix.enable = true;
+
+
programs.fish.enable = true;
+
+
services.fail2ban = {
+
enable = true;
+
+
maxretry = 5;
+
ignoreIP = [ ];
+
+
bantime = "24h";
+
bantime-increment = {
+
enable = true;
+
multipliers = "1 2 4 8 16 32 64";
+
maxtime = "168h";
+
overalljails = true;
+
};
+
+
jails = { };
+
};
+
+
services.caddy = {
+
enable = true;
+
package = pkgs.caddy.withPlugins {
+
doInstallCheck = false;
+
plugins = [
+
"github.com/tailscale/caddy-tailscale@v0.0.0-20251016213337-01d084e119cb"
+
"github.com/caddy-dns/hetzner@v2.0.0-preview-1"
+
];
+
hash = "sha256-muKwDYs5Jp4ib/psZxpp1Kyfsqz6wPz/lpHFGtx67uY=";
+
};
+
+
globalConfig = ''
+
tailscale {
+
ephemeral
+
}
+
'';
+
+
virtualHosts.${website-hostname}.extraConfig =
+
# TODO: host website on server with automatic deployment
+
''
+
reverse_proxy https://mrnossiom.github.io {
+
header_up Host {http.request.host}
+
}
+
'';
+
+
virtualHosts.${static-hostname}.extraConfig = ''
+
root /var/www/static
+
file_server browse
+
'';
+
};
+
+
# TODO: use bind to declare dns records declaratively
+
};
+
}
+25
hosts/weird-row-server/goatcounter.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
goatcounter-port = 3010;
+
goatcounter-hostname = "stats.wiro.world";
+
in
+
{
+
config = {
+
services.goatcounter = {
+
enable = true;
+
+
port = goatcounter-port;
+
proxy = true;
+
extraArgs = [ "-automigrate" ];
+
};
+
+
services.caddy = {
+
virtualHosts.${goatcounter-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString goatcounter-port}
+
'';
+
};
+
};
+
}
+84
hosts/weird-row-server/grafana.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
grafana-port = 3002;
+
grafana-hostname = "console.wiro.world";
+
+
prometheus-port = 9001;
+
prometheus-node-exporter-port = 9002;
+
caddy-metrics-port = 2019;
+
authelia-metrics-port = 9004;
+
headscale-metrics-port = 9003;
+
in
+
{
+
config = {
+
age.secrets.grafana-oidc-secret = { file = secrets/grafana-oidc-secret.age; owner = "grafana"; };
+
services.grafana = {
+
enable = true;
+
+
settings = {
+
server = {
+
http_port = grafana-port;
+
domain = grafana-hostname;
+
root_url = "https://${grafana-hostname}";
+
};
+
+
"auth.generic_oauth" = {
+
enable = true;
+
name = "Authelia";
+
icon = "signin";
+
+
client_id = "grafana";
+
client_secret_path = config.age.secrets.grafana-oidc-secret.path;
+
auto_login = true;
+
+
scopes = [ "openid" "profile" "email" "groups" ];
+
auth_url = "https://auth.wiro.world/api/oidc/authorization";
+
token_url = "https://auth.wiro.world/api/oidc/token";
+
api_url = "https://auth.wiro.world/api/oidc/userinfo";
+
use_pkce = true;
+
};
+
};
+
};
+
+
services.prometheus = {
+
enable = true;
+
port = prometheus-port;
+
+
exporters.node = {
+
enable = true;
+
port = prometheus-node-exporter-port;
+
};
+
+
scrapeConfigs = [
+
{
+
job_name = "caddy";
+
static_configs = [{ targets = [ "localhost:${toString caddy-metrics-port}" ]; }];
+
}
+
{
+
job_name = "node-exporter";
+
static_configs = [{ targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; }];
+
}
+
{
+
job_name = "headscale";
+
static_configs = [{ targets = [ "localhost:${toString headscale-metrics-port}" ]; }];
+
}
+
{
+
job_name = "authelia";
+
static_configs = [{ targets = [ "localhost:${toString authelia-metrics-port}" ]; }];
+
}
+
];
+
};
+
+
services.caddy = {
+
globalConfig = ''
+
metrics { per_host }
+
'';
+
virtualHosts.${grafana-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString grafana-port}
+
'';
+
};
+
};
+
}
+84
hosts/weird-row-server/headscale.nix
···
+
{ pkgs
+
, config
+
, ...
+
}:
+
+
let
+
json-format = pkgs.formats.json { };
+
+
headscale-port = 3006;
+
headscale-derp-port = 3478;
+
headscale-hostname = "headscale.wiro.world";
+
+
headscale-metrics-port = 9003;
+
in
+
{
+
config = {
+
networking.firewall.allowedUDPPorts = [ headscale-derp-port ];
+
+
age.secrets.headscale-oidc-secret = { file = secrets/headscale-oidc-secret.age; owner = config.services.headscale.user; };
+
services.headscale = {
+
enable = true;
+
+
port = headscale-port;
+
settings = {
+
server_url = "https://${headscale-hostname}";
+
metrics_listen_addr = "127.0.0.1:${toString headscale-metrics-port}";
+
+
policy.path = json-format.generate "policy.json" {
+
acls = [
+
{
+
action = "accept";
+
src = [ "autogroup:member" ];
+
dst = [ "autogroup:self:*" ];
+
}
+
];
+
ssh = [
+
{
+
action = "accept";
+
src = [ "autogroup:member" ];
+
dst = [ "autogroup:self" ];
+
# Adding root here is privilege escalation as a feature :)
+
users = [ "autogroup:nonroot" ];
+
}
+
];
+
};
+
+
# disable TLS
+
tls_cert_path = null;
+
tls_key_path = null;
+
+
dns = {
+
magic_dns = true;
+
base_domain = "net.wiro.world";
+
+
override_local_dns = true;
+
# Quad9 nameservers
+
nameservers.global = [ "9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9" ];
+
};
+
+
oidc = {
+
only_start_if_oidc_is_available = true;
+
issuer = "https://auth.wiro.world";
+
client_id = "headscale";
+
client_secret_path = config.age.secrets.headscale-oidc-secret.path;
+
scope = [ "openid" "profile" "email" "groups" ];
+
pkce.enabled = true;
+
};
+
+
derp.server = {
+
enable = true;
+
stun_listen_addr = "0.0.0.0:${toString headscale-derp-port}";
+
};
+
};
+
};
+
# headscale only starts if oidc is available
+
systemd.services.headscale.after = [ "authelia-main.service" ];
+
+
services.caddy = {
+
virtualHosts.${headscale-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString headscale-port}
+
'';
+
};
+
};
+
}
+42
hosts/weird-row-server/hypixel-bank-tracker.nix
···
+
{ self
+
, config
+
, ...
+
}:
+
+
let
+
inherit (self.inputs) hypixel-bank-tracker;
+
+
hbt-main-port = 3013;
+
hbt-banana-port = 3014;
+
in
+
{
+
imports = [ hypixel-bank-tracker.nixosModules.default ];
+
+
config = {
+
age.secrets.hypixel-bank-tracker-main.file = secrets/hypixel-bank-tracker-main.age;
+
services.hypixel-bank-tracker.instances.main = {
+
enable = true;
+
+
port = hbt-main-port;
+
environmentFile = config.age.secrets.hypixel-bank-tracker-main.path;
+
};
+
+
age.secrets.hypixel-bank-tracker-banana.file = secrets/hypixel-bank-tracker-banana.age;
+
services.hypixel-bank-tracker.instances.banana = {
+
enable = true;
+
+
port = hbt-banana-port;
+
environmentFile = config.age.secrets.hypixel-bank-tracker-banana.path;
+
};
+
+
services.caddy = {
+
virtualHosts."hypixel-bank-tracker.xyz".extraConfig = ''
+
reverse_proxy http://localhost:${toString config.services.hypixel-bank-tracker.instances.main.port}
+
'';
+
+
virtualHosts."banana.hypixel-bank-tracker.xyz".extraConfig = ''
+
reverse_proxy http://localhost:${toString config.services.hypixel-bank-tracker.instances.banana.port}
+
'';
+
};
+
};
+
}
+38
hosts/weird-row-server/lldap.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
lldap-port = 3007;
+
lldap-hostname = "ldap.wiro.world";
+
in
+
{
+
config = {
+
age.secrets.lldap-env.file = secrets/lldap-env.age;
+
users.users.lldap = { isSystemUser = true; group = "lldap"; };
+
users.groups.lldap = { };
+
age.secrets.lldap-user-pass = { file = secrets/lldap-user-pass.age; owner = "lldap"; };
+
services.lldap = {
+
enable = true;
+
+
silenceForceUserPassResetWarning = true;
+
+
settings = {
+
http_url = "https://${lldap-hostname}";
+
http_port = lldap-port;
+
+
ldap_user_pass_file = config.age.secrets.lldap-user-pass.path;
+
force_ldap_user_pass_reset = false;
+
+
ldap_base_dn = "dc=wiro,dc=world";
+
};
+
environmentFile = config.age.secrets.lldap-env.path;
+
};
+
+
services.caddy = {
+
virtualHosts.${lldap-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString lldap-port}
+
'';
+
};
+
};
+
}
+52
hosts/weird-row-server/miniflux.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
miniflux-port = 3012;
+
miniflux-hostname = "news.wiro.world";
+
in
+
{
+
config = {
+
users.users.miniflux = { isSystemUser = true; group = "miniflux"; };
+
users.groups.miniflux = { };
+
age.secrets.miniflux-oidc-secret = { file = secrets/miniflux-oidc-secret.age; owner = "miniflux"; };
+
services.miniflux = {
+
enable = true;
+
+
createDatabaseLocally = true;
+
config = {
+
BASE_URL = "https://${miniflux-hostname}/";
+
LISTEN_ADDR = "127.0.0.1:${toString miniflux-port}";
+
CREATE_ADMIN = 0;
+
+
METRICS_COLLECTOR = 1;
+
+
OAUTH2_PROVIDER = "oidc";
+
OAUTH2_OIDC_PROVIDER_NAME = "wiro.world SSO";
+
OAUTH2_CLIENT_ID = "miniflux";
+
OAUTH2_CLIENT_SECRET_FILE = config.age.secrets.miniflux-oidc-secret.path;
+
OAUTH2_REDIRECT_URL = "https://${miniflux-hostname}/oauth2/oidc/callback";
+
OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://auth.wiro.world";
+
OAUTH2_USER_CREATION = 1;
+
DISABLE_LOCAL_AUTH = 1;
+
+
RUN_MIGRATIONS = 1;
+
+
# NetNewsWire is a very good iOS oss client that integrates well
+
# https://b.j4.lc/2025/05/05/setting-up-netnewswire-with-miniflux/
+
};
+
};
+
+
services.prometheus.scrapeConfigs = [{
+
job_name = "miniflux";
+
static_configs = [{ targets = [ "localhost:${toString miniflux-port}" ]; }];
+
}];
+
+
services.caddy = {
+
virtualHosts.${miniflux-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString miniflux-port}
+
'';
+
};
+
};
+
}
+43
hosts/weird-row-server/pds.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
pds-port = 3001;
+
pds-hostname = "pds.wiro.world";
+
in
+
{
+
config = {
+
age.secrets.pds-env.file = secrets/pds-env.age;
+
services.bluesky-pds = {
+
enable = true;
+
+
settings = {
+
PDS_HOSTNAME = "pds.wiro.world";
+
PDS_PORT = pds-port;
+
# is in systemd /tmp subfolder
+
LOG_DESTINATION = "/tmp/pds.log";
+
};
+
+
environmentFiles = [
+
config.age.secrets.pds-env.path
+
];
+
};
+
+
services.caddy = {
+
globalConfig = ''
+
on_demand_tls {
+
ask http://localhost:${toString pds-port}/tls-check
+
}
+
'';
+
+
virtualHosts.${pds-hostname} = {
+
serverAliases = [ "*.${pds-hostname}" ];
+
extraConfig = ''
+
tls { on_demand }
+
reverse_proxy http://localhost:${toString pds-port}
+
'';
+
};
+
};
+
};
+
}
+54
hosts/weird-row-server/tangled.nix
···
+
{ self
+
, config
+
, ...
+
}:
+
+
let
+
inherit (self.inputs) tangled;
+
+
tangled-owner = "did:plc:xhgrjm4mcx3p5h3y6eino6ti";
+
tangled-knot-port = 3003;
+
tangled-knot-hostname = "knot.wiro.world";
+
tangled-spindle-port = 3004;
+
tangled-spindle-hostname = "spindle.wiro.world";
+
in
+
{
+
imports = [
+
tangled.nixosModules.knot
+
tangled.nixosModules.spindle
+
];
+
+
config = {
+
services.tangled.knot = {
+
enable = true;
+
openFirewall = true;
+
+
motd = "Welcome to @wiro.world's knot!\n";
+
server = {
+
listenAddr = "localhost:${toString tangled-knot-port}";
+
hostname = tangled-knot-hostname;
+
owner = tangled-owner;
+
};
+
};
+
+
services.tangled.spindle = {
+
enable = true;
+
+
server = {
+
listenAddr = "localhost:${toString tangled-spindle-port}";
+
hostname = tangled-spindle-hostname;
+
owner = tangled-owner;
+
};
+
};
+
+
services.caddy = {
+
virtualHosts.${tangled-knot-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString tangled-knot-port}
+
'';
+
+
virtualHosts.${tangled-spindle-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString tangled-spindle-port}
+
'';
+
};
+
};
+
}
+30
hosts/weird-row-server/thelounge.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
thelounge-port = 3005;
+
thelounge-hostname = "lounge.wiro.world";
+
in
+
{
+
config = {
+
services.thelounge = {
+
enable = true;
+
port = thelounge-port;
+
public = false;
+
+
extraConfig = {
+
host = "127.0.0.1";
+
reverseProxy = true;
+
+
# TODO: use ldap, find a way to hide password
+
};
+
};
+
+
services.caddy = {
+
virtualHosts.${thelounge-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString thelounge-port}
+
'';
+
};
+
};
+
}
+45
hosts/weird-row-server/tuwunel.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
matrix-port = 3009;
+
matrix-hostname = "matrix.wiro.world";
+
+
website-hostname = "wiro.world";
+
in
+
{
+
config = {
+
age.secrets.tuwunel-registration-tokens = { file = secrets/tuwunel-registration-tokens.age; owner = config.services.matrix-tuwunel.user; };
+
services.matrix-tuwunel = {
+
enable = true;
+
+
settings.global = {
+
address = [ "127.0.0.1" ];
+
port = [ matrix-port ];
+
+
server_name = "wiro.world";
+
well_known = {
+
client = "https://matrix.wiro.world";
+
server = "matrix.wiro.world:443";
+
};
+
+
grant_admin_to_first_user = true;
+
new_user_displayname_suffix = "";
+
+
allow_registration = true;
+
registration_token_file = config.age.secrets.tuwunel-registration-tokens.path;
+
};
+
};
+
+
services.caddy = {
+
virtualHosts.${matrix-hostname}.extraConfig = ''
+
reverse_proxy /_matrix/* http://localhost:${toString matrix-port}
+
'';
+
+
virtualHosts.${website-hostname}.extraConfig = ''
+
reverse_proxy /.well-known/matrix/* http://localhost:${toString matrix-port}
+
'';
+
};
+
};
+
}
+38
hosts/weird-row-server/vaultwarden.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
vaultwarden-port = 3011;
+
vaultwarden-hostname = "vault.wiro.world";
+
in
+
{
+
config = {
+
age.secrets.vaultwarden-env.file = secrets/vaultwarden-env.age;
+
services.vaultwarden = {
+
enable = true;
+
+
environmentFile = config.age.secrets.vaultwarden-env.path;
+
config = {
+
ROCKET_PORT = vaultwarden-port;
+
DOMAIN = "https://${vaultwarden-hostname}";
+
SIGNUPS_ALLOWED = false;
+
ADMIN_TOKEN = "$argon2id$v=19$m=65540,t=3,p=4$YIe9wmrTsmjgZNPxe8m34O/d3XW3Fl/uZPPLQs79dAc$mjDVQSdBJqz2uBJuxtAvCIoHPzOnTDhNPuhER3dhHrY";
+
+
SMTP_HOST = "smtp.resend.com";
+
SMTP_PORT = 2465;
+
SMTP_SECURITY = "force_tls";
+
SMTP_USERNAME = "resend";
+
# SMTP_PASSWORD = ...; # Via secret env
+
SMTP_FROM = "bitwarden@wiro.world";
+
SMTP_FROM_NAME = "Bitwarden wiro.world";
+
};
+
};
+
+
services.caddy = {
+
virtualHosts.${vaultwarden-hostname}.extraConfig = ''
+
reverse_proxy http://localhost:${toString vaultwarden-port}
+
'';
+
};
+
};
+
}
+28
hosts/weird-row-server/warrior.nix
···
+
{ config
+
, ...
+
}:
+
+
let
+
warrior-port = 3015;
+
warrior-hostname = "warrior.wiro.world";
+
+
authelia-port = 3008;
+
in
+
{
+
config = {
+
virtualisation.oci-containers.containers.archive-warrior = {
+
image = "atdr.meo.ws/archiveteam/warrior-dockerfile";
+
ports = [ "127.0.0.1:${toString warrior-port}:8001" ];
+
pull = "newer";
+
};
+
+
services.caddy = {
+
virtualHosts.${warrior-hostname}.extraConfig = ''
+
forward_auth localhost:${toString authelia-port} {
+
uri /api/authz/forward-auth
+
}
+
reverse_proxy http://localhost:${toString warrior-port}
+
'';
+
};
+
};
+
}
+77
hosts/weird-row-server/webfinger.nix
···
+
{ pkgs
+
, config
+
, ...
+
}:
+
+
let
+
webfinger-dir = pkgs.writeTextDir ".well-known/webfinger" ''
+
{
+
"subject": "acct:milo@wiro.world",
+
"aliases": [
+
"mailto:milo@wiro.world",
+
"https://wiro.world/"
+
],
+
"links": [
+
{
+
"rel": "http://wiro.world/rel/avatar",
+
"href": "https://wiro.world/logo.jpg",
+
"type": "image/jpeg"
+
},
+
{
+
"rel": "http://webfinger.net/rel/profile-page",
+
"href": "https://wiro.world/",
+
"type": "text/html"
+
},
+
{
+
"rel": "http://openid.net/specs/connect/1.0/issuer",
+
"href": "https://auth.wiro.world"
+
}
+
]
+
}
+
'';
+
+
well-known-discord-dir = pkgs.writeTextDir ".well-known/discord" ''
+
dh=919234284ceb2aba439d15b9136073eb2308989b
+
'';
+
+
website-hostname = "wiro.world";
+
in
+
{
+
config = {
+
services.caddy = {
+
virtualHosts.${website-hostname}.extraConfig = ''
+
@webfinger {
+
path /.well-known/webfinger
+
method GET HEAD
+
query resource=acct:milo@wiro.world
+
query resource=mailto:milo@wiro.world
+
query resource=https://wiro.world
+
query resource=https://wiro.world/
+
}
+
route @webfinger {
+
header {
+
Content-Type "application/jrd+json"
+
Access-Control-Allow-Origin "*"
+
X-Robots-Tag "noindex"
+
}
+
root ${webfinger-dir}
+
file_server
+
}
+
'' +
+
''
+
@discord {
+
path /.well-known/discord
+
method GET HEAD
+
}
+
route @discord {
+
header {
+
Access-Control-Allow-Origin "*"
+
X-Robots-Tag "noindex"
+
}
+
root ${well-known-discord-dir}
+
file_server
+
}
+
'';
+
};
+
};
+
}
+8 -1
lib/flake/default.nix
···
../../nixos/hardware/${hostName}.nix
../../nixos/profiles/${profile}.nix
];
-
networking.hostName = hostName;
+
config.networking.hostName = hostName;
+
};
+
host = hostName: {
+
imports = [
+
../../nixos/hardware/${hostName}.nix
+
../../hosts/${hostName}/default.nix
+
];
+
config.networking.hostName = hostName;
};
user = import ./user.nix;
managedDiskLayout = import ./managedDiskLayout.nix;
+1 -1
lib/flake/managedDiskLayout.nix
···
The recommended amount from RedHat is:
-
Amount of RAM Recommended swap space Recommended swap space
+
Amount of RAM Recommended swap space Recommended swap space
in the system if allowing for hibernation
—————————————— —————————————————————————— ———————————————————————————
⩽ 2 GB 2 times the amount of RAM 3 times the amount of RAM
+1 -1
modules/home-manager/xcompose.nix
···
loadConfigInEnv = mkOption {
description = ''
Load the XCompose file by passing the `XCOMPOSEFILE` environment variable instead of linking to ~/.XCompose.
-
+
That is nice to avoid cluttering the HOME directory, it's preferable to disable it when experimenting
with your compose config to reload faster than having to reload your VM.
'';
+1 -1
nixos/fragments/kanata/arsenik.kbd.lisp
···
;; Numrow layer
(deflayer numrow
XX XX XX XX XX XX XX XX XX XX XX
-
XX XX XX XX XX XX XX XX XX XX
+
XX XX XX XX XX XX XX XX XX XX
@1 @2 @3 @4 @5 @6 @7 @8 @9 @0
XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX
+2 -2
nixos/fragments/logiops.nix
···
let
cid = {
# Control IDs │ reprog? │ fn key? │ mouse key? │ gesture support?
-
leftMouse = 80; # 0x50 │ │ │ YES │
-
rightMouse = 81; # 0x51 │ │ │ YES │
+
leftMouse = 80; # 0x50 │ │ │ YES │
+
rightMouse = 81; # 0x51 │ │ │ YES │
middleMouse = 81; # 0x52 │ YES │ │ YES │ YES
back = 83; # 0x53 │ YES │ │ YES │ YES
forward = 86; # 0x56 │ YES │ │ YES │ YES
+1 -1
nixos/fragments/security.nix
···
security.polkit.enable = true;
security.rtkit.enable = true;
-
# Systemd Login
+
# Systemd Login
services.logind.settings.Login = {
HandleLidSwitch = "suspend";
IdleAction = "lock";
+1 -1
nixos/profiles/laptop.nix
···
};
users.users.${config.local.user.username}.extraGroups = [ "wireshark" "plugdev" ];
-
# This option is already filled with aliases that snowball and have
+
# This option is already filled with aliases that snowball and have
# priority on fish internal `ls` aliases
environment.shellAliases = { ls = null; ll = null; l = null; };
programs.fish.enable = true;
-724
nixos/profiles/server.nix
···
-
{ self
-
, config
-
, pkgs
-
, ...
-
}:
-
-
let
-
inherit (self.inputs) srvos hypixel-bank-tracker tangled;
-
-
json-format = pkgs.formats.json { };
-
-
ext-if = "eth0";
-
external-ip = "91.99.55.74";
-
external-netmask = 27;
-
external-gw = "144.x.x.255";
-
external-ip6 = "2a01:4f8:c2c:76d2::1";
-
external-netmask6 = 64;
-
external-gw6 = "fe80::1";
-
-
well-known-discord-dir = pkgs.writeTextDir ".well-known/discord" ''
-
dh=919234284ceb2aba439d15b9136073eb2308989b
-
'';
-
webfinger-dir = pkgs.writeTextDir ".well-known/webfinger" ''
-
{
-
"subject": "acct:milo@wiro.world",
-
"aliases": [
-
"mailto:milo@wiro.world",
-
"https://wiro.world/"
-
],
-
"links": [
-
{
-
"rel": "http://wiro.world/rel/avatar",
-
"href": "https://wiro.world/logo.jpg",
-
"type": "image/jpeg"
-
},
-
{
-
"rel": "http://webfinger.net/rel/profile-page",
-
"href": "https://wiro.world/",
-
"type": "text/html"
-
},
-
{
-
"rel": "http://openid.net/specs/connect/1.0/issuer",
-
"href": "https://auth.wiro.world"
-
}
-
]
-
}
-
'';
-
-
website-hostname = "wiro.world";
-
-
pds-port = 3001;
-
pds-hostname = "pds.wiro.world";
-
-
grafana-port = 3002;
-
grafana-hostname = "console.wiro.world";
-
-
tangled-owner = "did:plc:xhgrjm4mcx3p5h3y6eino6ti";
-
tangled-knot-port = 3003;
-
tangled-knot-hostname = "knot.wiro.world";
-
tangled-spindle-port = 3004;
-
tangled-spindle-hostname = "spindle.wiro.world";
-
-
thelounge-port = 3005;
-
thelounge-hostname = "lounge.wiro.world";
-
-
headscale-port = 3006;
-
headscale-derp-port = 3478;
-
headscale-hostname = "headscale.wiro.world";
-
-
lldap-port = 3007;
-
lldap-hostname = "ldap.wiro.world";
-
-
authelia-port = 3008;
-
authelia-hostname = "auth.wiro.world";
-
-
matrix-port = 3009;
-
matrix-hostname = "matrix.wiro.world";
-
-
goatcounter-port = 3010;
-
goatcounter-hostname = "stats.wiro.world";
-
-
vaultwarden-port = 3011;
-
vaultwarden-hostname = "vault.wiro.world";
-
-
miniflux-port = 3012;
-
miniflux-hostname = "news.wiro.world";
-
-
static-hostname = "static.wiro.world";
-
-
hbt-main-port = 3013;
-
hbt-banana-port = 3014;
-
-
warrior-port = 3015;
-
warrior-hostname = "warrior.wiro.world";
-
-
prometheus-port = 9001;
-
prometheus-node-exporter-port = 9002;
-
headscale-metrics-port = 9003;
-
authelia-metrics-port = 9004;
-
in
-
{
-
imports = [
-
srvos.nixosModules.server
-
srvos.nixosModules.hardware-hetzner-cloud
-
srvos.nixosModules.mixins-terminfo
-
-
hypixel-bank-tracker.nixosModules.default
-
-
tangled.nixosModules.knot
-
tangled.nixosModules.spindle
-
];
-
-
config = {
-
boot.loader.grub.enable = true;
-
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" "ext4" ];
-
-
# Single network card is `eth0`
-
networking.usePredictableInterfaceNames = false;
-
-
networking.nameservers = [ "2001:4860:4860::8888" "2001:4860:4860::8844" ];
-
-
networking = {
-
interfaces.${ext-if} = {
-
ipv4.addresses = [{ address = external-ip; prefixLength = external-netmask; }];
-
ipv6.addresses = [{ address = external-ip6; prefixLength = external-netmask6; }];
-
};
-
defaultGateway = { interface = ext-if; address = external-gw; };
-
defaultGateway6 = { interface = ext-if; address = external-gw6; };
-
-
# Reflect firewall configuration on Hetzner
-
firewall.allowedTCPPorts = [ 22 80 443 ];
-
firewall.allowedUDPPorts = [ headscale-derp-port ];
-
};
-
-
services.qemuGuest.enable = true;
-
-
services.openssh.enable = true;
-
-
services.tailscale.enable = true;
-
-
security.sudo.wheelNeedsPassword = false;
-
-
local.fragment.nix.enable = true;
-
-
programs.fish.enable = true;
-
-
services.fail2ban = {
-
enable = true;
-
-
maxretry = 5;
-
ignoreIP = [ ];
-
-
bantime = "24h";
-
bantime-increment = {
-
enable = true;
-
multipliers = "1 2 4 8 16 32 64";
-
maxtime = "168h";
-
overalljails = true;
-
};
-
-
jails = { };
-
};
-
-
services.caddy = {
-
enable = true;
-
-
globalConfig = ''
-
metrics { per_host }
-
-
on_demand_tls {
-
ask http://localhost:${toString pds-port}/tls-check
-
}
-
'';
-
-
virtualHosts.${website-hostname}.extraConfig =
-
''
-
@discord {
-
path /.well-known/discord
-
method GET HEAD
-
}
-
route @discord {
-
header {
-
Access-Control-Allow-Origin "*"
-
X-Robots-Tag "noindex"
-
}
-
root ${well-known-discord-dir}
-
file_server
-
}
-
'' +
-
''
-
@webfinger {
-
path /.well-known/webfinger
-
method GET HEAD
-
query resource=acct:milo@wiro.world
-
query resource=mailto:milo@wiro.world
-
query resource=https://wiro.world
-
query resource=https://wiro.world/
-
}
-
route @webfinger {
-
header {
-
Content-Type "application/jrd+json"
-
Access-Control-Allow-Origin "*"
-
X-Robots-Tag "noindex"
-
}
-
root ${webfinger-dir}
-
file_server
-
}
-
'' +
-
''
-
reverse_proxy /.well-known/matrix/* http://localhost:${toString matrix-port}
-
'' +
-
# TODO: host website on server with automatic deployment
-
''
-
reverse_proxy https://mrnossiom.github.io {
-
header_up Host {http.request.host}
-
}
-
'';
-
-
virtualHosts.${grafana-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString grafana-port}
-
'';
-
-
virtualHosts.${pds-hostname} = {
-
serverAliases = [ "*.${pds-hostname}" ];
-
extraConfig = ''
-
tls { on_demand }
-
reverse_proxy http://localhost:${toString pds-port}
-
'';
-
};
-
-
virtualHosts.${tangled-knot-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString tangled-knot-port}
-
'';
-
-
virtualHosts.${tangled-spindle-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString tangled-spindle-port}
-
'';
-
-
virtualHosts.${thelounge-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString thelounge-port}
-
'';
-
-
virtualHosts.${headscale-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString headscale-port}
-
'';
-
-
virtualHosts.${lldap-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString lldap-port}
-
'';
-
-
virtualHosts.${authelia-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString authelia-port}
-
'';
-
-
virtualHosts.${matrix-hostname}.extraConfig = ''
-
reverse_proxy /_matrix/* http://localhost:${toString matrix-port}
-
'';
-
-
virtualHosts.${goatcounter-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString goatcounter-port}
-
'';
-
-
virtualHosts.${vaultwarden-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString vaultwarden-port}
-
'';
-
-
virtualHosts.${miniflux-hostname}.extraConfig = ''
-
reverse_proxy http://localhost:${toString miniflux-port}
-
'';
-
-
virtualHosts.${static-hostname}.extraConfig = ''
-
root /var/www/static
-
file_server browse
-
'';
-
-
virtualHosts."hypixel-bank-tracker.xyz".extraConfig = ''
-
reverse_proxy http://localhost:${toString hbt-main-port}
-
'';
-
virtualHosts."banana.hypixel-bank-tracker.xyz".extraConfig = ''
-
reverse_proxy http://localhost:${toString hbt-banana-port}
-
'';
-
-
virtualHosts.${warrior-hostname}.extraConfig = ''
-
forward_auth localhost:${toString authelia-port} {
-
uri /api/authz/forward-auth
-
}
-
reverse_proxy http://localhost:${toString warrior-port}
-
'';
-
};
-
-
age.secrets.pds-env.file = ../../secrets/pds-env.age;
-
services.bluesky-pds = {
-
enable = true;
-
-
settings = {
-
PDS_HOSTNAME = "pds.wiro.world";
-
PDS_PORT = pds-port;
-
# is in systemd /tmp subfolder
-
LOG_DESTINATION = "/tmp/pds.log";
-
};
-
-
environmentFiles = [
-
config.age.secrets.pds-env.path
-
];
-
};
-
-
services.tangled.knot = {
-
enable = true;
-
openFirewall = true;
-
-
motd = "Welcome to @wiro.world's knot!\n";
-
server = {
-
listenAddr = "localhost:${toString tangled-knot-port}";
-
hostname = tangled-knot-hostname;
-
owner = tangled-owner;
-
};
-
};
-
-
services.tangled.spindle = {
-
enable = true;
-
-
server = {
-
listenAddr = "localhost:${toString tangled-spindle-port}";
-
hostname = tangled-spindle-hostname;
-
owner = tangled-owner;
-
};
-
};
-
-
age.secrets.grafana-oidc-secret = { file = ../../secrets/grafana-oidc-secret.age; owner = "grafana"; };
-
services.grafana = {
-
enable = true;
-
-
settings = {
-
server = {
-
http_port = grafana-port;
-
domain = grafana-hostname;
-
root_url = "https://${grafana-hostname}";
-
};
-
-
"auth.generic_oauth" = {
-
enable = true;
-
name = "Authelia";
-
icon = "signin";
-
-
client_id = "grafana";
-
client_secret_path = config.age.secrets.grafana-oidc-secret.path;
-
auto_login = true;
-
-
scopes = [ "openid" "profile" "email" "groups" ];
-
auth_url = "https://auth.wiro.world/api/oidc/authorization";
-
token_url = "https://auth.wiro.world/api/oidc/token";
-
api_url = "https://auth.wiro.world/api/oidc/userinfo";
-
use_pkce = true;
-
};
-
};
-
};
-
-
services.prometheus = {
-
enable = true;
-
port = prometheus-port;
-
-
scrapeConfigs = [
-
{
-
job_name = "caddy";
-
static_configs = [{ targets = [ "localhost:${toString 2019}" ]; }];
-
}
-
{
-
job_name = "node-exporter";
-
static_configs = [{ targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; }];
-
}
-
{
-
job_name = "headscale";
-
static_configs = [{ targets = [ "localhost:${toString headscale-metrics-port}" ]; }];
-
}
-
{
-
job_name = "authelia";
-
static_configs = [{ targets = [ "localhost:${toString authelia-metrics-port}" ]; }];
-
}
-
{
-
job_name = "miniflux";
-
static_configs = [{ targets = [ "localhost:${toString miniflux-port}" ]; }];
-
}
-
];
-
-
exporters.node = {
-
enable = true;
-
port = prometheus-node-exporter-port;
-
};
-
};
-
-
services.thelounge = {
-
enable = true;
-
port = thelounge-port;
-
public = false;
-
-
extraConfig = {
-
host = "127.0.0.1";
-
reverseProxy = true;
-
-
# TODO: use ldap, find a way to hide password
-
};
-
};
-
-
age.secrets.headscale-oidc-secret = { file = ../../secrets/headscale-oidc-secret.age; owner = config.services.headscale.user; };
-
# TODO: add dependency on authelia
-
services.headscale = {
-
enable = true;
-
-
port = headscale-port;
-
settings = {
-
server_url = "https://${headscale-hostname}";
-
metrics_listen_addr = "127.0.0.1:${toString headscale-metrics-port}";
-
-
policy.path = json-format.generate "policy.json" {
-
acls = [
-
{
-
action = "accept";
-
src = [ "autogroup:member" ];
-
dst = [ "autogroup:self:*" ];
-
}
-
];
-
ssh = [
-
{
-
action = "accept";
-
src = [ "autogroup:member" ];
-
dst = [ "autogroup:self" ];
-
# Adding root here is privilege escalation as a feature :)
-
users = [ "autogroup:nonroot" ];
-
}
-
];
-
};
-
-
# disable TLS
-
tls_cert_path = null;
-
tls_key_path = null;
-
-
dns = {
-
magic_dns = true;
-
base_domain = "net.wiro.world";
-
-
override_local_dns = true;
-
# Quad9 nameservers
-
nameservers.global = [ "9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9" ];
-
};
-
-
oidc = {
-
only_start_if_oidc_is_available = true;
-
issuer = "https://auth.wiro.world";
-
client_id = "headscale";
-
client_secret_path = config.age.secrets.headscale-oidc-secret.path;
-
scope = [ "openid" "profile" "email" "groups" ];
-
pkce.enabled = true;
-
};
-
-
derp.server = {
-
enable = true;
-
stun_listen_addr = "0.0.0.0:${toString headscale-derp-port}";
-
};
-
};
-
};
-
-
age.secrets.lldap-env.file = ../../secrets/lldap-env.age;
-
users.users.lldap = { isSystemUser = true; group = "lldap"; };
-
users.groups.lldap = { };
-
age.secrets.lldap-user-pass = { file = ../../secrets/lldap-user-pass.age; owner = "lldap"; };
-
services.lldap = {
-
enable = true;
-
settings = {
-
http_url = "https://${lldap-hostname}";
-
http_port = lldap-port;
-
-
ldap_user_pass_file = config.age.secrets.lldap-user-pass.path;
-
force_ldap_user_pass_reset = false;
-
-
ldap_base_dn = "dc=wiro,dc=world";
-
};
-
environmentFile = config.age.secrets.lldap-env.path;
-
};
-
-
age.secrets.authelia-jwt-secret = { file = ../../secrets/authelia-jwt-secret.age; owner = config.services.authelia.instances.main.user; };
-
age.secrets.authelia-issuer-private-key = { file = ../../secrets/authelia-issuer-private-key.age; owner = config.services.authelia.instances.main.user; };
-
age.secrets.authelia-storage-key = { file = ../../secrets/authelia-storage-key.age; owner = config.services.authelia.instances.main.user; };
-
age.secrets.authelia-ldap-password = { file = ../../secrets/authelia-ldap-password.age; owner = config.services.authelia.instances.main.user; };
-
age.secrets.authelia-smtp-password = { file = ../../secrets/authelia-smtp-password.age; owner = config.services.authelia.instances.main.user; };
-
services.authelia.instances.main = {
-
enable = true;
-
-
secrets = {
-
jwtSecretFile = config.age.secrets.authelia-jwt-secret.path;
-
oidcIssuerPrivateKeyFile = config.age.secrets.authelia-issuer-private-key.path;
-
storageEncryptionKeyFile = config.age.secrets.authelia-storage-key.path;
-
};
-
environmentVariables = {
-
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = config.age.secrets.authelia-ldap-password.path;
-
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = config.age.secrets.authelia-smtp-password.path;
-
};
-
settings = {
-
server.address = "localhost:${toString authelia-port}";
-
storage.local.path = "/var/lib/authelia-main/db.sqlite3";
-
telemetry.metrics = {
-
enabled = true;
-
address = "tcp://:${toString authelia-metrics-port}/metrics";
-
};
-
-
session = {
-
cookies = [{
-
domain = "wiro.world";
-
authelia_url = "https://${authelia-hostname}";
-
default_redirection_url = "https://wiro.world";
-
}];
-
};
-
-
authentication_backend.ldap = {
-
address = "ldap://localhost:3890";
-
timeout = "5m"; # replace with systemd dependency
-
-
user = "uid=authelia,ou=people,dc=wiro,dc=world";
-
# Set in `AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE`.
-
# password = "";
-
-
base_dn = "dc=wiro,dc=world";
-
users_filter = "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))";
-
additional_users_dn = "ou=people";
-
groups_filter = "(&(member={dn})(objectClass=groupOfNames))";
-
additional_groups_dn = "ou=groups";
-
-
attributes = {
-
username = "uid";
-
display_name = "cn";
-
given_name = "givenname";
-
family_name = "last_name";
-
mail = "mail";
-
picture = "avatar";
-
-
group_name = "cn";
-
};
-
};
-
-
access_control = {
-
default_policy = "deny";
-
# Rules are sequential and do not apply to OIDC
-
rules = [
-
{
-
domain = "headscale.wiro.world";
-
policy = "two_factor";
-
-
}
-
{
-
domain = "news.wiro.world";
-
policy = "one_factor";
-
-
subject = [ [ "group:miniflux" "oauth2:client:miniflux" ] ];
-
}
-
{
-
domain = "*.wiro.world";
-
policy = "two_factor";
-
}
-
];
-
};
-
-
identity_providers.oidc = {
-
enforce_pkce = "always";
-
-
authorization_policies =
-
let
-
mkStrictPolicy = policy: subject:
-
{ default_policy = "deny"; rules = [{ inherit policy subject; }]; };
-
in
-
{
-
headscale = mkStrictPolicy "two_factor" [ "group:headscale" ];
-
tailscale = mkStrictPolicy "two_factor" [ "group:headscale" ];
-
grafana = mkStrictPolicy "one_factor" [ "group:grafana" ];
-
miniflux = mkStrictPolicy "one_factor" [ "group:miniflux" ];
-
};
-
-
claims_policies.headscale = { id_token = [ "email" "name" "preferred_username" "picture" "groups" ]; };
-
-
clients = [
-
{
-
client_name = "Headscale";
-
client_id = "headscale";
-
client_secret = "$pbkdf2-sha256$310000$XY680D9gkSoWhD0UtYHNFg$ptWB3exOYCga6uq1N.oimuV3ILjK3F8lBWBpsBpibos";
-
redirect_uris = [ "https://${headscale-hostname}/oidc/callback" ];
-
authorization_policy = "headscale";
-
claims_policy = "headscale";
-
}
-
{
-
client_name = "Tailscale";
-
client_id = "tailscale";
-
client_secret = "$pbkdf2-sha256$310000$PcUaup9aWKI9ZLeCF6.avw$FpsTxkDaxcoQlBi8aIacegXpjEDiCI6nXcaHyZ2Sxyc";
-
redirect_uris = [ "https://login.tailscale.com/a/oauth_response" ];
-
authorization_policy = "tailscale";
-
}
-
{
-
client_name = "Grafana Console";
-
client_id = "grafana";
-
client_secret = "$pbkdf2-sha256$310000$UkwrqxTZodGMs9.Ca2cXAA$HCWFgQbFHGXZpuz.I3HHdkTZLUevRVGlhKEFaOlPmKs";
-
redirect_uris = [ "https://${grafana-hostname}/login/generic_oauth" ];
-
authorization_policy = "grafana";
-
}
-
{
-
client_name = "Miniflux";
-
client_id = "miniflux";
-
client_secret = "$pbkdf2-sha256$310000$uPqbWfCOBXDY6nV1vsx3uA$HOWG2hL.c/bs9Dwaee3b9DxjH7KFO.SaZMbasXV9Vdw";
-
redirect_uris = [ "https://${miniflux-hostname}/oauth2/oidc/callback" ];
-
authorization_policy = "miniflux";
-
}
-
];
-
};
-
-
notifier.smtp = {
-
address = "smtp://smtp.resend.com:2587";
-
username = "resend";
-
# Set in `AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE`.
-
# password = "";
-
sender = "authelia@wiro.world";
-
};
-
};
-
};
-
-
age.secrets.tuwunel-registration-tokens = { file = ../../secrets/tuwunel-registration-tokens.age; owner = config.services.matrix-tuwunel.user; };
-
services.matrix-tuwunel = {
-
enable = true;
-
-
settings.global = {
-
address = [ "127.0.0.1" ];
-
port = [ matrix-port ];
-
-
server_name = "wiro.world";
-
well_known = {
-
client = "https://matrix.wiro.world";
-
server = "matrix.wiro.world:443";
-
};
-
-
grant_admin_to_first_user = true;
-
new_user_displayname_suffix = "";
-
-
allow_registration = true;
-
registration_token_file = config.age.secrets.tuwunel-registration-tokens.path;
-
};
-
};
-
-
services.goatcounter = {
-
enable = true;
-
-
port = goatcounter-port;
-
proxy = true;
-
extraArgs = [ "-automigrate" ];
-
};
-
-
age.secrets.vaultwarden-env.file = ../../secrets/vaultwarden-env.age;
-
services.vaultwarden = {
-
enable = true;
-
-
environmentFile = config.age.secrets.vaultwarden-env.path;
-
config = {
-
ROCKET_PORT = vaultwarden-port;
-
DOMAIN = "https://${vaultwarden-hostname}";
-
SIGNUPS_ALLOWED = false;
-
ADMIN_TOKEN = "$argon2id$v=19$m=65540,t=3,p=4$YIe9wmrTsmjgZNPxe8m34O/d3XW3Fl/uZPPLQs79dAc$mjDVQSdBJqz2uBJuxtAvCIoHPzOnTDhNPuhER3dhHrY";
-
-
SMTP_HOST = "smtp.resend.com";
-
SMTP_PORT = 2465;
-
SMTP_SECURITY = "force_tls";
-
SMTP_USERNAME = "resend";
-
# SMTP_PASSWORD = ...; # Via secret env
-
SMTP_FROM = "bitwarden@wiro.world";
-
SMTP_FROM_NAME = "Bitwarden wiro.world";
-
};
-
};
-
-
users.users.miniflux = { isSystemUser = true; group = "miniflux"; };
-
users.groups.miniflux = { };
-
age.secrets.miniflux-oidc-secret = { file = ../../secrets/miniflux-oidc-secret.age; owner = "miniflux"; };
-
services.miniflux = {
-
enable = true;
-
-
createDatabaseLocally = true;
-
config = {
-
BASE_URL = "https://${miniflux-hostname}/";
-
LISTEN_ADDR = "127.0.0.1:${toString miniflux-port}";
-
CREATE_ADMIN = 0;
-
-
METRICS_COLLECTOR = 1;
-
-
OAUTH2_PROVIDER = "oidc";
-
OAUTH2_OIDC_PROVIDER_NAME = "wiro.world SSO";
-
OAUTH2_CLIENT_ID = "miniflux";
-
OAUTH2_CLIENT_SECRET_FILE = config.age.secrets.miniflux-oidc-secret.path;
-
OAUTH2_REDIRECT_URL = "https://${miniflux-hostname}/oauth2/oidc/callback";
-
OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://auth.wiro.world";
-
OAUTH2_USER_CREATION = 1;
-
DISABLE_LOCAL_AUTH = 1;
-
-
RUN_MIGRATIONS = 1;
-
-
# NetNewsWire is a very good iOS oss client that integrates well
-
# https://b.j4.lc/2025/05/05/setting-up-netnewswire-with-miniflux/
-
};
-
};
-
-
age.secrets.hypixel-bank-tracker-main.file = ../../secrets/hypixel-bank-tracker-main.age;
-
services.hypixel-bank-tracker.instances.main = {
-
enable = true;
-
-
port = hbt-main-port;
-
environmentFile = config.age.secrets.hypixel-bank-tracker-main.path;
-
};
-
-
age.secrets.hypixel-bank-tracker-banana.file = ../../secrets/hypixel-bank-tracker-banana.age;
-
services.hypixel-bank-tracker.instances.banana = {
-
enable = true;
-
-
port = hbt-banana-port;
-
environmentFile = config.age.secrets.hypixel-bank-tracker-banana.path;
-
};
-
-
virtualisation.oci-containers.containers.archive-warrior = {
-
image = "atdr.meo.ws/archiveteam/warrior-dockerfile";
-
ports = [ "127.0.0.1:${toString warrior-port}:8001" ];
-
pull = "newer";
-
};
-
};
-
}
+9 -2
secrets.nix
···
let
inherit (builtins) listToAttrs attrNames;
+
+
# Map the name and value of all items of an attrset
mapAttrs' =
f:
set:
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
+
+
keys = import ./secrets/keys.nix;
+
+
prependAttrsName = prefix: mapAttrs' (name: value: { name = prefix + name; inherit value; });
+
secretsDir = path: prependAttrsName (path + "/") ((import ./${path}/default.nix) keys);
in
-
# You can use agenix directly at repo top-level instead of having to change directory into `secrets/`
-
mapAttrs' (name: value: { name = ("secrets/" + name); inherit value; }) (import ./secrets/secrets.nix)
+
secretsDir "secrets"
+
// secretsDir "hosts/weird-row-server/secrets"
secrets/authelia-issuer-private-key.age hosts/weird-row-server/secrets/authelia-issuer-private-key.age
secrets/authelia-jwt-secret.age hosts/weird-row-server/secrets/authelia-jwt-secret.age
secrets/authelia-ldap-password.age hosts/weird-row-server/secrets/authelia-ldap-password.age
secrets/authelia-smtp-password.age hosts/weird-row-server/secrets/authelia-smtp-password.age
secrets/authelia-storage-key.age hosts/weird-row-server/secrets/authelia-storage-key.age
+22
secrets/default.nix
···
+
keys:
+
+
let
+
inherit (keys) sessions systems users;
+
+
nixos = systems ++ users;
+
home-manager = sessions ++ users;
+
in
+
{
+
# Used in NixOS config
+
"backup-rclone-googledrive.age".publicKeys = nixos;
+
"backup-restic-key.age".publicKeys = nixos;
+
+
# Used in Home Manager
+
"api-crates-io.age".publicKeys = home-manager;
+
"api-wakatime.age".publicKeys = home-manager;
+
"api-wakapi.age".publicKeys = home-manager;
+
+
# Not used in config but useful
+
"pgp-ca5e.age".publicKeys = users;
+
"ssh-uxgi.age".publicKeys = users;
+
}
secrets/grafana-oidc-secret.age hosts/weird-row-server/secrets/grafana-oidc-secret.age
secrets/headscale-oidc-secret.age hosts/weird-row-server/secrets/headscale-oidc-secret.age
secrets/hypixel-bank-tracker-banana.age hosts/weird-row-server/secrets/hypixel-bank-tracker-banana.age
secrets/hypixel-bank-tracker-main.age hosts/weird-row-server/secrets/hypixel-bank-tracker-main.age
secrets/lldap-env.age hosts/weird-row-server/secrets/lldap-env.age
secrets/lldap-user-pass.age hosts/weird-row-server/secrets/lldap-user-pass.age
secrets/miniflux-oidc-secret.age hosts/weird-row-server/secrets/miniflux-oidc-secret.age
secrets/pds-env.age hosts/weird-row-server/secrets/pds-env.age
+2 -19
secrets/secrets.nix hosts/weird-row-server/secrets/default.nix
···
+
keys:
let
-
inherit (import ./keys.nix) servers sessions systems users;
-
-
nixos = systems ++ users;
-
home-manager = sessions ++ users;
+
inherit (keys) servers users;
deploy = servers ++ users;
in
{
-
# Used in NixOS config
-
"backup-rclone-googledrive.age".publicKeys = nixos;
-
"backup-restic-key.age".publicKeys = nixos;
-
-
# Used in Home Manager
-
"api-crates-io.age".publicKeys = home-manager;
-
"api-wakatime.age".publicKeys = home-manager;
-
"api-wakapi.age".publicKeys = home-manager;
-
-
# Used in server deployment
-
# Defines `PDS_JWT_SECRET`, `PDS_ADMIN_PASSWORD`, `PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX`, `PDS_EMAIL_SMTP_URL`, `PDS_EMAIL_FROM_ADDRESS`.
"pds-env.age".publicKeys = deploy;
# Defines `LLDAP_JWT_SECRET`, `LLDAP_KEY_SEED`.
···
# Defines `HYPIXEL_API_KEY`, `PROFILE_UUID`
"hypixel-bank-tracker-main.age".publicKeys = deploy;
"hypixel-bank-tracker-banana.age".publicKeys = deploy;
-
-
# Not used in config but useful
-
"pgp-ca5e.age".publicKeys = users;
-
"ssh-uxgi.age".publicKeys = users;
}
secrets/tuwunel-registration-tokens.age hosts/weird-row-server/secrets/tuwunel-registration-tokens.age
secrets/vaultwarden-env.age hosts/weird-row-server/secrets/vaultwarden-env.age