Compare commits
1 commit
master
...
tlater/aut
Author | SHA1 | Date | |
---|---|---|---|
aef71f548a |
69 changed files with 3658 additions and 3641 deletions
.git-blame-ignore-revs.gitignore.sops.yaml
checks
configuration
default.nix
flake.lockflake.nixhardware-specific
nginx.nixservices
afvalcalendar.nixauth.nixbackups.nixbattery-manager.nixconduit.nix
sops.nixconduit
crowdsec.nixfail2ban.nixfoundryvtt.nixgitea.niximmich.nixmetrics
nextcloud.nixpostgres.nixstarbound.nixwebserver.nixwireguard.nixkeys
modules
pkgs
_sources_nextcloud
_sources_pkgs
afvalcalendar
crowdsec
default.nixmkNextcloudApp.nixnextcloud-apps.tomlnvfetcher.tomlprometheus
starbound
|
@ -1,14 +0,0 @@
|
|||
# Run this command to always ignore formatting commits in `git blame`
|
||||
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
|
||||
# Switch to nixfmt formatting
|
||||
04f7a7ef1d38906163afc9cddfa8ce2b0ebf3b45
|
||||
|
||||
# Switch to nixpkgs-fmt formatting
|
||||
fd138d45e6a2cad89fead6e9f246ba282070d6b7
|
||||
|
||||
# Switch to alejandra formatting
|
||||
046a88905ddfa7f9edc3291c310dbb985dee34f9
|
||||
|
||||
# Apply wide linting
|
||||
63b3cbe00be80ccb4b221aad64eb657ae5c96d70
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,2 @@
|
|||
/result
|
||||
*.qcow2
|
||||
/gcroots/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
keys:
|
||||
- &tlater B9C7AE554602D82B1AC2CFD0BC7BB2DB17C78E42!
|
||||
- &tlater 535B61015823443941C744DD12264F6BBDFABA89
|
||||
- &server_tlaternet 8a3737d48f1035fe6c3a0a8fd6a1976ca74c7f3b
|
||||
- &server_hetzner1 0af7641adb8aa843136cf6d047f71da3e5ad79f9
|
||||
- &server_staging 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
deploy-rs,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
statix' = pkgs.statix.overrideAttrs (old: {
|
||||
patches = old.patches ++ [
|
||||
(pkgs.fetchpatch {
|
||||
url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch";
|
||||
hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs=";
|
||||
})
|
||||
];
|
||||
});
|
||||
|
||||
runNuCheck =
|
||||
{
|
||||
name,
|
||||
packages,
|
||||
check,
|
||||
}:
|
||||
pkgs.stdenvNoCC.mkDerivation {
|
||||
inherit name;
|
||||
|
||||
src = nixpkgs.lib.cleanSourceWith {
|
||||
src = self;
|
||||
filter = nixpkgs.lib.cleanSourceFilter;
|
||||
};
|
||||
|
||||
dontPatch = true;
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
dontInstall = true;
|
||||
dontFixup = true;
|
||||
doCheck = true;
|
||||
|
||||
checkInputs = nixpkgs.lib.singleton pkgs.nushell ++ packages;
|
||||
|
||||
checkPhase = ''
|
||||
nu ${check}
|
||||
'';
|
||||
};
|
||||
in
|
||||
nixpkgs.lib.recursiveUpdate {
|
||||
lints = runNuCheck {
|
||||
name = "lints";
|
||||
|
||||
packages = [
|
||||
pkgs.deadnix
|
||||
pkgs.nixfmt-rfc-style
|
||||
pkgs.shellcheck
|
||||
statix'
|
||||
];
|
||||
|
||||
check = ./lints.nu;
|
||||
};
|
||||
} (deploy-rs.lib.${system}.deployChecks self.deploy)
|
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/env nu
|
||||
|
||||
let shell_files = ls **/*.sh | get name
|
||||
let nix_files = ls **/*.nix | where name !~ "hardware-configuration.nix|_sources" | get name
|
||||
|
||||
let linters = [
|
||||
([shellcheck] ++ $shell_files)
|
||||
([nixfmt --check --strict] ++ $nix_files)
|
||||
([deadnix --fail] ++ $nix_files)
|
||||
([statix check] ++ $nix_files)
|
||||
]
|
||||
|
||||
mkdir $env.out
|
||||
|
||||
def run-linter [linterArgs: list<string>] {
|
||||
print $'Running ($linterArgs.0)...'
|
||||
|
||||
let exit_code = try {
|
||||
^$linterArgs.0 ...($linterArgs | skip 1)
|
||||
$env.LAST_EXIT_CODE
|
||||
} catch {|e| $e.exit_code}
|
||||
|
||||
[$linterArgs.0, $exit_code]
|
||||
}
|
||||
|
||||
let results = $linters | each {|linter| run-linter $linter}
|
||||
|
||||
print 'Linter results:'
|
||||
|
||||
let success = $results | each {|result|
|
||||
match $result.1 {
|
||||
0 => {print $'(ansi green)($result.0)(ansi reset)'}
|
||||
_ => {print $'(ansi red)($result.0)(ansi reset)'}
|
||||
}
|
||||
|
||||
$result.1
|
||||
} | math sum
|
||||
|
||||
exit $success
|
|
@ -1,10 +1,11 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
modulesPath,
|
||||
flake-inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
}: {
|
||||
imports = [
|
||||
flake-inputs.disko.nixosModules.disko
|
||||
flake-inputs.sops-nix.nixosModules.sops
|
||||
|
@ -13,40 +14,49 @@
|
|||
"${modulesPath}/profiles/minimal.nix"
|
||||
(import ../modules)
|
||||
|
||||
./services/afvalcalendar.nix
|
||||
./services/auth.nix
|
||||
./services/backups.nix
|
||||
./services/battery-manager.nix
|
||||
./services/conduit
|
||||
./services/crowdsec.nix
|
||||
./services/conduit.nix
|
||||
./services/fail2ban.nix
|
||||
./services/foundryvtt.nix
|
||||
./services/gitea.nix
|
||||
./services/immich.nix
|
||||
./services/metrics
|
||||
./services/nextcloud.nix
|
||||
./services/webserver.nix
|
||||
./services/wireguard.nix
|
||||
# ./services/starbound.nix -- Not currently used
|
||||
./services/starbound.nix
|
||||
./services/postgres.nix
|
||||
./nginx.nix
|
||||
./sops.nix
|
||||
];
|
||||
|
||||
nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ];
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
local = import ../pkgs {
|
||||
pkgs = prev;
|
||||
lib = prev.lib;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
nix = {
|
||||
package = pkgs.nixFlakes;
|
||||
extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
'';
|
||||
|
||||
# Enable remote builds from tlater
|
||||
settings.trusted-users = [ "@wheel" ];
|
||||
settings.trusted-users = ["@wheel"];
|
||||
};
|
||||
|
||||
nixpkgs.config.allowUnfreePredicate = pkg:
|
||||
builtins.elem (lib.getName pkg) ["steam-original" "steam-runtime" "steam-run" "steamcmd"];
|
||||
|
||||
# Optimization for minecraft servers, see:
|
||||
# https://bugs.mojang.com/browse/MC-183518
|
||||
boot.kernelParams = [
|
||||
"highres=off"
|
||||
"nohz=off"
|
||||
];
|
||||
boot.kernelParams = ["highres=off" "nohz=off"];
|
||||
|
||||
networking = {
|
||||
usePredictableInterfaceNames = false;
|
||||
|
@ -97,15 +107,15 @@
|
|||
|
||||
users.users.tlater = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" ];
|
||||
openssh.authorizedKeys.keyFiles = [ ../keys/tlater.pub ];
|
||||
extraGroups = ["wheel"];
|
||||
openssh.authorizedKeys.keyFiles = [../keys/tlater.pub];
|
||||
};
|
||||
|
||||
services = {
|
||||
openssh = {
|
||||
enable = true;
|
||||
allowSFTP = false;
|
||||
ports = [ 2222 ];
|
||||
ports = [2222];
|
||||
startWhenNeeded = true;
|
||||
|
||||
settings = {
|
||||
|
@ -124,14 +134,14 @@
|
|||
pam = {
|
||||
sshAgentAuth = {
|
||||
enable = true;
|
||||
authorizedKeysFiles = [ "/etc/ssh/authorized_keys.d/%u" ];
|
||||
authorizedKeysFiles = ["/etc/ssh/authorized_keys.d/%u"];
|
||||
};
|
||||
services.sudo.sshAgentAuth = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Remove some unneeded packages
|
||||
environment.defaultPackages = [ ];
|
||||
environment.defaultPackages = [];
|
||||
|
||||
system.stateVersion = "20.09";
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# disables it by default.
|
||||
#
|
||||
# TODO(tlater): See if would be useful for anything?
|
||||
boot.kernelParams = [ "nosgx" ];
|
||||
boot.kernelParams = ["nosgx"];
|
||||
|
||||
networking.hostName = "hetzner-1";
|
||||
services.nginx.domain = "tlater.net";
|
||||
|
@ -19,11 +19,15 @@
|
|||
addresses = [
|
||||
# IPv4
|
||||
{
|
||||
Address = "116.202.158.55/32";
|
||||
Peer = "116.202.158.1/32"; # Gateway
|
||||
addressConfig = {
|
||||
Address = "116.202.158.55/32";
|
||||
Peer = "116.202.158.1/32"; # Gateway
|
||||
};
|
||||
}
|
||||
# IPv6
|
||||
{ Address = "2a01:4f8:10b:3c85::2/64"; }
|
||||
{
|
||||
addressConfig.Address = "2a01:4f8:10b:3c85::2/64";
|
||||
}
|
||||
];
|
||||
|
||||
networkConfig = {
|
||||
|
|
|
@ -1,106 +1,82 @@
|
|||
{
|
||||
disko.devices.disk =
|
||||
let
|
||||
bootPartition = {
|
||||
size = "1M";
|
||||
type = "EF02";
|
||||
disko.devices.disk = let
|
||||
bootPartition = {
|
||||
size = "1M";
|
||||
type = "EF02";
|
||||
};
|
||||
|
||||
swapPartition = {
|
||||
# 8G is apparently recommended for this much RAM, but we set up
|
||||
# 4G on both disks for mirroring purposes.
|
||||
#
|
||||
# That'll still be 8G during normal operation, and it's probably
|
||||
# not too bad to have slightly less swap if a disk dies.
|
||||
size = "4G";
|
||||
content = {
|
||||
type = "swap";
|
||||
randomEncryption = true;
|
||||
};
|
||||
};
|
||||
|
||||
swapPartition = {
|
||||
# 8G is apparently recommended for this much RAM, but we set up
|
||||
# 4G on both disks for mirroring purposes.
|
||||
#
|
||||
# That'll still be 8G during normal operation, and it's probably
|
||||
# not too bad to have slightly less swap if a disk dies.
|
||||
size = "4G";
|
||||
content = {
|
||||
type = "swap";
|
||||
randomEncryption = true;
|
||||
};
|
||||
};
|
||||
mountOptions = ["compress=zstd" "noatime"];
|
||||
in {
|
||||
sda = {
|
||||
type = "disk";
|
||||
device = "/dev/sda";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = bootPartition;
|
||||
swap = swapPartition;
|
||||
|
||||
mountOptions = [
|
||||
"compress=zstd"
|
||||
"noatime"
|
||||
];
|
||||
in
|
||||
{
|
||||
sda = {
|
||||
type = "disk";
|
||||
device = "/dev/sda";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = bootPartition;
|
||||
swap = swapPartition;
|
||||
|
||||
disk1 = {
|
||||
size = "100%";
|
||||
# Empty partition to combine in RAID0 with the other disk
|
||||
};
|
||||
disk1 = {
|
||||
size = "100%";
|
||||
# Empty partition to combine in RAID0 with the other disk
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sdb = {
|
||||
type = "disk";
|
||||
device = "/dev/sdb";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = bootPartition;
|
||||
swap = swapPartition;
|
||||
sdb = {
|
||||
type = "disk";
|
||||
device = "/dev/sdb";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = bootPartition;
|
||||
swap = swapPartition;
|
||||
|
||||
disk2 = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "btrfs";
|
||||
# Hack to get multi-device btrfs going
|
||||
# See https://github.com/nix-community/disko/issues/99
|
||||
extraArgs = [
|
||||
"-d"
|
||||
"raid1"
|
||||
"-m"
|
||||
"raid1"
|
||||
"--runtime-features"
|
||||
"quota"
|
||||
"/dev/sda3"
|
||||
];
|
||||
subvolumes = {
|
||||
"/volume" = { };
|
||||
"/volume/root" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/";
|
||||
};
|
||||
"/volume/home" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/home";
|
||||
};
|
||||
"/volume/var" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/var";
|
||||
};
|
||||
"/volume/var/lib/private/matrix-conduit" = {
|
||||
mountOptions = [
|
||||
# Explicitly don't compress here, since
|
||||
# conduwuit's database does compression by
|
||||
# itself, and relies on being able to read the
|
||||
# raw file data from disk (which is impossible
|
||||
# if btrfs compresses it)
|
||||
"noatime"
|
||||
];
|
||||
mountpoint = "/var/lib/private/matrix-conduit";
|
||||
};
|
||||
"/volume/nix-store" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/nix";
|
||||
};
|
||||
"/snapshots" = { };
|
||||
disk2 = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "btrfs";
|
||||
# Hack to get multi-device btrfs going
|
||||
# See https://github.com/nix-community/disko/issues/99
|
||||
extraArgs = ["-d" "raid1" "-m" "raid1" "--runtime-features" "quota" "/dev/sda3"];
|
||||
subvolumes = {
|
||||
"/volume" = {};
|
||||
"/volume/root" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/";
|
||||
};
|
||||
"/volume/home" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/home";
|
||||
};
|
||||
"/volume/var" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/var";
|
||||
};
|
||||
"/volume/nix-store" = {
|
||||
inherit mountOptions;
|
||||
mountpoint = "/nix";
|
||||
};
|
||||
"/snapshots" = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,35 +1,19 @@
|
|||
{ lib, ... }:
|
||||
{
|
||||
{lib, ...}: {
|
||||
users.users.tlater.password = "insecure";
|
||||
|
||||
# Disable graphical tty so -curses works
|
||||
boot.kernelParams = [ "nomodeset" ];
|
||||
boot.kernelParams = ["nomodeset"];
|
||||
|
||||
networking.hostName = "testvm";
|
||||
|
||||
services = {
|
||||
# Sets the base domain for nginx to a local domain so that we can
|
||||
# easily test locally with the VM.
|
||||
nginx.domain = "dev.local";
|
||||
|
||||
# Don't run this
|
||||
batteryManager.enable = lib.mkForce false;
|
||||
|
||||
openssh.hostKeys = lib.mkForce [
|
||||
{
|
||||
type = "rsa";
|
||||
bits = 4096;
|
||||
path = "/etc/staging.key";
|
||||
}
|
||||
];
|
||||
};
|
||||
# Sets the base domain for nginx to a local domain so that we can
|
||||
# easily test locally with the VM.
|
||||
services.nginx.domain = "dev.local";
|
||||
|
||||
# Use the staging secrets
|
||||
sops.defaultSopsFile = lib.mkOverride 99 ../../keys/staging.yaml;
|
||||
|
||||
systemd.network.networks."10-eth0" = {
|
||||
matchConfig.Name = "eth0";
|
||||
gateway = [ "192.168.9.1" ];
|
||||
networkConfig = {
|
||||
Address = "192.168.9.2/24";
|
||||
};
|
||||
|
@ -43,6 +27,14 @@
|
|||
source = ../../keys/hosts/staging.key;
|
||||
};
|
||||
|
||||
services.openssh.hostKeys = lib.mkForce [
|
||||
{
|
||||
type = "rsa";
|
||||
bits = 4096;
|
||||
path = "/etc/staging.key";
|
||||
}
|
||||
];
|
||||
|
||||
virtualisation.vmVariant = {
|
||||
virtualisation = {
|
||||
memorySize = 3941;
|
||||
|
|
|
@ -1,78 +1,67 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
services = {
|
||||
nginx = {
|
||||
enable = true;
|
||||
recommendedTlsSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
recommendedGzipSettings = true;
|
||||
recommendedProxySettings = true;
|
||||
clientMaxBodySize = "10G";
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
recommendedTlsSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
recommendedGzipSettings = true;
|
||||
recommendedProxySettings = true;
|
||||
clientMaxBodySize = "10G";
|
||||
|
||||
statusPage = true; # For metrics, should be accessible only from localhost
|
||||
statusPage = true; # For metrics, should be accessible only from localhost
|
||||
|
||||
commonHttpConfig = ''
|
||||
log_format upstream_time '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" '
|
||||
'rt=$request_time uct="$upstream_connect_time" '
|
||||
'uht="$upstream_header_time" urt="$upstream_response_time"';
|
||||
'';
|
||||
};
|
||||
|
||||
logrotate.settings =
|
||||
{
|
||||
# Override the default, just keep fewer logs
|
||||
nginx.rotate = 6;
|
||||
}
|
||||
// lib.mapAttrs' (
|
||||
virtualHost: _:
|
||||
lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
|
||||
frequency = "daily";
|
||||
rotate = 2;
|
||||
compress = true;
|
||||
delaycompress = true;
|
||||
su = "${config.services.nginx.user} ${config.services.nginx.group}";
|
||||
postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
|
||||
}
|
||||
) config.services.nginx.virtualHosts;
|
||||
|
||||
backups.acme = {
|
||||
user = "acme";
|
||||
paths = lib.mapAttrsToList (
|
||||
virtualHost: _: "/var/lib/acme/${virtualHost}"
|
||||
) config.services.nginx.virtualHosts;
|
||||
};
|
||||
commonHttpConfig = ''
|
||||
log_format upstream_time '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" '
|
||||
'rt=$request_time uct="$upstream_connect_time" '
|
||||
'uht="$upstream_header_time" urt="$upstream_response_time"';
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = lib.mapAttrsToList (
|
||||
virtualHost: _:
|
||||
#
|
||||
"d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}"
|
||||
) config.services.nginx.virtualHosts;
|
||||
services.logrotate.settings =
|
||||
{
|
||||
# Override the default, just keep fewer logs
|
||||
nginx.rotate = 6;
|
||||
}
|
||||
// lib.mapAttrs' (virtualHost: _:
|
||||
lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
|
||||
frequency = "daily";
|
||||
rotate = 2;
|
||||
compress = true;
|
||||
delaycompress = true;
|
||||
su = "${config.services.nginx.user} ${config.services.nginx.group}";
|
||||
postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
|
||||
})
|
||||
config.services.nginx.virtualHosts;
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
lib.mapAttrsToList (
|
||||
virtualHost: _:
|
||||
#
|
||||
"d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}"
|
||||
)
|
||||
config.services.nginx.virtualHosts;
|
||||
|
||||
security.acme = {
|
||||
defaults.email = "tm@tlater.net";
|
||||
acceptTerms = true;
|
||||
|
||||
certs."tlater.net" = {
|
||||
extraDomainNames = [
|
||||
"*.tlater.net"
|
||||
"tlater.com"
|
||||
"*.tlater.com"
|
||||
];
|
||||
dnsProvider = "porkbun";
|
||||
group = "ssl-cert";
|
||||
credentialFiles = {
|
||||
PORKBUN_API_KEY_FILE = config.sops.secrets."porkbun/api-key".path;
|
||||
PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets."porkbun/secret-api-key".path;
|
||||
};
|
||||
extraDomainNames = ["*.tlater.net"];
|
||||
dnsProvider = "hetzner";
|
||||
group = "nginx";
|
||||
credentialFiles."HETZNER_API_KEY_FILE" = config.sops.secrets."hetzner-api".path;
|
||||
};
|
||||
};
|
||||
|
||||
users.groups.ssl-cert = { };
|
||||
|
||||
systemd.services.nginx.serviceConfig.SupplementaryGroups = [
|
||||
config.security.acme.certs."tlater.net".group
|
||||
];
|
||||
services.backups.acme = {
|
||||
user = "acme";
|
||||
paths =
|
||||
lib.mapAttrsToList (virtualHost: _: "/var/lib/acme/${virtualHost}")
|
||||
config.services.nginx.virtualHosts;
|
||||
};
|
||||
}
|
||||
|
|
67
configuration/services/afvalcalendar.nix
Normal file
67
configuration/services/afvalcalendar.nix
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
systemd.services.afvalcalendar = {
|
||||
description = "Enschede afvalcalendar -> ical converter";
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network.target"];
|
||||
|
||||
script = ''
|
||||
${pkgs.local.afvalcalendar}/bin/afvalcalendar > /srv/afvalcalendar/afvalcalendar.ical
|
||||
'';
|
||||
|
||||
startAt = "daily";
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
ProtectHome = true; # Override the default (read-only)
|
||||
PrivateDevices = true;
|
||||
PrivateIPC = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = ["@system-service" "~@privileged @resources @setuid @keyring"];
|
||||
|
||||
Umask = 0002;
|
||||
SupplementaryGroups = "afvalcalendar-hosting";
|
||||
|
||||
ReadWritePaths = "/srv/afvalcalendar";
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."afvalcalendar.${config.services.nginx.domain}" = {
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
root = "/srv/afvalcalendar";
|
||||
};
|
||||
|
||||
users.groups.afvalcalendar-hosting = {};
|
||||
systemd.tmpfiles.settings."10-afvalcalendar" = {
|
||||
"/srv/afvalcalendar".d = {
|
||||
user = "nginx";
|
||||
group = "afvalcalendar-hosting";
|
||||
mode = "0775";
|
||||
};
|
||||
|
||||
"/srv/afvalcalendar/afvalcalendar.ical".f = {
|
||||
user = "nginx";
|
||||
group = "afvalcalendar-hosting";
|
||||
mode = "0775";
|
||||
};
|
||||
};
|
||||
}
|
95
configuration/services/auth.nix
Normal file
95
configuration/services/auth.nix
Normal file
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
user = config.services.authelia.instances.main.user;
|
||||
domain = "auth.${config.services.nginx.domain}";
|
||||
in {
|
||||
services.authelia.instances.main = {
|
||||
enable = true;
|
||||
settings = {
|
||||
theme = "auto";
|
||||
|
||||
access_control.default_policy = "one_factor";
|
||||
|
||||
authentication_backend = {
|
||||
password_reset.disable = true;
|
||||
file.path = "/var/lib/authelia-main/users.yml";
|
||||
};
|
||||
|
||||
notifier.filesystem.filename = "/var/lib/authelia-main/notification.txt";
|
||||
|
||||
session = {
|
||||
domain = config.services.nginx.domain;
|
||||
redis.host = config.services.redis.servers.authelia.unixSocket;
|
||||
};
|
||||
|
||||
storage.postgres = {
|
||||
host = "/run/postgresql";
|
||||
port = 5432;
|
||||
database = user;
|
||||
username = user;
|
||||
|
||||
password = "unnecessary";
|
||||
};
|
||||
};
|
||||
|
||||
secrets = {
|
||||
storageEncryptionKeyFile = config.sops.secrets."authelia/storageEncryptionKey".path; # Database
|
||||
sessionSecretFile = config.sops.secrets."authelia/sessionSecret".path; # Redis
|
||||
jwtSecretFile = config.sops.secrets."authelia/jwtSecret".path;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.authelia-main.after = ["postgresql.service"];
|
||||
|
||||
services.nginx = {
|
||||
# TODO(tlater): Possibly remove on next authelia release
|
||||
additionalModules = with pkgs.nginxModules; [
|
||||
develkit
|
||||
set-misc
|
||||
];
|
||||
|
||||
virtualHosts."${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
enableHSTS = true;
|
||||
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://127.0.0.1:9091";
|
||||
recommendedProxySettings = false;
|
||||
enableAutheliaProxy = true;
|
||||
};
|
||||
|
||||
"/api/verify" = {
|
||||
proxyPass = "http://127.0.0.1:9091";
|
||||
recommendedProxySettings = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.redis.servers.authelia = {
|
||||
inherit user;
|
||||
enable = true;
|
||||
};
|
||||
|
||||
sops.secrets = {
|
||||
"authelia/storageEncryptionKey" = {
|
||||
owner = user;
|
||||
group = user;
|
||||
};
|
||||
|
||||
"authelia/sessionSecret" = {
|
||||
owner = user;
|
||||
group = user;
|
||||
};
|
||||
|
||||
"authelia/jwtSecret" = {
|
||||
owner = user;
|
||||
group = user;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -3,33 +3,27 @@
|
|||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
inherit (lib) types optional singleton;
|
||||
mkShutdownScript =
|
||||
service:
|
||||
mkShutdownScript = service:
|
||||
pkgs.writeShellScript "backup-${service}-shutdown" ''
|
||||
if systemctl is-active --quiet '${service}'; then
|
||||
touch '/tmp/${service}-was-active'
|
||||
systemctl stop '${service}'
|
||||
fi
|
||||
'';
|
||||
mkRestartScript =
|
||||
service:
|
||||
mkRestartScript = service:
|
||||
pkgs.writeShellScript "backup-${service}-restart" ''
|
||||
if [ -f '/tmp/${service}-was-active' ]; then
|
||||
rm '/tmp/${service}-was-active'
|
||||
systemctl start '${service}'
|
||||
fi
|
||||
'';
|
||||
writeScript =
|
||||
name: packages: text:
|
||||
lib.getExe (
|
||||
pkgs.writeShellApplication {
|
||||
inherit name text;
|
||||
runtimeInputs = packages;
|
||||
}
|
||||
);
|
||||
writeScript = name: packages: text:
|
||||
lib.getExe (pkgs.writeShellApplication {
|
||||
inherit name text;
|
||||
runtimeInputs = packages;
|
||||
});
|
||||
|
||||
# *NOT* a TOML file, for some reason quotes are interpreted
|
||||
# *literally
|
||||
|
@ -48,98 +42,96 @@ let
|
|||
RESTIC_REPOSITORY = "rclone:storagebox:backups";
|
||||
RCLONE_CONFIG = rcloneConfig;
|
||||
};
|
||||
in
|
||||
{
|
||||
in {
|
||||
options = {
|
||||
services.backups = lib.mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Configure restic backups with a specific tag.
|
||||
'';
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
user = lib.mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The user as which to run the backup.
|
||||
'';
|
||||
};
|
||||
paths = lib.mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
The paths to back up.
|
||||
'';
|
||||
};
|
||||
tag = lib.mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The restic tag to mark the backup with.
|
||||
'';
|
||||
default = name;
|
||||
};
|
||||
preparation = {
|
||||
packages = lib.mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
description = ''
|
||||
The list of packages to make available in the
|
||||
preparation script.
|
||||
'';
|
||||
};
|
||||
text = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The preparation script to run before the backup.
|
||||
|
||||
This should include things like database dumps and
|
||||
enabling maintenance modes. If a service needs to be
|
||||
shut down for backups, use `pauseServices` instead.
|
||||
'';
|
||||
};
|
||||
};
|
||||
cleanup = {
|
||||
packages = lib.mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
description = ''
|
||||
The list of packages to make available in the
|
||||
cleanup script.
|
||||
'';
|
||||
};
|
||||
text = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The cleanup script to run after the backup.
|
||||
|
||||
This should do things like cleaning up database dumps
|
||||
and disabling maintenance modes.
|
||||
'';
|
||||
};
|
||||
};
|
||||
pauseServices = lib.mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
The systemd services that need to be shut down before
|
||||
the backup can run. Services will be restarted after the
|
||||
backup is complete.
|
||||
|
||||
This is intended to be used for services that do not
|
||||
support hot backups.
|
||||
'';
|
||||
};
|
||||
type = types.attrsOf (types.submodule ({
|
||||
config,
|
||||
name,
|
||||
...
|
||||
}: {
|
||||
options = {
|
||||
user = lib.mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The user as which to run the backup.
|
||||
'';
|
||||
};
|
||||
paths = lib.mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
The paths to back up.
|
||||
'';
|
||||
};
|
||||
tag = lib.mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The restic tag to mark the backup with.
|
||||
'';
|
||||
default = name;
|
||||
};
|
||||
preparation = {
|
||||
packages = lib.mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
description = ''
|
||||
The list of packages to make available in the
|
||||
preparation script.
|
||||
'';
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
text = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The preparation script to run before the backup.
|
||||
|
||||
This should include things like database dumps and
|
||||
enabling maintenance modes. If a service needs to be
|
||||
shut down for backups, use `pauseServices` instead.
|
||||
'';
|
||||
};
|
||||
};
|
||||
cleanup = {
|
||||
packages = lib.mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
description = ''
|
||||
The list of packages to make available in the
|
||||
cleanup script.
|
||||
'';
|
||||
};
|
||||
text = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The cleanup script to run after the backup.
|
||||
|
||||
This should do things like cleaning up database dumps
|
||||
and disabling maintenance modes.
|
||||
'';
|
||||
};
|
||||
};
|
||||
pauseServices = lib.mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
The systemd services that need to be shut down before
|
||||
the backup can run. Services will be restarted after the
|
||||
backup is complete.
|
||||
|
||||
This is intended to be used for services that do not
|
||||
support hot backups.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.services.backups != { }) {
|
||||
config = lib.mkIf (config.services.backups != {}) {
|
||||
systemd.services =
|
||||
{
|
||||
restic-prune = {
|
||||
|
@ -172,15 +164,16 @@ in
|
|||
};
|
||||
};
|
||||
}
|
||||
// lib.mapAttrs' (
|
||||
name: backup:
|
||||
// lib.mapAttrs' (name: backup:
|
||||
lib.nameValuePair "backup-${name}" {
|
||||
# Don't want to restart mid-backup
|
||||
restartIfChanged = false;
|
||||
|
||||
environment = resticEnv // {
|
||||
RESTIC_CACHE_DIR = "%C/backup-${name}";
|
||||
};
|
||||
environment =
|
||||
resticEnv
|
||||
// {
|
||||
RESTIC_CACHE_DIR = "%C/backup-${name}";
|
||||
};
|
||||
|
||||
path = with pkgs; [
|
||||
coreutils
|
||||
|
@ -203,60 +196,47 @@ in
|
|||
PrivateTmp = true;
|
||||
|
||||
ExecStart = [
|
||||
(lib.concatStringsSep " " (
|
||||
[
|
||||
"${pkgs.restic}/bin/restic"
|
||||
"backup"
|
||||
"--tag"
|
||||
name
|
||||
]
|
||||
++ backup.paths
|
||||
))
|
||||
(lib.concatStringsSep " " (["${pkgs.restic}/bin/restic" "backup" "--tag" name] ++ backup.paths))
|
||||
];
|
||||
|
||||
ExecStartPre =
|
||||
map (service: "+${mkShutdownScript service}") backup.pauseServices
|
||||
++ singleton (
|
||||
writeScript "backup-${name}-repo-init" [ ] ''
|
||||
restic snapshots || restic init
|
||||
''
|
||||
)
|
||||
++ optional (backup.preparation.text != null) (
|
||||
writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text
|
||||
);
|
||||
++ singleton (writeScript "backup-${name}-repo-init" [] ''
|
||||
restic snapshots || restic init
|
||||
'')
|
||||
++ optional (backup.preparation.text != null)
|
||||
(writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text);
|
||||
|
||||
# TODO(tlater): Add repo pruning/checking
|
||||
ExecStopPost =
|
||||
map (service: "+${mkRestartScript service}") backup.pauseServices
|
||||
++ optional (backup.cleanup.text != null) (
|
||||
writeScript "backup-${name}-cleanup" backup.cleanup.packages backup.cleanup.text
|
||||
);
|
||||
++ optional (backup.cleanup.text != null)
|
||||
(writeScript "backup-${name}-cleanup" backup.cleanup.packages backup.cleanup.text);
|
||||
};
|
||||
}
|
||||
) config.services.backups;
|
||||
})
|
||||
config.services.backups;
|
||||
|
||||
systemd.timers =
|
||||
{
|
||||
restic-prune = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
wantedBy = ["timers.target"];
|
||||
timerConfig.OnCalendar = "Thursday 03:00:00 UTC";
|
||||
# Don't make this persistent, in case the server was offline
|
||||
# for a while. This job cannot run at the same time as any
|
||||
# of the backup jobs.
|
||||
};
|
||||
}
|
||||
// lib.mapAttrs' (
|
||||
name: _:
|
||||
// lib.mapAttrs' (name: backup:
|
||||
lib.nameValuePair "backup-${name}" {
|
||||
wantedBy = [ "timers.target" ];
|
||||
wantedBy = ["timers.target"];
|
||||
timerConfig = {
|
||||
OnCalendar = "Wednesday 02:30:00 UTC";
|
||||
RandomizedDelaySec = "1h";
|
||||
FixedRandomDelay = true;
|
||||
Persistent = true;
|
||||
};
|
||||
}
|
||||
) config.services.backups;
|
||||
})
|
||||
config.services.backups;
|
||||
|
||||
users = {
|
||||
# This user is only used to own the ssh key, because apparently
|
||||
|
@ -265,7 +245,7 @@ in
|
|||
group = "backup";
|
||||
isSystemUser = true;
|
||||
};
|
||||
groups.backup = { };
|
||||
groups.backup = {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
{ config, flake-inputs, ... }:
|
||||
{
|
||||
imports = [ flake-inputs.sonnenshift.nixosModules.default ];
|
||||
config,
|
||||
flake-inputs,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
flake-inputs.sonnenshift.nixosModules.default
|
||||
];
|
||||
|
||||
services.batteryManager = {
|
||||
enable = true;
|
||||
battery = "3ca39300-c523-4315-b9a3-d030f85a9373";
|
||||
|
||||
emailFile = "${config.sops.secrets."battery-manager/email".path}";
|
||||
passwordFile = "${config.sops.secrets."battery-manager/password".path}";
|
||||
|
||||
settings = {
|
||||
battery_id = "3ca39300-c523-4315-b9a3-d030f85a9373";
|
||||
log_level = "DEBUG";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
254
configuration/services/conduit.nix
Normal file
254
configuration/services/conduit.nix
Normal file
|
@ -0,0 +1,254 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit (lib.strings) concatMapStringsSep;
|
||||
|
||||
cfg = config.services.matrix-conduit;
|
||||
domain = "matrix.${config.services.nginx.domain}";
|
||||
turn-realm = "turn.${config.services.nginx.domain}";
|
||||
in {
|
||||
services.matrix-conduit = {
|
||||
enable = true;
|
||||
settings.global = {
|
||||
address = "127.0.0.1";
|
||||
server_name = domain;
|
||||
database_backend = "rocksdb";
|
||||
|
||||
turn_uris = let
|
||||
address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
|
||||
tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
|
||||
in [
|
||||
"turn:${address}?transport=udp"
|
||||
"turn:${address}?transport=tcp"
|
||||
"turns:${tls-address}?transport=udp"
|
||||
"turns:${tls-address}?transport=tcp"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.heisenbridge = let
|
||||
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
|
||||
registrationFile = builtins.toFile "heisenbridge-registration.yaml" (builtins.toJSON {
|
||||
id = "heisenbridge";
|
||||
url = "http://127.0.0.1:9898";
|
||||
as_token = "@AS_TOKEN@";
|
||||
hs_token = "@HS_TOKEN@";
|
||||
rate_limited = false;
|
||||
sender_localpart = "heisenbridge";
|
||||
namespaces = {
|
||||
users = [
|
||||
{
|
||||
regex = "@irc_.*";
|
||||
exclusive = true;
|
||||
}
|
||||
{
|
||||
regex = "@heisenbridge:.*";
|
||||
exclusive = true;
|
||||
}
|
||||
];
|
||||
aliases = [];
|
||||
rooms = [];
|
||||
};
|
||||
});
|
||||
|
||||
# TODO(tlater): Starting with systemd 253 it will become possible
|
||||
# to do the credential setup as part of ExecStartPre/preStart
|
||||
# instead.
|
||||
#
|
||||
# This will also make it possible to actually set caps on the
|
||||
# heisenbridge process using systemd, so that we can run the
|
||||
# identd process.
|
||||
execScript = pkgs.writeShellScript "heisenbridge" ''
|
||||
cp ${registrationFile} "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
|
||||
chmod 600 $RUNTIME_DIRECTORY/heisenbridge-registration.yaml
|
||||
${replaceSecretBin} '@AS_TOKEN@' "$CREDENTIALS_DIRECTORY/heisenbridge_as-token" "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
|
||||
${replaceSecretBin} '@HS_TOKEN@' "$CREDENTIALS_DIRECTORY/heisenbridge_hs-token" "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
|
||||
chmod 400 $RUNTIME_DIRECTORY/heisenbridge-registration.yaml
|
||||
|
||||
${pkgs.heisenbridge}/bin/heisenbridge \
|
||||
--config $RUNTIME_DIRECTORY/heisenbridge-registration.yaml \
|
||||
--owner @tlater:matrix.tlater.net \
|
||||
'http://localhost:${toString cfg.settings.global.port}'
|
||||
'';
|
||||
in {
|
||||
description = "Matrix<->IRC bridge";
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["conduit.service"];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
|
||||
LoadCredential = "heisenbridge:/run/secrets/heisenbridge";
|
||||
|
||||
ExecStart = execScript;
|
||||
|
||||
DynamicUser = true;
|
||||
RuntimeDirectory = "heisenbridge";
|
||||
RuntimeDirectoryMode = "0700";
|
||||
|
||||
RestrictNamespaces = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = ["AF_INET AF_INET6"];
|
||||
LockPersonality = true;
|
||||
RestrictRealtime = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
UMask = 0077;
|
||||
|
||||
# For the identd port
|
||||
# CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"];
|
||||
# AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
|
||||
};
|
||||
};
|
||||
|
||||
# Pass in the TURN secret via EnvironmentFile, not supported by
|
||||
# upstream module currently.
|
||||
#
|
||||
# See also https://gitlab.com/famedly/conduit/-/issues/314
|
||||
systemd.services.conduit.serviceConfig.EnvironmentFile = config.sops.secrets."turn/env".path;
|
||||
|
||||
services.coturn = {
|
||||
enable = true;
|
||||
no-cli = true;
|
||||
use-auth-secret = true;
|
||||
static-auth-secret-file = config.sops.secrets."turn/secret".path;
|
||||
realm = turn-realm;
|
||||
relay-ips = [
|
||||
"116.202.158.55"
|
||||
];
|
||||
|
||||
# SSL config
|
||||
#
|
||||
# TODO(tlater): Switch to letsencrypt once google fix:
|
||||
# https://github.com/vector-im/element-android/issues/1533
|
||||
pkey = config.sops.secrets."turn/ssl-key".path;
|
||||
cert = config.sops.secrets."turn/ssl-cert".path;
|
||||
|
||||
# Based on suggestions from
|
||||
# https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
|
||||
# and
|
||||
# https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
|
||||
no-tcp-relay = true;
|
||||
secure-stun = true;
|
||||
extraConfig = ''
|
||||
# Deny various local IP ranges, see
|
||||
# https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
|
||||
no-multicast-peers
|
||||
denied-peer-ip=0.0.0.0-0.255.255.255
|
||||
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||
denied-peer-ip=100.64.0.0-100.127.255.255
|
||||
denied-peer-ip=127.0.0.0-127.255.255.255
|
||||
denied-peer-ip=169.254.0.0-169.254.255.255
|
||||
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||
denied-peer-ip=192.0.0.0-192.0.0.255
|
||||
denied-peer-ip=192.0.2.0-192.0.2.255
|
||||
denied-peer-ip=192.88.99.0-192.88.99.255
|
||||
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
denied-peer-ip=198.18.0.0-198.19.255.255
|
||||
denied-peer-ip=198.51.100.0-198.51.100.255
|
||||
denied-peer-ip=203.0.113.0-203.0.113.255
|
||||
denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1
|
||||
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
|
||||
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
|
||||
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
|
||||
# *Allow* any IP addresses that we explicitly set as relay IPs
|
||||
${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
|
||||
|
||||
# Various other security settings
|
||||
no-tlsv1
|
||||
no-tlsv1_1
|
||||
|
||||
# Monitoring
|
||||
prometheus
|
||||
'';
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."${domain}" = {
|
||||
useACMEHost = "tlater.net";
|
||||
|
||||
listen = [
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
addr = "[::0]";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 443;
|
||||
ssl = true;
|
||||
}
|
||||
{
|
||||
addr = "[::0]";
|
||||
port = 443;
|
||||
ssl = true;
|
||||
}
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 8448;
|
||||
ssl = true;
|
||||
}
|
||||
{
|
||||
addr = "[::0]";
|
||||
port = 8448;
|
||||
ssl = true;
|
||||
}
|
||||
];
|
||||
|
||||
forceSSL = true;
|
||||
enableHSTS = true;
|
||||
extraConfig = ''
|
||||
merge_slashes off;
|
||||
'';
|
||||
|
||||
locations = {
|
||||
"/_matrix" = {
|
||||
proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
|
||||
# Recommended by conduit
|
||||
extraConfig = ''
|
||||
proxy_buffering off;
|
||||
'';
|
||||
};
|
||||
|
||||
# Add Element X support
|
||||
# TODO(tlater): Remove when no longer required: https://github.com/vector-im/element-x-android/issues/1085
|
||||
"=/.well-known/matrix/client" = {
|
||||
alias = pkgs.writeText "well-known-matrix-client" (builtins.toJSON {
|
||||
"m.homeserver".base_url = "https://${domain}";
|
||||
"org.matrix.msc3575.proxy".url = "https://${domain}";
|
||||
});
|
||||
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.backups.conduit = {
|
||||
user = "root";
|
||||
paths = [
|
||||
"/var/lib/private/matrix-conduit/"
|
||||
];
|
||||
# Other services store their data in conduit, so no other services
|
||||
# need to be shut down currently.
|
||||
pauseServices = ["conduit.service"];
|
||||
};
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib.strings) concatMapStringsSep;
|
||||
|
||||
cfg = config.services.matrix-conduit;
|
||||
domain = "matrix.${config.services.nginx.domain}";
|
||||
turn-realm = "turn.${config.services.nginx.domain}";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./heisenbridge.nix
|
||||
./matrix-hookshot.nix
|
||||
];
|
||||
|
||||
services = {
|
||||
matrix-conduit = {
|
||||
enable = true;
|
||||
package = pkgs.matrix-continuwuity;
|
||||
settings.global = {
|
||||
address = "127.0.0.1";
|
||||
server_name = domain;
|
||||
new_user_displayname_suffix = "🦆";
|
||||
allow_check_for_updates = true;
|
||||
|
||||
# Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended
|
||||
# This is primarily to make sliding sync work
|
||||
well_known = {
|
||||
client = "https://${domain}";
|
||||
server = "${domain}:443";
|
||||
};
|
||||
|
||||
turn_uris =
|
||||
let
|
||||
address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
|
||||
tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
|
||||
in
|
||||
[
|
||||
"turn:${address}?transport=udp"
|
||||
"turn:${address}?transport=tcp"
|
||||
"turns:${tls-address}?transport=udp"
|
||||
"turns:${tls-address}?transport=tcp"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
coturn = {
|
||||
enable = true;
|
||||
no-cli = true;
|
||||
use-auth-secret = true;
|
||||
static-auth-secret-file = config.sops.secrets."turn/secret".path;
|
||||
realm = turn-realm;
|
||||
relay-ips = [ "116.202.158.55" ];
|
||||
|
||||
# SSL config
|
||||
pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem";
|
||||
cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem";
|
||||
|
||||
# Based on suggestions from
|
||||
# https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
|
||||
# and
|
||||
# https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
|
||||
no-tcp-relay = true;
|
||||
secure-stun = true;
|
||||
extraConfig = ''
|
||||
# Deny various local IP ranges, see
|
||||
# https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
|
||||
no-multicast-peers
|
||||
denied-peer-ip=0.0.0.0-0.255.255.255
|
||||
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||
denied-peer-ip=100.64.0.0-100.127.255.255
|
||||
denied-peer-ip=127.0.0.0-127.255.255.255
|
||||
denied-peer-ip=169.254.0.0-169.254.255.255
|
||||
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||
denied-peer-ip=192.0.0.0-192.0.0.255
|
||||
denied-peer-ip=192.0.2.0-192.0.2.255
|
||||
denied-peer-ip=192.88.99.0-192.88.99.255
|
||||
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
denied-peer-ip=198.18.0.0-198.19.255.255
|
||||
denied-peer-ip=198.51.100.0-198.51.100.255
|
||||
denied-peer-ip=203.0.113.0-203.0.113.255
|
||||
denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1
|
||||
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
|
||||
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
|
||||
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
|
||||
# *Allow* any IP addresses that we explicitly set as relay IPs
|
||||
${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
|
||||
|
||||
# Various other security settings
|
||||
no-tlsv1
|
||||
no-tlsv1_1
|
||||
|
||||
# Monitoring
|
||||
prometheus
|
||||
'';
|
||||
};
|
||||
|
||||
nginx.virtualHosts."${domain}" = {
|
||||
useACMEHost = "tlater.net";
|
||||
|
||||
listen = [
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
addr = "[::0]";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 443;
|
||||
ssl = true;
|
||||
}
|
||||
{
|
||||
addr = "[::0]";
|
||||
port = 443;
|
||||
ssl = true;
|
||||
}
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 8448;
|
||||
ssl = true;
|
||||
}
|
||||
{
|
||||
addr = "[::0]";
|
||||
port = 8448;
|
||||
ssl = true;
|
||||
}
|
||||
];
|
||||
|
||||
forceSSL = true;
|
||||
enableHSTS = true;
|
||||
extraConfig = ''
|
||||
merge_slashes off;
|
||||
'';
|
||||
|
||||
locations = {
|
||||
"/_matrix" = {
|
||||
proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
|
||||
# Recommended by conduit
|
||||
extraConfig = ''
|
||||
proxy_buffering off;
|
||||
'';
|
||||
};
|
||||
"/.well-known/matrix" = {
|
||||
proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
backups.conduit = {
|
||||
user = "root";
|
||||
paths = [ "/var/lib/private/matrix-conduit/" ];
|
||||
# Other services store their data in conduit, so no other services
|
||||
# need to be shut down currently.
|
||||
pauseServices = [ "conduit.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.conduit.serviceConfig = {
|
||||
ExecStart = lib.mkForce "${config.services.matrix-conduit.package}/bin/conduwuit";
|
||||
# Pass in the TURN secret via EnvironmentFile, not supported by
|
||||
# upstream module currently.
|
||||
#
|
||||
# See also https://gitlab.com/famedly/conduit/-/issues/314
|
||||
EnvironmentFile = config.sops.secrets."turn/env".path;
|
||||
};
|
||||
|
||||
systemd.services.coturn.serviceConfig.SupplementaryGroups = [
|
||||
config.security.acme.certs."tlater.net".group
|
||||
];
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
conduitCfg = config.services.matrix-conduit;
|
||||
matrixLib = pkgs.callPackage ./lib.nix { };
|
||||
in
|
||||
{
|
||||
systemd.services.heisenbridge =
|
||||
let
|
||||
registration = matrixLib.writeRegistrationScript {
|
||||
id = "heisenbridge";
|
||||
url = "http://127.0.0.1:9898";
|
||||
sender_localpart = "heisenbridge";
|
||||
|
||||
namespaces = {
|
||||
users = [
|
||||
{
|
||||
regex = "@irc_.*";
|
||||
exclusive = true;
|
||||
}
|
||||
{
|
||||
regex = "@heisenbridge:.*";
|
||||
exclusive = true;
|
||||
}
|
||||
];
|
||||
|
||||
aliases = [ ];
|
||||
rooms = [ ];
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
description = "Matrix<->IRC bridge";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "conduit.service" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "exec";
|
||||
|
||||
LoadCredential = "heisenbridge:/run/secrets/heisenbridge";
|
||||
|
||||
inherit (registration) ExecStartPre;
|
||||
ExecStart = lib.concatStringsSep " " [
|
||||
"${lib.getExe pkgs.heisenbridge}"
|
||||
"--config \${RUNTIME_DIRECTORY}/heisenbridge-registration.yaml"
|
||||
"--owner @tlater:matrix.tlater.net"
|
||||
"http://localhost:${toString conduitCfg.settings.global.port}"
|
||||
];
|
||||
|
||||
DynamicUser = true;
|
||||
RuntimeDirectory = "heisenbridge";
|
||||
RuntimeDirectoryMode = "0700";
|
||||
|
||||
RestrictNamespaces = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
|
||||
LockPersonality = true;
|
||||
RestrictRealtime = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
UMask = 77;
|
||||
|
||||
# For the identd port
|
||||
# CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
|
||||
# AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
lib,
|
||||
writeShellScript,
|
||||
formats,
|
||||
replace-secret,
|
||||
}:
|
||||
let
|
||||
replaceSecretBin = "${lib.getExe replace-secret}";
|
||||
in
|
||||
{
|
||||
# Write a script that will set up the service's registration.yaml
|
||||
# with secrets from systemd credentials.
|
||||
#
|
||||
# The credentials should be named `${id}_as-token` and
|
||||
# `${id}_hs-token`.
|
||||
#
|
||||
# This registration file needs to be manually added to conduit by
|
||||
# messaging the admin with the yaml file.
|
||||
#
|
||||
# TODO(tlater): Conduwuit seems to support a CLI interface for this,
|
||||
# may want to migrate to that sometime.
|
||||
writeRegistrationScript =
|
||||
{
|
||||
id, # Must be unique among all registered appservices/bots
|
||||
url, # The URL on which the service listens
|
||||
sender_localpart,
|
||||
rate_limited ? false,
|
||||
namespaces ? {
|
||||
aliases = [ ];
|
||||
rooms = [ ];
|
||||
users = [ ];
|
||||
},
|
||||
extraSettings ? { },
|
||||
# The location to place the file; assumes systemd runtime dir
|
||||
runtimeRegistration ? "$RUNTIME_DIRECTORY/${id}-registration.yaml",
|
||||
}:
|
||||
let
|
||||
registrationFile = (formats.yaml { }).generate "${id}-registration.yaml" (
|
||||
{
|
||||
inherit
|
||||
id
|
||||
url
|
||||
sender_localpart
|
||||
rate_limited
|
||||
namespaces
|
||||
;
|
||||
|
||||
as_token = "@AS_TOKEN@";
|
||||
hs_token = "@HS_TOKEN@";
|
||||
}
|
||||
// extraSettings
|
||||
);
|
||||
in
|
||||
{
|
||||
inherit runtimeRegistration;
|
||||
ExecStartPre = writeShellScript "${id}-registration-setup.sh" ''
|
||||
cp -f ${registrationFile} "${runtimeRegistration}"
|
||||
chmod 600 "${runtimeRegistration}"
|
||||
|
||||
# Write actual secrets into config
|
||||
${replaceSecretBin} '@AS_TOKEN@' "$CREDENTIALS_DIRECTORY/${id}_as-token" "${runtimeRegistration}"
|
||||
${replaceSecretBin} '@HS_TOKEN@' "$CREDENTIALS_DIRECTORY/${id}_hs-token" "${runtimeRegistration}"
|
||||
|
||||
chmod 400 "${runtimeRegistration}"
|
||||
'';
|
||||
};
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
matrixLib = pkgs.callPackage ./lib.nix { };
|
||||
|
||||
cfg = config.services.matrix-hookshot;
|
||||
conduitCfg = config.services.matrix-conduit;
|
||||
|
||||
domain = conduitCfg.settings.global.server_name;
|
||||
|
||||
registration = matrixLib.writeRegistrationScript {
|
||||
id = "matrix-hookshot";
|
||||
url = "http://127.0.0.1:9993";
|
||||
sender_localpart = "hookshot";
|
||||
|
||||
namespaces = {
|
||||
aliases = [ ];
|
||||
rooms = [ ];
|
||||
users = [
|
||||
{
|
||||
regex = "@${cfg.settings.generic.userIdPrefix}.*:${domain}";
|
||||
exclusive = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Encryption support
|
||||
# TODO(tlater): Enable when
|
||||
# https://github.com/matrix-org/matrix-hookshot/issues/1060 is
|
||||
# fixed
|
||||
# extraSettings = {
|
||||
# "de.sorunome.msc2409.push_ephemeral" = true;
|
||||
# push_ephemeral = true;
|
||||
# "org.matrix.msc3202" = true;
|
||||
# };
|
||||
|
||||
runtimeRegistration = "${cfg.registrationFile}";
|
||||
};
|
||||
in
|
||||
{
|
||||
# users = {
|
||||
# users.matrix-hookshot = {
|
||||
# home = "/run/matrix-hookshot";
|
||||
# group = "matrix-hookshot";
|
||||
# isSystemUser = true;
|
||||
# };
|
||||
|
||||
# groups.matrix-hookshot = { };
|
||||
# };
|
||||
|
||||
systemd.services.matrix-hookshot = {
|
||||
serviceConfig = {
|
||||
Type = lib.mkForce "exec";
|
||||
|
||||
LoadCredential = "matrix-hookshot:/run/secrets/matrix-hookshot";
|
||||
inherit (registration) ExecStartPre;
|
||||
|
||||
# Some library in matrix-hookshot wants a home directory
|
||||
Environment = [ "HOME=/run/matrix-hookshot" ];
|
||||
|
||||
# User = "matrix-hookshot";
|
||||
DynamicUser = true;
|
||||
StateDirectory = "matrix-hookshot";
|
||||
RuntimeDirectory = "matrix-hookshot";
|
||||
RuntimeDirectoryMode = "0700";
|
||||
|
||||
RestrictNamespaces = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [
|
||||
# "AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
LockPersonality = true;
|
||||
RestrictRealtime = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
UMask = 77;
|
||||
};
|
||||
};
|
||||
|
||||
# services.redis.servers.matrix-hookshot = {
|
||||
# enable = true;
|
||||
# user = "matrix-hookshot";
|
||||
# };
|
||||
|
||||
services.matrix-hookshot = {
|
||||
enable = true;
|
||||
|
||||
serviceDependencies = [ "conduit.service" ];
|
||||
|
||||
registrationFile = "/run/matrix-hookshot/registration.yaml";
|
||||
|
||||
settings = {
|
||||
bridge = {
|
||||
inherit domain;
|
||||
url = "http://localhost:${toString conduitCfg.settings.global.port}";
|
||||
mediaUrl = conduitCfg.settings.global.well_known.client;
|
||||
port = 9993;
|
||||
bindAddress = "127.0.0.1";
|
||||
};
|
||||
|
||||
bot.displayname = "Hookshot";
|
||||
|
||||
# cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}";
|
||||
|
||||
generic = {
|
||||
enabled = true;
|
||||
outbound = false;
|
||||
# Only allow webhooks from localhost for the moment
|
||||
urlPrefix = "http://127.0.0.1:9000/webhook";
|
||||
userIdPrefix = "_webhooks_";
|
||||
allowJsTransformationFunctions = true;
|
||||
};
|
||||
|
||||
# TODO(tlater): Enable when
|
||||
# https://github.com/matrix-org/matrix-hookshot/issues/1060 is
|
||||
# fixed
|
||||
# encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore";
|
||||
|
||||
permissions = [
|
||||
{
|
||||
actor = "matrix.tlater.net";
|
||||
services = [
|
||||
{
|
||||
service = "*";
|
||||
level = "notifications";
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
actor = "@tlater:matrix.tlater.net";
|
||||
services = [
|
||||
{
|
||||
service = "*";
|
||||
level = "admin";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
listeners = [
|
||||
{
|
||||
port = 9000;
|
||||
resources = [ "webhooks" ];
|
||||
}
|
||||
{
|
||||
port = 9001;
|
||||
resources = [ "metrics" ];
|
||||
}
|
||||
];
|
||||
|
||||
metrics.enabled = true;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
security.crowdsec = {
|
||||
enable = true;
|
||||
|
||||
parserWhitelist = [ "10.45.249.2" ];
|
||||
|
||||
extraGroups = [
|
||||
"systemd-journal"
|
||||
"nginx"
|
||||
];
|
||||
|
||||
acquisitions = [
|
||||
{
|
||||
source = "journalctl";
|
||||
labels.type = "syslog";
|
||||
journalctl_filter = [ "SYSLOG_IDENTIFIER=Nextcloud" ];
|
||||
}
|
||||
|
||||
{
|
||||
source = "journalctl";
|
||||
labels.type = "syslog";
|
||||
journalctl_filter = [ "SYSLOG_IDENTIFIER=sshd-session" ];
|
||||
}
|
||||
|
||||
{
|
||||
labels.type = "nginx";
|
||||
filenames =
|
||||
[ "/var/log/nginx/*.log" ]
|
||||
++ lib.mapAttrsToList (
|
||||
vHost: _: "/var/log/nginx/${vHost}/access.log"
|
||||
) config.services.nginx.virtualHosts;
|
||||
}
|
||||
];
|
||||
|
||||
remediationComponents.firewallBouncer = {
|
||||
enable = true;
|
||||
settings.prometheus = {
|
||||
enabled = true;
|
||||
listen_addr = "127.0.0.1";
|
||||
listen_port = "60601";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Add whitelists for matrix
|
||||
systemd.tmpfiles.settings."10-matrix" =
|
||||
let
|
||||
stateDir = config.security.crowdsec.stateDirectory;
|
||||
in
|
||||
{
|
||||
"${stateDir}/config/postoverflows".d = {
|
||||
user = "crowdsec";
|
||||
group = "crowdsec";
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
"${stateDir}/config/postoverflows/s01-whitelist".d = {
|
||||
user = "crowdsec";
|
||||
group = "crowdsec";
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
"${stateDir}/config/postoverflows/s01-whitelist/matrix-whitelist.yaml"."L+".argument =
|
||||
((pkgs.formats.yaml { }).generate "crowdsec-matrix-whitelist.yaml" {
|
||||
name = "tetsumaki/matrix";
|
||||
description = "custom matrix whitelist";
|
||||
whitelist = {
|
||||
reason = "whitelist false positive for matrix";
|
||||
expression = [
|
||||
"evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'"
|
||||
"evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']"
|
||||
];
|
||||
};
|
||||
}).outPath;
|
||||
};
|
||||
}
|
42
configuration/services/fail2ban.nix
Normal file
42
configuration/services/fail2ban.nix
Normal file
|
@ -0,0 +1,42 @@
|
|||
{pkgs, ...}: {
|
||||
services.fail2ban = {
|
||||
enable = true;
|
||||
extraPackages = [pkgs.ipset];
|
||||
banaction = "iptables-ipset-proto6-allports";
|
||||
bantime-increment.enable = true;
|
||||
|
||||
jails = {
|
||||
nginx-botsearch = ''
|
||||
enabled = true
|
||||
logpath = /var/log/nginx/access.log
|
||||
'';
|
||||
};
|
||||
|
||||
ignoreIP = [
|
||||
"127.0.0.0/8"
|
||||
"10.0.0.0/8"
|
||||
"172.16.0.0/12"
|
||||
"192.168.0.0/16"
|
||||
];
|
||||
};
|
||||
|
||||
# Allow metrics services to connect to the socket as well
|
||||
users.groups.fail2ban = {};
|
||||
systemd.services.fail2ban.serviceConfig = {
|
||||
ExecStartPost =
|
||||
"+"
|
||||
+ (pkgs.writeShellScript "fail2ban-post-start" ''
|
||||
while ! [ -S /var/run/fail2ban/fail2ban.sock ]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
while ! ${pkgs.netcat}/bin/nc -zU /var/run/fail2ban/fail2ban.sock; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
${pkgs.coreutils}/bin/chown root:fail2ban /var/run/fail2ban /var/run/fail2ban/fail2ban.sock
|
||||
${pkgs.coreutils}/bin/chmod 660 /var/run/fail2ban/fail2ban.sock
|
||||
${pkgs.coreutils}/bin/chmod 710 /var/run/fail2ban
|
||||
'');
|
||||
};
|
||||
}
|
|
@ -2,48 +2,42 @@
|
|||
lib,
|
||||
config,
|
||||
flake-inputs,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
domain = "foundryvtt.${config.services.nginx.domain}";
|
||||
in
|
||||
{
|
||||
imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ];
|
||||
in {
|
||||
imports = [flake-inputs.foundryvtt.nixosModules.foundryvtt];
|
||||
|
||||
services = {
|
||||
foundryvtt = {
|
||||
enable = true;
|
||||
hostName = domain;
|
||||
minifyStaticFiles = true;
|
||||
proxySSL = true;
|
||||
proxyPort = 443;
|
||||
package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13;
|
||||
};
|
||||
|
||||
nginx.virtualHosts."${domain}" =
|
||||
let
|
||||
inherit (config.services.foundryvtt) port;
|
||||
in
|
||||
{
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
locations."/" = {
|
||||
proxyWebsockets = true;
|
||||
proxyPass = "http://localhost:${toString port}";
|
||||
};
|
||||
};
|
||||
|
||||
backups.foundryvtt = {
|
||||
user = "foundryvtt";
|
||||
paths = [ config.services.foundryvtt.dataDir ];
|
||||
pauseServices = [ "foundryvtt.service" ];
|
||||
};
|
||||
services.foundryvtt = {
|
||||
enable = true;
|
||||
hostName = domain;
|
||||
minifyStaticFiles = true;
|
||||
proxySSL = true;
|
||||
proxyPort = 443;
|
||||
};
|
||||
|
||||
# Want to start it manually when I need it, not have it constantly
|
||||
# running
|
||||
systemd.services.foundryvtt.wantedBy = lib.mkForce [ ];
|
||||
systemd.services.foundryvtt.wantedBy = lib.mkForce [];
|
||||
|
||||
services.nginx.virtualHosts."${domain}" = let
|
||||
inherit (config.services.foundryvtt) port;
|
||||
in {
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
locations."/" = {
|
||||
proxyWebsockets = true;
|
||||
proxyPass = "http://localhost:${toString port}";
|
||||
};
|
||||
};
|
||||
|
||||
services.backups.foundryvtt = {
|
||||
user = "foundryvtt";
|
||||
paths = [
|
||||
config.services.foundryvtt.dataDir
|
||||
];
|
||||
pauseServices = ["foundryvtt.service"];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,81 +3,93 @@
|
|||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
domain = "gitea.${config.services.nginx.domain}";
|
||||
in
|
||||
{
|
||||
services = {
|
||||
forgejo = {
|
||||
enable = true;
|
||||
database.type = "postgres";
|
||||
in {
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
database.type = "postgres";
|
||||
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = domain;
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
ROOT_URL = "https://${domain}/";
|
||||
SSH_PORT = 2222;
|
||||
};
|
||||
|
||||
metrics = {
|
||||
ENABLED = true;
|
||||
TOKEN = "#metricstoken#";
|
||||
};
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
session.COOKIE_SECURE = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Set up SSL
|
||||
nginx.virtualHosts."${domain}" =
|
||||
let
|
||||
httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
|
||||
httpPort = config.services.forgejo.settings.server.HTTP_PORT;
|
||||
in
|
||||
{
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
|
||||
locations."/metrics" = {
|
||||
extraConfig = ''
|
||||
access_log off;
|
||||
allow 127.0.0.1;
|
||||
${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
|
||||
deny all;
|
||||
'';
|
||||
};
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = domain;
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
ROOT_URL = "https://${domain}/";
|
||||
SSH_PORT = 2222;
|
||||
};
|
||||
|
||||
backups.forgejo = {
|
||||
user = "forgejo";
|
||||
paths = [
|
||||
"/var/lib/forgejo/forgejo-db.sql"
|
||||
"/var/lib/forgejo/repositories/"
|
||||
"/var/lib/forgejo/data/"
|
||||
"/var/lib/forgejo/custom/"
|
||||
# Conf is backed up via nix
|
||||
];
|
||||
preparation = {
|
||||
packages = [ config.services.postgresql.package ];
|
||||
text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
|
||||
metrics = {
|
||||
ENABLED = true;
|
||||
TOKEN = "#metricstoken#";
|
||||
};
|
||||
cleanup = {
|
||||
packages = [ pkgs.coreutils ];
|
||||
text = "rm /var/lib/forgejo/forgejo-db.sql";
|
||||
};
|
||||
pauseServices = [ "forgejo.service" ];
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
session.COOKIE_SECURE = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.forgejo.serviceConfig.ExecStartPre =
|
||||
let
|
||||
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
|
||||
secretPath = config.sops.secrets."forgejo/metrics-token".path;
|
||||
runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
|
||||
in
|
||||
[ "+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'" ];
|
||||
systemd.services.forgejo.serviceConfig.ExecStartPre = let
|
||||
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
|
||||
secretPath = config.sops.secrets."forgejo/metrics-token".path;
|
||||
runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
|
||||
in [
|
||||
"+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'"
|
||||
];
|
||||
|
||||
# Set up SSL
|
||||
services.nginx.virtualHosts."${domain}" = let
|
||||
httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
|
||||
httpPort = config.services.forgejo.settings.server.HTTP_PORT;
|
||||
in {
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
|
||||
locations."/metrics" = {
|
||||
extraConfig = ''
|
||||
access_log off;
|
||||
allow 127.0.0.1;
|
||||
${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
|
||||
deny all;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# Block repeated failed login attempts
|
||||
#
|
||||
# TODO(tlater): Update this - we switched to forgejo, who knows what
|
||||
# the new matches are.
|
||||
# environment.etc = {
|
||||
# "fail2ban/filter.d/gitea.conf".text = ''
|
||||
# [Definition]
|
||||
# failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>
|
||||
# journalmatch = _SYSTEMD_UNIT=forgejo.service + _COMM=forgejo + SYSLOG_IDENTIFIER=forgejo
|
||||
# '';
|
||||
# };
|
||||
|
||||
# services.fail2ban.jails = {
|
||||
# gitea = ''
|
||||
# enabled = true
|
||||
# '';
|
||||
# };
|
||||
|
||||
services.backups.forgejo = {
|
||||
user = "forgejo";
|
||||
paths = [
|
||||
"/var/lib/forgejo/forgejo-db.sql"
|
||||
"/var/lib/forgejo/repositories/"
|
||||
"/var/lib/forgejo/data/"
|
||||
"/var/lib/forgejo/custom/"
|
||||
# Conf is backed up via nix
|
||||
];
|
||||
preparation = {
|
||||
packages = [config.services.postgresql.package];
|
||||
text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
|
||||
};
|
||||
cleanup = {
|
||||
packages = [pkgs.coreutils];
|
||||
text = "rm /var/lib/forgejo/forgejo-db.sql";
|
||||
};
|
||||
pauseServices = ["forgejo.service"];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
hostName = "immich.${config.services.nginx.domain}";
|
||||
in
|
||||
{
|
||||
services = {
|
||||
immich = {
|
||||
enable = true;
|
||||
settings.server.externalDomain = "https://${hostName}";
|
||||
|
||||
environment.IMMICH_TELEMETRY_INCLUDE = "all";
|
||||
};
|
||||
|
||||
nginx.virtualHosts.${hostName} =
|
||||
let
|
||||
local = "http://${config.services.immich.host}:${toString config.services.immich.port}";
|
||||
in
|
||||
{
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = local;
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
locations."/metrics" = {
|
||||
extraConfig = ''
|
||||
access_log off;
|
||||
allow 127.0.0.1;
|
||||
${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
|
||||
deny all;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
backups.immich =
|
||||
let
|
||||
db-dump = "${config.services.immich.mediaLocation}/immich-db.sql";
|
||||
in
|
||||
{
|
||||
user = "immich";
|
||||
paths = [ config.services.immich.mediaLocation ];
|
||||
|
||||
preparation = {
|
||||
packages = [ config.services.postgresql.package ];
|
||||
text = ''
|
||||
pg_dump ${config.services.immich.database.name} --clean --if-exists --file=${db-dump}
|
||||
'';
|
||||
};
|
||||
|
||||
cleanup = {
|
||||
packages = [ pkgs.coreutils ];
|
||||
text = "rm ${db-dump}";
|
||||
};
|
||||
pauseServices = [
|
||||
"immich-server.service"
|
||||
"immich-machine-learning.service"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -5,6 +5,5 @@
|
|||
./exporters.nix
|
||||
./grafana.nix
|
||||
./victoriametrics.nix
|
||||
./victorialogs.nix
|
||||
];
|
||||
}
|
||||
|
|
|
@ -3,49 +3,23 @@
|
|||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
yaml = pkgs.formats.yaml { };
|
||||
in
|
||||
{
|
||||
}: let
|
||||
yaml = pkgs.formats.yaml {};
|
||||
in {
|
||||
services.prometheus = {
|
||||
exporters = {
|
||||
blackbox = {
|
||||
enable = true;
|
||||
listenAddress = "127.0.0.1";
|
||||
configFile = yaml.generate "blackbox.yaml" {
|
||||
modules = {
|
||||
http_2xx = {
|
||||
prober = "http";
|
||||
timeout = "5s";
|
||||
http.preferred_ip_protocol = "ip4";
|
||||
};
|
||||
|
||||
turn_server = {
|
||||
prober = "tcp";
|
||||
timeout = "5s";
|
||||
tcp = {
|
||||
preferred_ip_protocol = "ip4";
|
||||
source_ip_address = "116.202.158.55";
|
||||
tls = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Periodically check domain registration status
|
||||
domain = {
|
||||
enable = true;
|
||||
listenAddress = "127.0.0.1";
|
||||
extraFlags =
|
||||
let
|
||||
conf.domains = [
|
||||
"tlater.net"
|
||||
"tlater.com"
|
||||
];
|
||||
in
|
||||
[ "--config=${yaml.generate "domains.yml" conf}" ];
|
||||
extraFlags = let
|
||||
conf.domains = [
|
||||
"tlater.net"
|
||||
"tlater.com"
|
||||
];
|
||||
in [
|
||||
"--config=${yaml.generate "domains.yml" conf}"
|
||||
];
|
||||
};
|
||||
|
||||
# System statistics
|
||||
|
@ -74,29 +48,54 @@ in
|
|||
listenAddress = "127.0.0.1";
|
||||
group = "nginx";
|
||||
|
||||
settings.namespaces = lib.mapAttrsToList (name: _: {
|
||||
inherit name;
|
||||
metrics_override.prefix = "nginxlog";
|
||||
namespace_label = "vhost";
|
||||
settings.namespaces =
|
||||
lib.mapAttrsToList (name: virtualHost: {
|
||||
inherit name;
|
||||
metrics_override.prefix = "nginxlog";
|
||||
namespace_label = "vhost";
|
||||
|
||||
format = lib.concatStringsSep " " [
|
||||
"$remote_addr - $remote_user [$time_local]"
|
||||
''"$request" $status $body_bytes_sent''
|
||||
''"$http_referer" "$http_user_agent"''
|
||||
''rt=$request_time uct="$upstream_connect_time"''
|
||||
''uht="$upstream_header_time" urt="$upstream_response_time"''
|
||||
];
|
||||
format = lib.concatStringsSep " " [
|
||||
"$remote_addr - $remote_user [$time_local]"
|
||||
''"$request" $status $body_bytes_sent''
|
||||
''"$http_referer" "$http_user_agent"''
|
||||
''rt=$request_time uct="$upstream_connect_time"''
|
||||
''uht="$upstream_header_time" urt="$upstream_response_time"''
|
||||
];
|
||||
|
||||
source.files = [ "/var/log/nginx/${name}/access.log" ];
|
||||
}) config.services.nginx.virtualHosts;
|
||||
source.files = [
|
||||
"/var/log/nginx/${name}/access.log"
|
||||
];
|
||||
})
|
||||
config.services.nginx.virtualHosts;
|
||||
};
|
||||
};
|
||||
|
||||
extraExporters = {
|
||||
fail2ban = let
|
||||
cfg = config.services.prometheus.extraExporters.fail2ban;
|
||||
in {
|
||||
port = 9191;
|
||||
serviceOpts = {
|
||||
after = ["fail2ban.service"];
|
||||
requires = ["fail2ban.service"];
|
||||
serviceConfig = {
|
||||
Group = "fail2ban";
|
||||
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
|
||||
ExecStart = lib.concatStringsSep " " [
|
||||
"${pkgs.local.prometheus-fail2ban-exporter}/bin/fail2ban-prometheus-exporter"
|
||||
"--collector.f2b.socket=/var/run/fail2ban/fail2ban.sock"
|
||||
"--web.listen-address='${cfg.listenAddress}:${toString cfg.port}'"
|
||||
"--collector.f2b.exit-on-socket-connection-error=true"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# TODO(tlater):
|
||||
# - wireguard (?)
|
||||
# - postgres (?)
|
||||
# - blackbox (?) (curl to see if http and similar is up)
|
||||
# - ssl_exporter (?)
|
||||
};
|
||||
|
||||
services.dbus.implementation = "broker";
|
||||
}
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
{ pkgs, config, ... }:
|
||||
let
|
||||
{config, ...}: let
|
||||
domain = "metrics.${config.services.nginx.domain}";
|
||||
in
|
||||
{
|
||||
in {
|
||||
services.grafana = {
|
||||
enable = true;
|
||||
settings = {
|
||||
server = {
|
||||
http_port = 3001; # Default overlaps with gitea
|
||||
root_url = "https://metrics.tlater.net";
|
||||
};
|
||||
server.http_port = 3001; # Default overlaps with gitea
|
||||
|
||||
security = {
|
||||
admin_user = "tlater";
|
||||
|
@ -28,11 +23,6 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
declarativePlugins = [
|
||||
pkgs.grafanaPlugins.victoriametrics-metrics-datasource
|
||||
pkgs.grafanaPlugins.victoriametrics-logs-datasource
|
||||
];
|
||||
|
||||
provision = {
|
||||
enable = true;
|
||||
|
||||
|
@ -40,16 +30,7 @@ in
|
|||
{
|
||||
name = "Victoriametrics - tlater.net";
|
||||
url = "http://localhost:8428";
|
||||
type = "victoriametrics-metrics-datasource";
|
||||
access = "proxy";
|
||||
isDefault = true;
|
||||
}
|
||||
|
||||
{
|
||||
name = "Victorialogs - tlater.net";
|
||||
url = "http://${config.services.victorialogs.bindAddress}";
|
||||
type = "victoriametrics-logs-datasource";
|
||||
access = "proxy";
|
||||
type = "prometheus";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
@ -59,12 +40,7 @@ in
|
|||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
locations = {
|
||||
"/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
|
||||
"/api/live" = {
|
||||
proxyWebsockets = true;
|
||||
proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
|
||||
};
|
||||
};
|
||||
enableAuthorization = true;
|
||||
locations."/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,235 +3,202 @@
|
|||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
inherit (lib) types mkOption mkDefault;
|
||||
yaml = pkgs.formats.yaml { };
|
||||
in
|
||||
{
|
||||
yaml = pkgs.formats.yaml {};
|
||||
in {
|
||||
options = {
|
||||
services.prometheus = {
|
||||
extraExporters = mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
description = "The port on which this exporter listens.";
|
||||
};
|
||||
listenAddress = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Address to listen on.";
|
||||
};
|
||||
serviceOpts = mkOption {
|
||||
type = types.attrs;
|
||||
description = "An attrset to be merged with the exporter's systemd service.";
|
||||
};
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
description = "The port on which this exporter listens.";
|
||||
};
|
||||
}
|
||||
);
|
||||
listenAddress = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Address to listen on.";
|
||||
};
|
||||
serviceOpts = mkOption {
|
||||
type = types.attrs;
|
||||
description = "An attrset to be merged with the exporter's systemd service.";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
services.victoriametrics.scrapeConfigs = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
job_name = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
type = types.attrsOf (types.submodule ({
|
||||
name,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
options = {
|
||||
job_name = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
|
||||
extraSettings = mkOption {
|
||||
type = types.anything;
|
||||
description = ''
|
||||
Other settings to set for this scrape config.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
|
||||
targets = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = lib.mdDoc ''
|
||||
Addresses scrape targets for this config listen on.
|
||||
|
||||
Shortcut for `static_configs = lib.singleton {targets = [<targets>];}`
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
static_configs = mkOption {
|
||||
default = [];
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
targets = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = lib.mdDoc ''
|
||||
The addresses scrape targets for this config listen on.
|
||||
|
||||
Must in `listenAddress:port` format.
|
||||
'';
|
||||
};
|
||||
labels = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = lib.mdDoc ''
|
||||
Labels to apply to all targets defined for this static config.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
extraSettings = mkOption {
|
||||
inherit (pkgs.formats.yaml { }) type;
|
||||
description = ''
|
||||
Other settings to set for this scrape config.
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
|
||||
targets = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = lib.mdDoc ''
|
||||
Addresses scrape targets for this config listen on.
|
||||
|
||||
Shortcut for `static_configs = lib.singleton {targets = [<targets>];}`
|
||||
'';
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
static_configs = mkOption {
|
||||
default = [ ];
|
||||
type = types.listOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
targets = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = lib.mdDoc ''
|
||||
The addresses scrape targets for this config listen on.
|
||||
|
||||
Must in `listenAddress:port` format.
|
||||
'';
|
||||
};
|
||||
labels = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = lib.mdDoc ''
|
||||
Labels to apply to all targets defined for this static config.
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
systemd.services = lib.mkMerge [
|
||||
(lib.mapAttrs' (
|
||||
name: exporter:
|
||||
lib.nameValuePair "prometheus-${name}-exporter" (
|
||||
lib.mkMerge [
|
||||
{
|
||||
# Shamelessly copied from upstream because the upstream
|
||||
# module is an intractable mess
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Restart = mkDefault "always";
|
||||
PrivateTmp = mkDefault true;
|
||||
WorkingDirectory = mkDefault /tmp;
|
||||
DynamicUser = mkDefault true;
|
||||
# Hardening
|
||||
CapabilityBoundingSet = mkDefault [ "" ];
|
||||
DeviceAllow = [ "" ];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = mkDefault true;
|
||||
ProtectClock = mkDefault true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectSystem = mkDefault "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
UMask = "0077";
|
||||
};
|
||||
}
|
||||
exporter.serviceOpts
|
||||
]
|
||||
)
|
||||
) config.services.prometheus.extraExporters)
|
||||
(lib.mapAttrs' (name: exporter:
|
||||
lib.nameValuePair "prometheus-${name}-exporter" (lib.mkMerge [
|
||||
{
|
||||
# Shamelessly copied from upstream because the upstream
|
||||
# module is an intractable mess
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network.target"];
|
||||
serviceConfig.Restart = mkDefault "always";
|
||||
serviceConfig.PrivateTmp = mkDefault true;
|
||||
serviceConfig.WorkingDirectory = mkDefault /tmp;
|
||||
serviceConfig.DynamicUser = mkDefault true;
|
||||
# Hardening
|
||||
serviceConfig.CapabilityBoundingSet = mkDefault [""];
|
||||
serviceConfig.DeviceAllow = [""];
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.MemoryDenyWriteExecute = true;
|
||||
serviceConfig.NoNewPrivileges = true;
|
||||
serviceConfig.PrivateDevices = mkDefault true;
|
||||
serviceConfig.ProtectClock = mkDefault true;
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectHostname = true;
|
||||
serviceConfig.ProtectKernelLogs = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectKernelTunables = true;
|
||||
serviceConfig.ProtectSystem = mkDefault "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = ["AF_INET" "AF_INET6"];
|
||||
serviceConfig.RestrictNamespaces = true;
|
||||
serviceConfig.RestrictRealtime = true;
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.UMask = "0077";
|
||||
}
|
||||
exporter.serviceOpts
|
||||
]))
|
||||
config.services.prometheus.extraExporters)
|
||||
|
||||
{
|
||||
vmagent-scrape-exporters =
|
||||
let
|
||||
inherit (config.services.victoriametrics) listenAddress;
|
||||
vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
|
||||
promscrape = yaml.generate "prometheus.yml" {
|
||||
scrape_configs = lib.mapAttrsToList (
|
||||
_: scrape:
|
||||
lib.recursiveUpdate {
|
||||
inherit (scrape) job_name;
|
||||
static_configs =
|
||||
scrape.static_configs
|
||||
++ lib.optional (scrape.targets != [ ]) { inherit (scrape) targets; };
|
||||
} scrape.extraSettings
|
||||
) config.services.victoriametrics.scrapeConfigs;
|
||||
};
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
path = [ pkgs.victoriametrics ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"network.target"
|
||||
"victoriametrics.service"
|
||||
];
|
||||
serviceConfig = {
|
||||
ExecStart = [
|
||||
(lib.concatStringsSep " " [
|
||||
"${pkgs.victoriametrics}/bin/vmagent"
|
||||
"-promscrape.config=${promscrape}"
|
||||
"-remoteWrite.url=http://${vmAddr}/api/v1/write"
|
||||
"-remoteWrite.tmpDataPath=%t/vmagent"
|
||||
])
|
||||
];
|
||||
SupplementaryGroups = "metrics";
|
||||
|
||||
DynamicUser = true;
|
||||
RuntimeDirectory = "vmagent";
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
DeviceAllow = [ "" ];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
UMask = "0077";
|
||||
};
|
||||
vmagent-scrape-exporters = let
|
||||
listenAddress = config.services.victoriametrics.listenAddress;
|
||||
vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
|
||||
promscrape = yaml.generate "prometheus.yml" {
|
||||
scrape_configs = lib.mapAttrsToList (_: scrape:
|
||||
lib.recursiveUpdate {
|
||||
inherit (scrape) job_name;
|
||||
static_configs =
|
||||
scrape.static_configs
|
||||
++ lib.optional (scrape.targets != []) {targets = scrape.targets;};
|
||||
}
|
||||
scrape.extraSettings)
|
||||
config.services.victoriametrics.scrapeConfigs;
|
||||
};
|
||||
in {
|
||||
enable = true;
|
||||
path = [pkgs.victoriametrics];
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network.target" "victoriametrics.service"];
|
||||
serviceConfig = {
|
||||
ExecStart = [
|
||||
(lib.concatStringsSep " " [
|
||||
"${pkgs.victoriametrics}/bin/vmagent"
|
||||
"-promscrape.config=${promscrape}"
|
||||
"-remoteWrite.url=http://${vmAddr}/api/v1/write"
|
||||
"-remoteWrite.tmpDataPath=%t/vmagent"
|
||||
])
|
||||
];
|
||||
SupplementaryGroups = "metrics";
|
||||
|
||||
DynamicUser = true;
|
||||
RuntimeDirectory = "vmagent";
|
||||
CapabilityBoundingSet = [""];
|
||||
DeviceAllow = [""];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = ["AF_INET" "AF_INET6"];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
users.groups.metrics = { };
|
||||
users.groups.metrics = {};
|
||||
|
||||
services.victoriametrics.scrapeConfigs =
|
||||
let
|
||||
allExporters = lib.mapAttrs (_: exporter: { inherit (exporter) listenAddress port; }) (
|
||||
(lib.filterAttrs (
|
||||
name: exporter:
|
||||
# A bunch of deprecated exporters that need to be ignored
|
||||
!(builtins.elem name [
|
||||
"blackbox"
|
||||
"minio"
|
||||
"tor"
|
||||
"unifi-poller"
|
||||
])
|
||||
&& builtins.isAttrs exporter
|
||||
&& exporter.enable
|
||||
) config.services.prometheus.exporters)
|
||||
// config.services.prometheus.extraExporters
|
||||
);
|
||||
in
|
||||
services.victoriametrics.scrapeConfigs = let
|
||||
allExporters =
|
||||
lib.mapAttrs (name: exporter: {
|
||||
inherit (exporter) listenAddress port;
|
||||
}) ((lib.filterAttrs (_: exporter: builtins.isAttrs exporter && exporter.enable)
|
||||
config.services.prometheus.exporters)
|
||||
// config.services.prometheus.extraExporters);
|
||||
in
|
||||
lib.mapAttrs (_: exporter: {
|
||||
targets = [ "${exporter.listenAddress}:${toString exporter.port}" ];
|
||||
}) allExporters;
|
||||
targets = ["${exporter.listenAddress}:${toString exporter.port}"];
|
||||
})
|
||||
allExporters;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.services.victorialogs;
|
||||
in
|
||||
{
|
||||
options.services.victorialogs.bindAddress = lib.mkOption {
|
||||
readOnly = true;
|
||||
type = lib.types.str;
|
||||
description = ''
|
||||
Final address on which victorialogs listens.
|
||||
'';
|
||||
};
|
||||
|
||||
config = {
|
||||
services.victorialogs = {
|
||||
enable = true;
|
||||
bindAddress =
|
||||
(lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress;
|
||||
};
|
||||
|
||||
services.journald.upload = {
|
||||
enable = true;
|
||||
settings.Upload = {
|
||||
URL = "http://${cfg.bindAddress}/insert/journald";
|
||||
NetworkTimeoutSec = "20s";
|
||||
};
|
||||
};
|
||||
systemd.services."systemd-journal-upload".after = [ "victorialogs.service" ];
|
||||
};
|
||||
}
|
|
@ -1,99 +1,16 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
blackbox_host = config.services.prometheus.exporters.blackbox.listenAddress;
|
||||
blackbox_port = config.services.prometheus.exporters.blackbox.port;
|
||||
in
|
||||
{
|
||||
{config, ...}: {
|
||||
config.services.victoriametrics = {
|
||||
enable = true;
|
||||
extraOptions = [ "-storage.minFreeDiskSpaceBytes=5GB" ];
|
||||
extraOptions = [
|
||||
"-storage.minFreeDiskSpaceBytes=5GB"
|
||||
];
|
||||
|
||||
scrapeConfigs = {
|
||||
forgejo = {
|
||||
targets = [ "127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}" ];
|
||||
targets = ["127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}"];
|
||||
extraSettings.authorization.credentials_file = config.sops.secrets."forgejo/metrics-token".path;
|
||||
};
|
||||
|
||||
blackbox = {
|
||||
static_configs = lib.singleton {
|
||||
targets = lib.mapAttrsToList (vHost: _: "https://${vHost}") config.services.nginx.virtualHosts;
|
||||
};
|
||||
|
||||
extraSettings = {
|
||||
metrics_path = "/probe";
|
||||
params.module = [ "http_2xx" ];
|
||||
|
||||
relabel_configs = [
|
||||
{
|
||||
source_labels = [ "__address__" ];
|
||||
target_label = "__param_target";
|
||||
}
|
||||
{
|
||||
source_labels = [ "__param_target" ];
|
||||
target_label = "instance";
|
||||
}
|
||||
{
|
||||
target_label = "__address__";
|
||||
replacement = "${blackbox_host}:${toString blackbox_port}";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
blackbox_turn = {
|
||||
targets = [ "turn.tlater.net:${toString config.services.coturn.tls-listening-port}" ];
|
||||
|
||||
extraSettings = {
|
||||
metrics_path = "/probe";
|
||||
params.module = [ "turn_server" ];
|
||||
|
||||
relabel_configs = [
|
||||
{
|
||||
source_labels = [ "__address__" ];
|
||||
target_label = "__param_target";
|
||||
}
|
||||
{
|
||||
source_labels = [ "__param_target" ];
|
||||
target_label = "instance";
|
||||
}
|
||||
{
|
||||
target_label = "__address__";
|
||||
replacement = "${blackbox_host}:${toString blackbox_port}";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
blackbox_exporter.targets = [ "${blackbox_host}:${toString blackbox_port}" ];
|
||||
|
||||
coturn.targets = [ "127.0.0.1:9641" ];
|
||||
|
||||
crowdsec.targets =
|
||||
let
|
||||
address = config.security.crowdsec.settings.prometheus.listen_addr;
|
||||
port = config.security.crowdsec.settings.prometheus.listen_port;
|
||||
in
|
||||
[ "${address}:${toString port}" ];
|
||||
|
||||
csFirewallBouncer.targets =
|
||||
let
|
||||
address =
|
||||
config.security.crowdsec.remediationComponents.firewallBouncer.settings.prometheus.listen_addr;
|
||||
port =
|
||||
config.security.crowdsec.remediationComponents.firewallBouncer.settings.prometheus.listen_port;
|
||||
in
|
||||
[ "${address}:${toString port}" ];
|
||||
|
||||
immich.targets = [
|
||||
"127.0.0.1:8081"
|
||||
"127.0.0.1:8082"
|
||||
];
|
||||
|
||||
# Configured in the hookshot listeners, but it's hard to filter
|
||||
# the correct values out of that config.
|
||||
matrixHookshot.targets = [ "127.0.0.1:9001" ];
|
||||
|
||||
victorialogs.targets = [ config.services.victorialogs.bindAddress ];
|
||||
coturn.targets = ["127.0.0.1:9641"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,103 +1,105 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
nextcloud = pkgs.nextcloud31;
|
||||
}: let
|
||||
# Update pending on rewrite of nextcloud news, though there is an
|
||||
# alpha to switch to if it becomes necessary:
|
||||
# https://github.com/nextcloud/news/issues/2610
|
||||
nextcloud = pkgs.nextcloud27;
|
||||
hostName = "nextcloud.${config.services.nginx.domain}";
|
||||
in
|
||||
{
|
||||
services = {
|
||||
nextcloud = {
|
||||
inherit hostName;
|
||||
in {
|
||||
services.nextcloud = {
|
||||
inherit hostName;
|
||||
|
||||
package = nextcloud;
|
||||
phpPackage = lib.mkForce (
|
||||
pkgs.php.override {
|
||||
packageOverrides = _: prev: {
|
||||
extensions = prev.extensions // {
|
||||
pgsql = prev.extensions.pgsql.overrideAttrs (_: {
|
||||
configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
|
||||
});
|
||||
pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (_: {
|
||||
configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
enable = true;
|
||||
maxUploadSize = "2G";
|
||||
https = true;
|
||||
package = nextcloud;
|
||||
enable = true;
|
||||
maxUploadSize = "2G";
|
||||
https = true;
|
||||
|
||||
configureRedis = true;
|
||||
configureRedis = true;
|
||||
|
||||
config = {
|
||||
dbtype = "pgsql";
|
||||
dbhost = "/run/postgresql";
|
||||
config = {
|
||||
dbtype = "pgsql";
|
||||
dbhost = "/run/postgresql";
|
||||
|
||||
adminuser = "tlater";
|
||||
adminpassFile = config.sops.secrets."nextcloud/tlater".path;
|
||||
};
|
||||
|
||||
settings = {
|
||||
default_phone_region = "AT";
|
||||
overwriteprotocol = "https";
|
||||
};
|
||||
|
||||
phpOptions = {
|
||||
"opcache.interned_strings_buffer" = "16";
|
||||
};
|
||||
|
||||
extraApps = {
|
||||
inherit (config.services.nextcloud.package.packages.apps)
|
||||
calendar
|
||||
contacts
|
||||
cookbook
|
||||
news
|
||||
;
|
||||
};
|
||||
adminuser = "tlater";
|
||||
adminpassFile = config.sops.secrets."nextcloud/tlater".path;
|
||||
};
|
||||
|
||||
# Set up SSL
|
||||
nginx.virtualHosts."${hostName}" = {
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
# The upstream module already adds HSTS
|
||||
settings = {
|
||||
default_phone_region = "AT";
|
||||
overwriteprotocol = "https";
|
||||
};
|
||||
|
||||
backups.nextcloud = {
|
||||
user = "nextcloud";
|
||||
paths = [
|
||||
"/var/lib/nextcloud/nextcloud-db.sql"
|
||||
"/var/lib/nextcloud/data/"
|
||||
"/var/lib/nextcloud/config/config.php"
|
||||
];
|
||||
preparation = {
|
||||
packages = [
|
||||
config.services.postgresql.package
|
||||
config.services.nextcloud.occ
|
||||
];
|
||||
text = ''
|
||||
nextcloud-occ maintenance:mode --on
|
||||
pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
|
||||
'';
|
||||
};
|
||||
cleanup = {
|
||||
packages = [
|
||||
pkgs.coreutils
|
||||
config.services.nextcloud.occ
|
||||
];
|
||||
text = ''
|
||||
rm /var/lib/nextcloud/nextcloud-db.sql
|
||||
nextcloud-occ maintenance:mode --off
|
||||
'';
|
||||
};
|
||||
phpOptions = {
|
||||
"opcache.interned_strings_buffer" = "16";
|
||||
};
|
||||
|
||||
extraApps = {
|
||||
inherit (pkgs.local) bookmarks calendar contacts cookbook news notes;
|
||||
};
|
||||
};
|
||||
|
||||
# Ensure that this service doesn't start before postgres is ready
|
||||
systemd.services.nextcloud-setup.after = [ "postgresql.service" ];
|
||||
systemd.services.nextcloud-setup.after = ["postgresql.service"];
|
||||
|
||||
# Set up SSL
|
||||
services.nginx.virtualHosts."${hostName}" = {
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
# The upstream module already adds HSTS
|
||||
};
|
||||
|
||||
# Block repeated failed login attempts
|
||||
environment.etc = {
|
||||
"fail2ban/filter.d/nextcloud.conf".text = ''
|
||||
[Definition]
|
||||
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
|
||||
failregex = \{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
|
||||
\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
|
||||
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"
|
||||
journalmatch = SYSLOG_IDENTIFIER=Nextcloud
|
||||
'';
|
||||
};
|
||||
|
||||
services.fail2ban.jails = {
|
||||
nextcloud = ''
|
||||
enabled = true
|
||||
|
||||
# Nextcloud does some throttling already, so we need to set
|
||||
# these to something bigger.
|
||||
findtime = 43200
|
||||
bantime = 86400
|
||||
'';
|
||||
};
|
||||
|
||||
services.backups.nextcloud = {
|
||||
user = "nextcloud";
|
||||
paths = [
|
||||
"/var/lib/nextcloud/nextcloud-db.sql"
|
||||
"/var/lib/nextcloud/data/"
|
||||
"/var/lib/nextcloud/config/config.php"
|
||||
];
|
||||
preparation = {
|
||||
packages = [
|
||||
config.services.postgresql.package
|
||||
config.services.nextcloud.occ
|
||||
];
|
||||
text = ''
|
||||
nextcloud-occ maintenance:mode --on
|
||||
pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
|
||||
'';
|
||||
};
|
||||
cleanup = {
|
||||
packages = [
|
||||
pkgs.coreutils
|
||||
config.services.nextcloud.occ
|
||||
];
|
||||
text = ''
|
||||
rm /var/lib/nextcloud/nextcloud-db.sql
|
||||
nextcloud-occ maintenance:mode --off
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
services.postgresql = {
|
||||
package = pkgs.postgresql_14;
|
||||
enable = true;
|
||||
|
@ -25,11 +28,16 @@
|
|||
name = "nextcloud";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
{
|
||||
name = config.services.authelia.instances.main.user;
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
|
||||
ensureDatabases = [
|
||||
"grafana"
|
||||
"nextcloud"
|
||||
config.services.authelia.instances.main.user
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
{ pkgs, lib, ... }:
|
||||
let
|
||||
inherit (lib) concatStringsSep;
|
||||
in
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) concatStringsSep;
|
||||
in {
|
||||
# Sadly, steam-run requires some X libs
|
||||
environment.noXlibs = false;
|
||||
|
||||
systemd.services.starbound = {
|
||||
description = "Starbound";
|
||||
after = [ "network.target" ];
|
||||
after = ["network.target"];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.local.starbound}/bin/launch-starbound ${./configs/starbound.json}";
|
||||
|
@ -65,7 +67,7 @@ in
|
|||
# Game servers shouldn't use cgroups themselves either
|
||||
ProtectControlGroups = true;
|
||||
# Most game servers will never need other socket types
|
||||
RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ];
|
||||
RestrictAddressFamilies = ["AF_UNIX AF_INET AF_INET6"];
|
||||
# Also a no-brainer, no game server should ever need this
|
||||
LockPersonality = true;
|
||||
# Some game servers will probably try to set this, but they
|
||||
|
@ -111,7 +113,9 @@ in
|
|||
|
||||
services.backups.starbound = {
|
||||
user = "root";
|
||||
paths = [ "/var/lib/private/starbound/storage/universe/" ];
|
||||
pauseServices = [ "starbound.service" ];
|
||||
paths = [
|
||||
"/var/lib/private/starbound/storage/universe/"
|
||||
];
|
||||
pauseServices = ["starbound.service"];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
{ config, ... }:
|
||||
let
|
||||
inherit (config.services.nginx) domain;
|
||||
in
|
||||
{
|
||||
{config, ...}: let
|
||||
domain = config.services.nginx.domain;
|
||||
in {
|
||||
services.tlaternet-webserver = {
|
||||
enable = true;
|
||||
listen = {
|
||||
|
@ -12,17 +10,15 @@ in
|
|||
};
|
||||
|
||||
# Set up SSL
|
||||
services.nginx.virtualHosts."${domain}" =
|
||||
let
|
||||
inherit (config.services.tlaternet-webserver.listen) addr port;
|
||||
in
|
||||
{
|
||||
serverAliases = [ "www.${domain}" ];
|
||||
services.nginx.virtualHosts."${domain}" = let
|
||||
inherit (config.services.tlaternet-webserver.listen) addr port;
|
||||
in {
|
||||
serverAliases = ["www.${domain}"];
|
||||
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
forceSSL = true;
|
||||
useACMEHost = "tlater.net";
|
||||
enableHSTS = true;
|
||||
|
||||
locations."/".proxyPass = "http://${addr}:${toString port}";
|
||||
};
|
||||
locations."/".proxyPass = "http://${addr}:${toString port}";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{ config, ... }:
|
||||
{
|
||||
{config, ...}: {
|
||||
# iptables needs to permit forwarding from wg0 to wg0
|
||||
networking.firewall.extraCommands = ''
|
||||
iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
|
||||
|
@ -24,10 +23,20 @@
|
|||
};
|
||||
|
||||
wireguardPeers = [
|
||||
# yui
|
||||
{
|
||||
AllowedIPs = [ "10.45.249.2/32" ];
|
||||
PublicKey = "5mlnqEVJWks5OqgeFA2bLIrvST9TlCE81Btl+j4myz0=";
|
||||
# yui
|
||||
wireguardPeerConfig = {
|
||||
AllowedIPs = ["10.45.249.2/32"];
|
||||
PublicKey = "5mlnqEVJWks5OqgeFA2bLIrvST9TlCE81Btl+j4myz0=";
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
# yuanyuan
|
||||
wireguardPeerConfig = {
|
||||
AllowedIPs = ["10.45.249.10/32"];
|
||||
PublicKey = "0UsFE2atz/O5P3OKQ8UHyyyGQNJbp1MeIWUJLuoerwE=";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
@ -38,23 +47,23 @@
|
|||
matchConfig.Name = "wg0";
|
||||
|
||||
networkConfig = {
|
||||
Description = "VLAN";
|
||||
|
||||
Address = [
|
||||
"10.45.249.1/32"
|
||||
# TODO(tlater): Add IPv6 whenever that becomes relevant
|
||||
];
|
||||
|
||||
IPv4Forwarding = "yes";
|
||||
IPForward = "yes";
|
||||
IPv4ProxyARP = "yes";
|
||||
};
|
||||
|
||||
routes = [
|
||||
{
|
||||
Source = "10.45.249.0/24";
|
||||
Destination = "10.45.249.0/24";
|
||||
Gateway = "10.45.249.1";
|
||||
GatewayOnLink = "no";
|
||||
routeConfig = {
|
||||
Source = "10.45.249.0/24";
|
||||
Destination = "10.45.249.0/24";
|
||||
Gateway = "10.45.249.1";
|
||||
GatewayOnLink = "no";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -3,9 +3,15 @@
|
|||
defaultSopsFile = ../keys/production.yaml;
|
||||
|
||||
secrets = {
|
||||
"battery-manager/email" = { };
|
||||
"battery-manager/email" = {
|
||||
owner = "battery-manager";
|
||||
group = "battery-manager";
|
||||
};
|
||||
|
||||
"battery-manager/password" = { };
|
||||
"battery-manager/password" = {
|
||||
owner = "battery-manager";
|
||||
group = "battery-manager";
|
||||
};
|
||||
|
||||
# Gitea
|
||||
"forgejo/metrics-token" = {
|
||||
|
@ -25,12 +31,12 @@
|
|||
};
|
||||
|
||||
# Heisenbridge
|
||||
"heisenbridge/as-token" = { };
|
||||
"heisenbridge/hs-token" = { };
|
||||
"heisenbridge/as-token" = {};
|
||||
"heisenbridge/hs-token" = {};
|
||||
|
||||
# Matrix-hookshot
|
||||
"matrix-hookshot/as-token" = { };
|
||||
"matrix-hookshot/hs-token" = { };
|
||||
"hetzner-api" = {
|
||||
owner = "acme";
|
||||
};
|
||||
|
||||
# Nextcloud
|
||||
"nextcloud/tlater" = {
|
||||
|
@ -38,14 +44,6 @@
|
|||
group = "nextcloud";
|
||||
};
|
||||
|
||||
# Porkbub/ACME
|
||||
"porkbun/api-key" = {
|
||||
owner = "acme";
|
||||
};
|
||||
"porkbun/secret-api-key" = {
|
||||
owner = "acme";
|
||||
};
|
||||
|
||||
# Restic
|
||||
"restic/local-backups" = {
|
||||
owner = "root";
|
||||
|
@ -64,10 +62,10 @@
|
|||
};
|
||||
|
||||
# Steam
|
||||
"steam/tlater" = { };
|
||||
"steam/tlater" = {};
|
||||
|
||||
# Turn
|
||||
"turn/env" = { };
|
||||
"turn/env" = {};
|
||||
"turn/secret" = {
|
||||
owner = "turnserver";
|
||||
};
|
||||
|
|
960
flake.lock
generated
960
flake.lock
generated
File diff suppressed because it is too large
Load diff
252
flake.nix
252
flake.nix
|
@ -2,7 +2,8 @@
|
|||
description = "tlater.net host configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
|
||||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
disko = {
|
||||
url = "github:nix-community/disko";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
@ -12,6 +13,10 @@
|
|||
url = "github:Mic92/sops-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
nvfetcher = {
|
||||
url = "github:berberman/nvfetcher";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
tlaternet-webserver = {
|
||||
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
@ -22,146 +27,131 @@
|
|||
};
|
||||
|
||||
sonnenshift = {
|
||||
url = "git+ssh://git@github.com/sonnenshift/battery-manager";
|
||||
url = "git+ssh://git@github.com/sonnenshift/battery-manager?ref=tlater/implement-nix-module";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
sops-nix,
|
||||
deploy-rs,
|
||||
...
|
||||
}@inputs:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
vm = nixpkgs.lib.nixosSystem {
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
sops-nix,
|
||||
nvfetcher,
|
||||
deploy-rs,
|
||||
...
|
||||
} @ inputs: let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
##################
|
||||
# Configurations #
|
||||
##################
|
||||
nixosConfigurations = {
|
||||
# The actual system definition
|
||||
hetzner-1 = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs.flake-inputs = inputs;
|
||||
|
||||
modules = [
|
||||
./configuration
|
||||
./configuration/hardware-specific/vm.nix
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
##################
|
||||
# Configurations #
|
||||
##################
|
||||
nixosConfigurations = {
|
||||
# The actual system definition
|
||||
hetzner-1 = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs.flake-inputs = inputs;
|
||||
|
||||
modules = [
|
||||
./configuration
|
||||
./configuration/hardware-specific/hetzner
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
############################
|
||||
# Deployment configuration #
|
||||
############################
|
||||
deploy.nodes = {
|
||||
hetzner-1 = {
|
||||
hostname = "116.202.158.55";
|
||||
|
||||
profiles.system = {
|
||||
user = "root";
|
||||
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1;
|
||||
};
|
||||
|
||||
sshUser = "tlater";
|
||||
sshOpts = [
|
||||
"-p"
|
||||
"2222"
|
||||
"-o"
|
||||
"ForwardAgent=yes"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
#########
|
||||
# Tests #
|
||||
#########
|
||||
checks.${system} = import ./checks (inputs // { inherit system; });
|
||||
|
||||
###########################
|
||||
# Garbage collection root #
|
||||
###########################
|
||||
|
||||
packages.${system} =
|
||||
let
|
||||
localPkgs = import ./pkgs { inherit pkgs; };
|
||||
in
|
||||
{
|
||||
default = vm.config.system.build.vm;
|
||||
crowdsec-hub = localPkgs.crowdsec.hub;
|
||||
crowdsec-firewall-bouncer = localPkgs.crowdsec.firewall-bouncer;
|
||||
};
|
||||
|
||||
###################
|
||||
# Utility scripts #
|
||||
###################
|
||||
apps.${system} = {
|
||||
default = self.apps.${system}.run-vm;
|
||||
|
||||
run-vm = {
|
||||
type = "app";
|
||||
program =
|
||||
(pkgs.writeShellScript "" ''
|
||||
${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
|
||||
'').outPath;
|
||||
};
|
||||
|
||||
update-crowdsec-packages =
|
||||
let
|
||||
git = pkgs.lib.getExe pkgs.git;
|
||||
nvfetcher = pkgs.lib.getExe pkgs.nvfetcher;
|
||||
in
|
||||
{
|
||||
type = "app";
|
||||
program =
|
||||
(pkgs.writeShellScript "update-crowdsec-packages" ''
|
||||
cd "$(${git} rev-parse --show-toplevel)"
|
||||
cd ./pkgs/crowdsec
|
||||
${nvfetcher}
|
||||
echo 'Remember to update the vendorHash of any go packages!'
|
||||
'').outPath;
|
||||
};
|
||||
};
|
||||
|
||||
###########################
|
||||
# Development environment #
|
||||
###########################
|
||||
devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
|
||||
sopsPGPKeyDirs = [
|
||||
"./keys/hosts/"
|
||||
"./keys/users/"
|
||||
];
|
||||
nativeBuildInputs = [ sops-nix.packages.${system}.sops-import-keys-hook ];
|
||||
|
||||
packages = with pkgs; [
|
||||
sops-nix.packages.${system}.sops-init-gpg-key
|
||||
deploy-rs.packages.${system}.default
|
||||
|
||||
nixpkgs-fmt
|
||||
|
||||
cargo
|
||||
clippy
|
||||
rustc
|
||||
rustfmt
|
||||
rust-analyzer
|
||||
pkg-config
|
||||
openssl
|
||||
./configuration/hardware-specific/hetzner
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
############################
|
||||
# Deployment configuration #
|
||||
############################
|
||||
deploy.nodes = {
|
||||
hetzner-1 = {
|
||||
hostname = "116.202.158.55";
|
||||
|
||||
profiles.system = {
|
||||
user = "root";
|
||||
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1;
|
||||
};
|
||||
|
||||
sshUser = "tlater";
|
||||
sshOpts = ["-p" "2222" "-o" "ForwardAgent=yes"];
|
||||
};
|
||||
};
|
||||
|
||||
#########
|
||||
# Tests #
|
||||
#########
|
||||
checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
|
||||
|
||||
###################
|
||||
# Utility scripts #
|
||||
###################
|
||||
apps.${system} = {
|
||||
default = self.apps.${system}.run-vm;
|
||||
|
||||
run-vm = {
|
||||
type = "app";
|
||||
program = let
|
||||
vm = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs.flake-inputs = inputs;
|
||||
|
||||
modules = [
|
||||
./configuration
|
||||
./configuration/hardware-specific/vm.nix
|
||||
];
|
||||
};
|
||||
in
|
||||
(pkgs.writeShellScript "" ''
|
||||
${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
|
||||
'')
|
||||
.outPath;
|
||||
};
|
||||
|
||||
update-pkgs = {
|
||||
type = "app";
|
||||
program = let
|
||||
nvfetcher-bin = "${nvfetcher.packages.${system}.default}/bin/nvfetcher";
|
||||
in
|
||||
(pkgs.writeShellScript "update-pkgs" ''
|
||||
cd "$(git rev-parse --show-toplevel)/pkgs"
|
||||
${nvfetcher-bin} -o _sources_pkgs -c nvfetcher.toml
|
||||
'')
|
||||
.outPath;
|
||||
};
|
||||
|
||||
update-nextcloud-apps = {
|
||||
type = "app";
|
||||
program = let
|
||||
nvfetcher-bin = "${nvfetcher.packages.${system}.default}/bin/nvfetcher";
|
||||
in
|
||||
(pkgs.writeShellScript "update-nextcloud-apps" ''
|
||||
cd "$(git rev-parse --show-toplevel)/pkgs"
|
||||
${nvfetcher-bin} -o _sources_nextcloud -c nextcloud-apps.toml
|
||||
'')
|
||||
.outPath;
|
||||
};
|
||||
};
|
||||
|
||||
###########################
|
||||
# Development environment #
|
||||
###########################
|
||||
devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
|
||||
sopsPGPKeyDirs = ["./keys/hosts/" "./keys/users/"];
|
||||
nativeBuildInputs = [
|
||||
sops-nix.packages.${system}.sops-import-keys-hook
|
||||
];
|
||||
|
||||
packages = with pkgs; [
|
||||
sops-nix.packages.${system}.sops-init-gpg-key
|
||||
deploy-rs.packages.${system}.default
|
||||
|
||||
cargo
|
||||
clippy
|
||||
rustc
|
||||
rustfmt
|
||||
rust-analyzer
|
||||
pkg-config
|
||||
openssl
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
porkbun:
|
||||
api-key: ENC[AES256_GCM,data:p3lqvGc8m2U/12rBPjoNR7hxQyD52CyEen/V8q59k5CSJZSqzZS8M5vEXFBsUMjz2lrmKM4pgtz4wa2fWK6Ty4LJCaI=,iv:OQC3FpwTtPmqHvDbA41mWF7LGYwC/jD2ZMBsE8ktNOg=,tag:kq5hUR7TBgczuGcXpsdu2A==,type:str]
|
||||
secret-api-key: ENC[AES256_GCM,data:zV5PTKf45Zab8uW8mbuXmPNzciq6tV9OF0wUND7YnRk/DjZneYWItAsNBVoM+iHA+XsUPDoeKo6hoJiGkH/cCQ8WvuM=,iv:yr1M5DlgI8k6BgzNz3HRnqspHOrQuf2PmoZS1HGp0v8=,tag:JkNNziMMfKFZV2hnx5lXRg==,type:str]
|
||||
authelia:
|
||||
storageEncryptionKey: ENC[AES256_GCM,data:OUCC+6Gcr6U7Mub1+DaIyswTV6da1wd1u0WGEm4wpJ8L0mi7WSpEmVjH79YyRhp7AmiZhdFFDXFeEYthBb2AZl+xoS9gqs6rWyfU4ezaCbXBiS/dIhsA5foPg13wq5A33qJWtPTy7DJEgqHaIonnaBuVJIBwH3wzPTHc3bDvBo4=,iv:intiZzngz5cMTtjEI9rTKMW0Xv3KB3ZEgtYN3amwKCE=,tag:AKxfbeZlPs54esHCsVnNCg==,type:str]
|
||||
sessionSecret: ENC[AES256_GCM,data:GEMWhBltOIOs0g9FsWk3OQGs6dMcbwz3ZuhlyBFYROylsIZb4xTXWLgNwIpHwQukQU3TgvIxbCW/fGRWiALPanE2koSVAHNx0UU0hj1mVNRFQGK4H3EL10tPp7l4PofrcdeCbLPrOwM/xLOuPt+52sKlcbL2Awz5/MmpUVpCKXc=,iv:kWX2ptOpTgW3obBgri0MvVv6gCEPR3o77sldOXFQeks=,tag:je4pqLcEOhuBTQkoZHYNCw==,type:str]
|
||||
battery-manager:
|
||||
email: ENC[AES256_GCM,data:rYLUACXR/n+bLBmZ,iv:sUBEkh2+7qGjHZ5R23e/hoCiyTA7GTL4bJvXmxjZ5Sw=,tag:fdPMllaQQfRgX0WZKIre4g==,type:str]
|
||||
password: ENC[AES256_GCM,data:7cokZa6Q6ahSeiFPz+cV,iv:vz405P0IcG9FsAQXlY7mi78GuushQUKJm2irG6buGzc=,tag:JLHG2jTkJDGbinAq9dXRsQ==,type:str]
|
||||
|
@ -16,9 +16,6 @@ steam:
|
|||
heisenbridge:
|
||||
as-token: ENC[AES256_GCM,data:+2yo6T18j34622H8ZWblAFB2phLw1q0k0vUQEZ5sFj7dQaRnkEiAMi0R3p17Zq0pOtGEC0RRZuPLYkcZ1oKP0w==,iv:lGwrQYp//FufpmJocrLIVyy9RK7lEEVcpAi0wmkjr34=,tag:yV06UbhAYJQz36O2XdhY+A==,type:str]
|
||||
hs-token: ENC[AES256_GCM,data:u52WpkQFd/J7JFoE/rfNluebyZQLOokvkVdL7+AEAvrhJhrkJli1ztkD79lbC+6tGUH4tT3T+nX9wvGKnrRUQg==,iv:as+9fVuvMg2IoE2WIKD9mHi+znhNcWRh5Zq+yr0xcDQ=,tag:mZ7fh7U0MfgI8hyq/28Bcg==,type:str]
|
||||
matrix-hookshot:
|
||||
as-token: ENC[AES256_GCM,data:nXTanPhDyDF7R3AllLqpM5dzljBrHwlh1KJnTGIi5PhbDY2lPj4+uXkMEwvm1u+hQjPyM7vKZPfK+0/dms6Y7A==,iv:fSakJN+yai0gfOJKFxxaxgyUtk0pNmIeqVgrdq92/24=,tag:Qc7+SUnm5/Nq5+QIScR9kQ==,type:str]
|
||||
hs-token: ENC[AES256_GCM,data:Bwyj0JTTN0NNnwOs1zA8CqbtZSNcvlINeT7QVc2eJiHda92J6vQk7bSxy6KuqCN9DxlUsK13ggYjNORY2vic5w==,iv:Npnp8arYQ3Yb6CXrnKgE03hD7ZjGINPa/DwFI8D+5tA=,tag:FqNE6yI0nF4puEUw9MGAjQ==,type:str]
|
||||
wireguard:
|
||||
server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str]
|
||||
restic:
|
||||
|
@ -37,63 +34,63 @@ sops:
|
|||
azure_kv: []
|
||||
hc_vault: []
|
||||
age: []
|
||||
lastmodified: "2025-02-07T19:44:49Z"
|
||||
mac: ENC[AES256_GCM,data:+0hpd/E7GxK/27f2Itf0hDV+3Ga4gHb8xxLutJ32HLBWLZ5Y+dN03xgkz8jBTiM+BeHwS4gz70Cs9X3zLMHbosWVuIV9DLuRaHRq/IU9KiADwqmCySZALqCf3+T5QKZr3Qs4AZJHwaAXkRX9HbnRFriIAFDJW/BGdIHdoROquxY=,iv:TeXI8LGqHVa5wo61sGdNbZ2nJvSlPdgn9R3Lq5qUggU=,tag:TFort5wxVTdi9LMlMeT/DQ==,type:str]
|
||||
lastmodified: "2024-04-11T23:38:56Z"
|
||||
mac: ENC[AES256_GCM,data:GjIB0EbWsh4o+QoFSyIXgGYnNhRlvfSmue1LyTt6oUlIjNgODhdIB8px8LnRo0rmm/f1YHbDq2MFOxlgdm3PTNaqm/MoKyW3r/wuAeWADsYayQszLNxyhTMXcjWtfm6zCRIuc/+YyM44pXRfVrOZRAin9B6pmJZsRJwBAZpogbU=,iv:r/ZQZvrP0E9dOW5fhBH2I21Z0uv2e3njdEGmadxEALg=,tag:iZvbGTvRJFo80n8aoKSSmQ==,type:str]
|
||||
pgp:
|
||||
- created_at: "2025-01-21T17:55:44Z"
|
||||
- created_at: "2024-03-18T04:02:00Z"
|
||||
enc: |-
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQEMA7x7stsXx45CAQf6AjhhYCHhRvJx4xxiXPMTfpIpAvseB1CXVfuhfj2r2yrf
|
||||
3HfJnNOSDBcmHdp9fiLJattqfsykcGisUDVIplCeA7cIJjH3sf8MuIJXDTLmvE0Z
|
||||
BmV8LuwP/UPQSZzY3w1eTAoYT/by35gfJm4ofipft2qyIjQuBgOlrg/0swvBl7LK
|
||||
47mI6mVdds8RutHw/xhJZeNjKF/5tADPJ2CHjOHbCQhLji13Kpm3yObOnM8K2SfV
|
||||
P9uVudCFN/ZBiTlVkB7PsuitwZx1fW0SR3jcWxbRd17M2k2RAQQDUCqPKaoJ3T3f
|
||||
r2ExwmyO4j7G2vkFv1RgQnhAoHqqRZ1nSjNw1+27MNJeAbT1ddia2TC5Q2zjRZY9
|
||||
tRJi2pNZH9A424lpIBLnkPl5rpCR/UZ+bqhaQ9C2kFMldSldPn8dMiBy8XG0Trji
|
||||
B6X44Q/0RCsJD3FS61GASIjaWdEX0DdSOdhtbtBLLQ==
|
||||
=yLOx
|
||||
hQEMA7x7stsXx45CAQf7BjF+HR3WKdMyAV6R1M0+lqDz6hBHKyGH7YBB/QZBqRbK
|
||||
3hdABIwWUsqpHjleEOp/Gj0VhZqwagqHxK4Fp5G0r3QBupbAO8u/+DNI8wll0Nva
|
||||
dlOh0Jqp4E17TkERMQL02rrQ1ZmpOYmPkCd2//xkmWAQ1LatHWeRVSRxQBuMtPQi
|
||||
btrefcQNjQCvS9/60dp8oTu8nxlFA4iHCBQKNIKVGqQH7jkdIfMPdUILjCkCiyCc
|
||||
h+OxlHZZnpU6U9A+hjMBinvCzebSkZh48VX/T33Kr+4b0CBr1gR9MSXKG9f2MPQP
|
||||
PMl6rPvqSqG6ddN9QDI+0HEHYaRvxPIV8uDS36tVxNJeAQHB5/6Lt7hJdYWgwf5E
|
||||
TLgbZ0IxB17++6K++GlaG8WHO65l1jzmkPlN+ZGcwnhibDxnZjP6kqGqDFcZP4ge
|
||||
cnV0KnhYcC59IooQYrWKzAJex9rnwPo7MGKV6XwZOQ==
|
||||
=Hy9T
|
||||
-----END PGP MESSAGE-----
|
||||
fp: B9C7AE554602D82B1AC2CFD0BC7BB2DB17C78E42!
|
||||
- created_at: "2025-01-21T17:55:44Z"
|
||||
fp: 535B61015823443941C744DD12264F6BBDFABA89
|
||||
- created_at: "2024-03-18T04:02:00Z"
|
||||
enc: |-
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQIMA9ahl2ynTH87AQ/9F/lyLXn60X5Slcd9I7VPdM6x4IjFI0UwMAhMVv0GeimD
|
||||
9Mj1vKW+l1v0gFtqCGjANEEYpXMr109CES+m6z7dNXcIQeR6pyjW6uCTiPUK/ZO/
|
||||
ZQyhJR0zsT15BTHc6c9Eso9YedsggpRAbjjVIlmrTJx52LfqYYGoRA0XqcLE6LUS
|
||||
6v6KR/299zzSHsTFVon6wac9wY7i1XvL0Q7JiYSNV2cNADiCJdCIBw1UkaSVH1uz
|
||||
sTVdoEoQ1m7hu9pgg4UksUzi9IV3xBpte82tlEK9p0M+RXK/02fjMTWcx3q5fnjN
|
||||
pBu3HkVzLpAjAQHMQ/O2cgbjFmwgjp5APUhn3IBx5M3F2ypGICnAvsrjfzalTChN
|
||||
nkCmTfLynNIL1bX7PViABX2Q/yXGzPqFDe96pPw2hGjOXmxjKxjDoLT2IHixYp4D
|
||||
cxb5519/WmOMrFd4SJeGiUR2Ph/VBZBVGafRfGAKMzxi666igWjwSU0YSDYeEUW/
|
||||
BalkkWoz/KQZ5HgQwL9dyp26/cjDtpIwe8jLKVNI6aWhZ2ZmBxFwNEB6fE1txOjf
|
||||
ceIJAfm8y2qIolw3TpBAFk6s53jir17SrEux9VzfiEfFeQ0g5q7cjAs5HfhTOMfh
|
||||
iuUnHcCFy27wd+8bPxEaRYR57u7hneenTn3BMuu1CZputDFJWRvweMZk3cH8tiXS
|
||||
WAGpXw7aKGIOpxe7Ye5X+T3xvYCBN77aFQKkrOmHMWFCbkr2QfnwVanxmmL9BwxF
|
||||
rf8pG+H0URxBAsy9RZzSC+dXugnwnNBse3wupXf5YkipLx3rX9gtz4Y=
|
||||
=a7xO
|
||||
hQIMA9ahl2ynTH87AQ/+LNXxC3acjs2+c38gHZRW6Am4XFx1t/4tfxIgaaK/Boq8
|
||||
PGU5CFNOMDGv8u/cwyDbfNM7GuL5g7vrLmBXzSV5ErZqc6bJ0+ZCNPTRIxP1Vxph
|
||||
tWiDIyTwuqUzxWpOlSzii2Sqhlp8CyiWzBe95eIr96XzDCCtfzyCZ0BYyKgpHHxB
|
||||
BltH0/+0JZFiR19zvf6M99AHwM8OddRQkXav+mRIJQpA87ovVZcAv5skYGJgNCqN
|
||||
55fbskuYmqEnloQCZVJ2+2ZXK5Qn/uq4fLJCiIdZm4YsctJnV5spzZIL+dcOty65
|
||||
Plk77BWzLaU5UOKCBAJWrK8oZSTGOrp4VZqb62DuqMRejG0JXmneIVk7p79yn5eA
|
||||
ANVMGRF4b8RP9YUhzE8HACFzQebKpUU8XKv9+qsmO9Le5jUhU3UQeCSSzT/T5Dr8
|
||||
kLDNtmW+mliQnxFlKcVWq2JIG+HaQD1KLOAl0JBNCOSLif2ofaHahuZ15agbYeis
|
||||
hyrBY92EhzqYXHk/Kzv4ff4r+WUs9NN7R4Gg+wfWvMcTtVfbi4Ht+pjjTtCZwK1C
|
||||
M8JebQn0NZSpVi3e7Xaz1fQ5Tqrg8PHZtkYGoIHLRPJQwLn9PHYtGzC3rFAk+Fqq
|
||||
5WWHELxfcsZ6DakAGSXPK/80QhEZkpGmKizTwrEde+7fpEPxjdzUqlmH3rv7mFzS
|
||||
WAGSiBIMjLR6ofb65vpghbwh6gXkpCtgUyINRhx/D+Kj5Z4lGD1u1I05DT1xD6VJ
|
||||
FAbnH7oZ3PJecoAXgRT05FndFA1xfPMCkugmec8ML/sEZt+c3kbrXaA=
|
||||
=MqS3
|
||||
-----END PGP MESSAGE-----
|
||||
fp: 8a3737d48f1035fe6c3a0a8fd6a1976ca74c7f3b
|
||||
- created_at: "2025-01-21T17:55:44Z"
|
||||
- created_at: "2024-03-18T04:02:00Z"
|
||||
enc: |-
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQIMA0f3HaPlrXn5ARAAph6bpRqnO1TKEE7K6cMCxCA9k3xj7hknBdHN1KlIQkeb
|
||||
LUOW9lvxh96q/fQ3NvZXpEQqZoEgdhnYTHgr/Hqx7VSksSicOkJLorrBLELjP1Xo
|
||||
FKTxRYQmgZmgmBc0u7+lF2t0VLey6DfsC9ehY2LQcRuY9WxxCp2nDjjR4wrfmMKi
|
||||
i/SbpaM9Q29DpdNpwkSlTe2sKr+uMDVA2rCrjpisSDiUBgPe2eJEYPQJ7DHFxB4N
|
||||
26NHS+QtUgZhAA1DOGYZvrXzqcZSeAPGk8WXY44KA7o2iRFw+TczpF/VaMHRR5vN
|
||||
M62IoXGqnMIBau+tsiE1JEVg60DIHpBjsEY/WUgbNX02zfaHcp1OenxU4p7W19Xn
|
||||
Y8psPm7JNzmi3+nItB2i3+OLQrumk6VaT021ykterc52tR/3ejCzQCsfbqDLnc/6
|
||||
r2SvNVDiKpxQ/iFyHb0uLHWy0Jx0lYwmMUrThe1f4k/+m9fEPrA5v/1kTGY5o1Iy
|
||||
aTuHE19VtB6V+g1H6ZRPr8g8wn/3pg1nC2vUoRzT+oUudxrijrUH8SSlwhu59y1z
|
||||
7SrdMmtqA1/JsGNMawCLWd63u+/3GC9LmZzK2h/bV/R4DG5f8tsCpy9BrFAHqTMs
|
||||
lZ3SONUUeM3uMOggQ/JT26EVxECmNGYIX1TYLOxcqisRXLbfco5LEc6T8PJvdivS
|
||||
WAFr6kBMdoeWhUT8MdT5AJBm9mo95A0WM8I14pGrszaezfgo9zc2zs0ebdLEjhI4
|
||||
jlTf4tXgK3RG455CRWOd9OA3ukR83W7UW6LYjoNaDWHw2RXZlb+hv8E=
|
||||
=+MFT
|
||||
hQIMA0f3HaPlrXn5AQ/+N5NK5UJdtw5e7O9T4hfIhtMXci/og1cJiI64daSyNeDH
|
||||
jq+CPJ8e73yiTPwu6wHHqfuEhlEuI6sJY0ZJVFU+h4SIBtG21veGEz7GzlYgBCJm
|
||||
xvJHXjtM8AprqnFVO7Fj9QA/ik5QBP6ZpkOY7j8/qf1G7alOIne/MYRALXDvvIMH
|
||||
HTWE+Y2N57yZK55Pokmdw10hawbrn/N1nt2Y7sa1+5TlRNtuA/+zLkXtEjRr5U3N
|
||||
DId+hqCKgXDqKLBMkh4mZUTGOGsk8eeKAWwyPp9+8A5/0rfy+xOJYEjjHICXQMSE
|
||||
zfe6qvj/fRJKGzT5lEzD+ZKHlR0zHEwGRfHqrVUTdPcPdKj3DZILjsoe5ba4VlAp
|
||||
sS0CAYTg3YuWMT4iHuOQlY5IoQxHHrn7k8ox5iZULFecg58f6r6iJL3AepDYWAey
|
||||
gtQXYBeaeCm5Ddwmd6TBVz8Q4bCVYIrHbVeAhSDkxfrWLc5UORggvLEWiXilGDJi
|
||||
DzAv0MVHE2Wa3eOJLq05K2/LBqRBD1XYM3dcS6JSdFxWWMzvLdUOB4dAuPt9gpl9
|
||||
liaA13Blw/ev+U4ADxptrl+QuYRbWz3z6rniYpluSrTbVCKFRoHXSGFPy5u8/N6O
|
||||
QyjfoovIBxXKnbUq2kMoFa/qFpc1pDUn0sjQNsUBdtorAu3Up4icyoih7qwx2J3S
|
||||
WAGB1jHWMfcsBJqPwjRYkqBf6MuwHAZWdd+zvj/fKfft9jtxLcCGOIM6QdfiWbl0
|
||||
Wq4gHdH7OhSy+ZgRnaBRt/GAkzkHvfG68HfulviHZ1h2mrQN1y3mxpg=
|
||||
=RCYB
|
||||
-----END PGP MESSAGE-----
|
||||
fp: 0af7641adb8aa843136cf6d047f71da3e5ad79f9
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.2
|
||||
version: 3.8.1
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
porkbun:
|
||||
api-key: ENC[AES256_GCM,data:A5J1sqwq6hs=,iv:77Mar3IX7mq7z7x6s9sSeGNVYc1Wv78HptJElEC7z3Q=,tag:eM/EF9TxKu+zcbJ1SYXiuA==,type:str]
|
||||
secret-api-key: ENC[AES256_GCM,data:8Xv+jWYaWMI=,iv:li4tdY0pch5lksftMmfMVS729caAwfaacoztaQ49az0=,tag:KhfElBGzVH4ByFPfuQsdhw==,type:str]
|
||||
authelia:
|
||||
storageEncryptionKey: ENC[AES256_GCM,data:8X6Zvq7ct1MkMH+lc5kTTHXOo6rGyhSzc3aWxGRA5tnBet+TGcENo0RYTYmactsPGVpTIUGGplaG7B7dqRPhkdDHhbCCZCm2nLaYjpVJ241DrpUNKHn8lvg/bMxUQ/Dvw76ByYuWN6bREr3XRaBztBSPzld8zTSYx71I0CKY7vk=,iv:cJSwfuVWO39qqKCGt2Mvw7pN8+hD6kRH9v4c/u4hLuk=,tag:YhdlXuX2ETxjb443RI8MsA==,type:str]
|
||||
sessionSecret: ENC[AES256_GCM,data:dnoWmc4HND62w3jMXL+akncAEb61c/I70DgRytx55Wxcn4rMiswp6zCkRdsP4CkouTQ1lyAcQrubp5I8M9Kyow/KBMYz9dPkr4+2xJ9w0SEmAVhyPe2DFvYos3x0Uvx5S0B3o1mXoXqbg78e4w5yEIbALiJT8VPGrWK8Cl4nVPo=,iv:FHDXUW2DWUmEZzWUYkYduogdVOtvMlRH4/fVg05cZaI=,tag:u282WQnHpBsZGYJH7mFFKA==,type:str]
|
||||
jwtSecret: ENC[AES256_GCM,data:0M3AyoMp+orrljl5NsxmthzrHMmu0REcz7+9fpFKbwwqV6KqlpgGddjYZIsTpHEWEq9zhZ2YWLJkMxKdDgROVHUFZGKut28JPSAjjY+1V0wxNBnfSCnxEv5BUw2+cCxcpCwYQyNfRK6SotTt8aqpxvda4oRXpzxV6SW7ogDjc6E=,iv:D57SynZkW2JuFyX6bpZYkxpR2KtkOmKaySg1Bxim0r8=,tag:JCPGZaumdHrtgcH16A7b+g==,type:str]
|
||||
battery-manager:
|
||||
email: ENC[AES256_GCM,data:LM/EGzWHfVQ=,iv:jFaoUQuUfuGoOyj/GFpdI8TerH/c8D9fjvio+IEt2Tc=,tag:IWLiN011JEnHRLIXWQgfmA==,type:str]
|
||||
password: ENC[AES256_GCM,data:SUxjqS7SJHM=,iv:LvdKk88S+nSImh6/ZezbFGLCUBu1Lpdu+neF2xyHdBg=,tag:rcMyZuW4FVNbcbz00wQKBg==,type:str]
|
||||
|
@ -16,9 +17,6 @@ steam:
|
|||
heisenbridge:
|
||||
as-token: ENC[AES256_GCM,data:tXbOeo7nv8I=,iv:wJAKcOXX9nGIw4n38ThOoj29u7dUWhsxSQG/p79JlEw=,tag:rTVaGS2UuWcea1uBa8YX2g==,type:str]
|
||||
hs-token: ENC[AES256_GCM,data:VBwvwomv0Xg=,iv:q6INtJ+rg+QiXj8uBdBzQYQZUBBXp+9odxDHwvu8Jxc=,tag:XKhm8nxygAkKaiVPJ2Fcdg==,type:str]
|
||||
matrix-hookshot:
|
||||
as-token: ENC[AES256_GCM,data:uSUOo4f2KqA=,iv:Xb9G8Ecv6m59m51kDw2bOfq3SMJt4g9/6/EdH74R+KM=,tag:K9MSfO2c2Y4rlf0eYrmTnw==,type:str]
|
||||
hs-token: ENC[AES256_GCM,data:0KsyA06InL4=,iv:zAR0Y1fk8SyodcSLBHlQ8I+BAmttz9Hkd8Q3OREFqs4=,tag:t1Et8N/3seq95DeGoUd7Sw==,type:str]
|
||||
wireguard:
|
||||
server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str]
|
||||
restic:
|
||||
|
@ -37,43 +35,43 @@ sops:
|
|||
azure_kv: []
|
||||
hc_vault: []
|
||||
age: []
|
||||
lastmodified: "2025-02-07T17:43:24Z"
|
||||
mac: ENC[AES256_GCM,data:akmD/bfgeTyFzW1quvM16cdj0fC6+CbJ8WyX9173H11yKGxvE1USQYcErpl1SHOx9Jk8LVb7f+MsUm2fjQF1MEq6xaWI74jem12lZ9CGXFaTL7e87JvfbK7pV+aKpxSBBNFyJgbYm30ibdUwxwKmNVfPb1e0HT9qwenvoV7RobM=,iv:mKqOW0ULXL711uczUbRf9NPo6uPTQoS/IbR46S+JID4=,tag:vE6NYzYLbQHDImov1XGTcg==,type:str]
|
||||
lastmodified: "2024-04-12T01:00:31Z"
|
||||
mac: ENC[AES256_GCM,data:fVnMwfvGi7vtP1Fg4NLrhGvLF2PcIgZPOcwk4Ssm4iw5iSj0K1npOX3pd5BWzyszqchfYYRHY99GllAump0bZmprVAld9rf70B2HZIVvowBPuUXfc9Cz/5q0z+s8bQ5vCdElW1Bh7h8W/POePdc8cFGAyBS4i1ZVNheIDOHdDjI=,iv:Bi6rekXOx3/dwwPRryF3CoAoQi3D06ABysRF1oBeG5A=,tag:0TCra+AkhBDczj4uvAzKMw==,type:str]
|
||||
pgp:
|
||||
- created_at: "2025-01-21T17:55:30Z"
|
||||
enc: |-
|
||||
- created_at: "2023-12-29T15:25:27Z"
|
||||
enc: |
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQEMA7x7stsXx45CAQf9ELnm9TdXCIO6fTPiSCkKthx0tSHqBWX/s63158k4IUu7
|
||||
v0WWgQy0SKFU3AwIFuVaAYEXB32SaOWKq2WbVAbFZU+xhyUmNe9asg9Fl24+zjGI
|
||||
oYnPzv3lz/5vcI6Q9rZi8F2uIi2GQZnbscS1XfjA5u17uOalQpb0hjUXr0LMaUvU
|
||||
Sggm1ZMKE1o1mHAWK6ZT9SrTMIroWFArJRZLS1eY/vI9ja7I5YR3z0MkLqIvdIp+
|
||||
B4DsOXLlqAqtVoPGcK1CixQiXzzwyQAYHyJc3JFDpaF9Y4S5/bkMLGyQVMA259a+
|
||||
W7ge+EngdJdXV8Unj4s7ndB1e1iM87Jc+4YOcA7jC9JeAR98n8GL+MN9vE8q2AsB
|
||||
qSOXiGSwmAkhq7+ZwJHWlivlB1In0coyJ4eMd/yhyBuc2NrstO0t595HlA93GcLN
|
||||
5JsXIFMqklqGSzE2KgEXhxa2aUoJxcpApVz2BLFPvg==
|
||||
=X72N
|
||||
hQEMA7x7stsXx45CAQf/RWxP6z7xjV5TqiA6lFhtygjrH9x3y1DUWG9aUb/dO+xH
|
||||
zDbGMYqGe9RPlgi5sWPstdKXvCgs+AKNj93qJYMwEtaasJOinYXCGeAQmzg90+pt
|
||||
bS6SoBHhGIxAvvLKKPtYx0V50I2reYR+32ux9bcrnzwIsV0P7/SSp1Cl8H+sotB8
|
||||
yf+0ULXcpC+SYECmZqzR9qQ3S+3I6/+QS+QgWj4NsyF+apxnE9oQDcBLdYP4aKgR
|
||||
JHERA9HYfDTKoS137pFHxgINqHkFRY6lhoZdz1yDzOjiPxd8YVfPdKyf022Rg+cX
|
||||
J/Q2P+OhNZEG3gapNATp6wH3niovA89KwZKSmbTZOdJeAZ6NV6TiUP+TgGg5+CmV
|
||||
pSLaGel2NZRnFVNdDFi0dsOwhHv3FpKhIpALJh08/jsmAAslfE7vVlcEnaoUJPTS
|
||||
3v86AACUC5D/gUxmFrrED1qoxbELCmZ17xTwjQzxwg==
|
||||
=KzdF
|
||||
-----END PGP MESSAGE-----
|
||||
fp: B9C7AE554602D82B1AC2CFD0BC7BB2DB17C78E42!
|
||||
- created_at: "2025-01-21T17:55:30Z"
|
||||
enc: |-
|
||||
fp: 535B61015823443941C744DD12264F6BBDFABA89
|
||||
- created_at: "2023-12-29T15:25:27Z"
|
||||
enc: |
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQIMA/3lh+ZzfS28AQ//TPVk5x8wZtKnNcbZjqmXUvi7FzaDMGYFNtiX+bQ17Lkm
|
||||
qdo9TcH8gZI+xni6wlROQBVmu5MAVkT+NWnZIyxN7pokQPmb2v9zo7oyUPV70owU
|
||||
dD/LDNVqgyFTVzWKco4wr8CUwQWhOJI9wM0sjTCOTTbCT5hYVnJOLe630sNG9G5b
|
||||
cJYvaQFxKJeHKJq/DL1I9wT02gOE7vu2xh2OyEozEz3SlB7Bp2mGTLNSiOZBhbzh
|
||||
DNwFRTeDc1Z/ACQJyoGEXkmj1VLyBFuCfXu4UvUQSmlfyCXKTD1/CUo2uiw1N44W
|
||||
sYh5UpIrCU2eVAAXAiD1nB6dHxiau3QlapNQfbY2sOZVSnsB21yIja9C3aQN/0Q5
|
||||
gRrKYcwULzLZ0Z3oQqQxQG9acU8L9CwjKJ1vOKgPVF7hcWkba2bLVsMMaK6seVNc
|
||||
jazp9gDAj440S2aE86CdQvgcOEsfgPhBZrulYglbhW8ZaIN2SdjDN/xP1Tn5PadP
|
||||
gS2DVgqILKPRMF4VavzV3uhEA77QF39Hr18SeToNWcDmfqNPNNf7HpnogstGPf3y
|
||||
xrFAysLbD8IClU4LqI5M19akODON8qeQa5QD+jHOJmAYnMYNRmY/IwHb/SC5WyIm
|
||||
EPNZg5E+q9cNrTKtEIuWec0SObqpaUz2E/Vt9+dge0uVgTA/QqPvMP7x19XBrRDS
|
||||
WAFiKkv7MxImNgcqqe7D3StZoeNm+RXJiULaxxuR8qmMnmvaC+L7ggI4QR3TPQw2
|
||||
mSoi7SkdjQUCa8ut15UwNHTGO+smbRs9aonGP4G7c1cOH90YYvR+BTs=
|
||||
=q2IG
|
||||
hQIMA/3lh+ZzfS28ARAAm729dMouF7juUeHAb+aHMoyZVKsXapxnxebkjE/LSIbz
|
||||
IEZwegTNrtxQJLclV4Km2gUaBTcE4vLJCpB7YxZvk7JV9OdVKi97o9PcXUXbz9ej
|
||||
/WomnEvFyyxTZGTiHU+L4kNudl8UAKhTt3P4fR3PLpTily75Kn53tzLFJuCO8fAY
|
||||
I/YwQAzayxhPcxk3FuPsD/ONiG7mW8n2ZwfwgOkKXwnrlJv7DreKJRYzu/EeuvX/
|
||||
d4oz+k+xofniOeZmQjZllzR7/++MBg/e1U9VocN1EAWpWHP5taLiThfnVSGDhlQM
|
||||
+4WT5ezH6EuUQlAyQNpDaCincBvCHInhrNlUPOpW51nHMb0y3n4x2hMtZA0JbYEu
|
||||
mkWTYDe65cHjImHXQk9oO2/v4oIyq7ywHX7g2hqVbbiLHZqqTaGfV8lP30+r6/UQ
|
||||
29iAdWac1hY5HDzwbqpY6b38i60j4bkiS83xqrGYBy037bCFk1oHJqwxp5P7vrzr
|
||||
rTv5NBr95BlwF+s8xPEPZneaEu7N3UnhhSzDWp1jgsCxN9b/XHarchNt70xEt2VS
|
||||
xpgs9GEXhsJcbrFNPYqTkFb8vjLFI+poGPTfadW17j4Pp5ftIBRNdKvDG0ni/AIp
|
||||
K98R/nvaHEFuX31SkL8ZUIRqhJm3JVqilFxLAJrqGuSN3jA6wKrimUYpK+t+64jS
|
||||
WAEN9jHYFQDTVHix3g15S5YTGh5ROyqxouDhvSDFTmGtbm5W/HYgnkZmh53TgVeJ
|
||||
Rph/O9QptculzTN+nEqshBhbjhl/uDsLsjLYo/O1AyCwTUSd3OKn6uU=
|
||||
=zThh
|
||||
-----END PGP MESSAGE-----
|
||||
fp: 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.2
|
||||
version: 3.8.1
|
||||
|
|
|
@ -11,230 +11,62 @@ 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
|
||||
GmeDneCrlseO
|
||||
=ncr2
|
||||
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
|
||||
PhCB2Dz76iQ5nDY4MQ==
|
||||
=R7Pm
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
|
|
@ -1,383 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.security.crowdsec;
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
|
||||
hub = pkgs.fetchFromGitHub {
|
||||
owner = "crowdsecurity";
|
||||
repo = "hub";
|
||||
rev = "7a3b4753f4577257c0cbeb8f8f90c7f17d2ae008";
|
||||
hash = "sha256-HB4jHyhiO8gjBkLmpo6bDbwhfm5m5nAtNlKhDkZjt2I=";
|
||||
};
|
||||
|
||||
cscli = pkgs.writeShellScriptBin "cscli" ''
|
||||
export PATH="$PATH:${cfg.package}/bin/"
|
||||
|
||||
sudo=exec
|
||||
if [ "$USER" != "crowdsec" ]; then
|
||||
sudo='exec /run/wrappers/bin/sudo -u crowdsec'
|
||||
fi
|
||||
|
||||
$sudo ${cfg.package}/bin/cscli "$@"
|
||||
'';
|
||||
|
||||
acquisitions = ''
|
||||
---
|
||||
${lib.concatMapStringsSep "\n---\n" builtins.toJSON cfg.acquisitions}
|
||||
---
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [ ./remediations ];
|
||||
|
||||
options.security.crowdsec =
|
||||
let
|
||||
inherit (lib.types)
|
||||
nullOr
|
||||
listOf
|
||||
package
|
||||
path
|
||||
str
|
||||
;
|
||||
in
|
||||
{
|
||||
enable = lib.mkEnableOption "crowdsec";
|
||||
|
||||
package = lib.mkOption {
|
||||
type = package;
|
||||
default = pkgs.crowdsec;
|
||||
};
|
||||
|
||||
stateDirectory = lib.mkOption {
|
||||
type = path;
|
||||
readOnly = true;
|
||||
|
||||
description = ''
|
||||
The state directory of the crowdsec instance. Cannot be
|
||||
changed, but is exposed for downstream use.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
inherit (settingsFormat) type;
|
||||
default = { };
|
||||
|
||||
description = ''
|
||||
The crowdsec configuration. Refer to
|
||||
<https://docs.crowdsec.net/docs/next/configuration/crowdsec_configuration/>
|
||||
for details on supported values.
|
||||
'';
|
||||
};
|
||||
|
||||
parserWhitelist = lib.mkOption {
|
||||
type = listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Set of IP addresses to add to a parser-based whitelist.
|
||||
|
||||
Addresses can be specified either as plain IP addresses or
|
||||
in CIDR notation.
|
||||
'';
|
||||
};
|
||||
|
||||
acquisitions = lib.mkOption {
|
||||
type = listOf settingsFormat.type;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Log acquisitions.
|
||||
'';
|
||||
};
|
||||
|
||||
extraGroups = lib.mkOption {
|
||||
type = listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Additional groups to make the service part of.
|
||||
|
||||
Required to permit reading from various log sources.
|
||||
'';
|
||||
};
|
||||
|
||||
hubConfigurations = {
|
||||
collections = lib.mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
List of pre-made crowdsec collections to install.
|
||||
'';
|
||||
};
|
||||
|
||||
scenarios = lib.mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
List of pre-made crowdsec scenarios to install.
|
||||
'';
|
||||
};
|
||||
|
||||
parsers = lib.mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
List of pre-made crowdsec parsers to install.
|
||||
'';
|
||||
};
|
||||
|
||||
postoverflows = lib.mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
List of pre-made crowdsec postoverflows to install.
|
||||
'';
|
||||
};
|
||||
|
||||
appsecConfigs = lib.mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
List of pre-made crowdsec appsec configurations to install.
|
||||
'';
|
||||
};
|
||||
|
||||
appsecRules = lib.mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
List of pre-made crowdsec appsec rules to install.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
centralApiCredentials = lib.mkOption {
|
||||
type = nullOr path;
|
||||
default = null;
|
||||
|
||||
description = ''
|
||||
The API key to access crowdsec's central API - this is
|
||||
required to access any of the shared blocklists.
|
||||
|
||||
Use of this feature is optional, entering no API key (the
|
||||
default) turns all sharing or receiving of blocked IPs off.
|
||||
|
||||
Note that adding the API key by itself does not enable
|
||||
sharing of blocked IPs with the central API. This limits the
|
||||
types of blocklists this instance can access.
|
||||
|
||||
To also turn sharing blocked IPs on, set
|
||||
`api.server.online_client.sharing = true;`.
|
||||
'';
|
||||
};
|
||||
|
||||
ctiApiKey = lib.mkOption {
|
||||
type = nullOr path;
|
||||
default = null;
|
||||
|
||||
description = ''
|
||||
The API key for crowdsec's CTI offering.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Set up default settings; anything that *shouldn't* be changed is
|
||||
# set to the default priority so that users need to use
|
||||
# `lib.mkForce`.
|
||||
security.crowdsec = {
|
||||
stateDirectory = "/var/lib/crowdsec";
|
||||
|
||||
settings = {
|
||||
common = {
|
||||
daemonize = true;
|
||||
# The default logs to files, which isn't the preferred way
|
||||
# on NixOS
|
||||
log_media = "stdout";
|
||||
};
|
||||
|
||||
config_paths = {
|
||||
config_dir = "${cfg.stateDirectory}/config/";
|
||||
data_dir = "${cfg.stateDirectory}/data/";
|
||||
# This "config" file is intended to be written to using the
|
||||
# cscli tool, so you can temporarily make it so rules don't
|
||||
# do anything but log what they *would* do for
|
||||
# experimentation.
|
||||
simulation_path = "${cfg.stateDirectory}/config/simulation.yaml";
|
||||
|
||||
pattern_dir = lib.mkDefault "${cfg.package}/share/crowdsec/config/patterns";
|
||||
|
||||
hub_dir = hub;
|
||||
index_path = "${hub}/.index.json";
|
||||
|
||||
# Integrations aren't supported for now
|
||||
notification_dir = lib.mkDefault "/var/empty/";
|
||||
plugin_dir = lib.mkDefault "/var/empty/";
|
||||
};
|
||||
|
||||
crowdsec_service.acquisition_path =
|
||||
# Using an if/else here because `mkMerge` does not work in
|
||||
# YAML-type options
|
||||
if cfg.acquisitions == [ ] then
|
||||
"${cfg.package}/share/crowdsec/config/acquis.yaml"
|
||||
else
|
||||
pkgs.writeText "acquis.yaml" acquisitions;
|
||||
|
||||
cscli = {
|
||||
prometheus_uri = lib.mkDefault "127.0.0.1:6060";
|
||||
};
|
||||
|
||||
db_config = {
|
||||
type = lib.mkDefault "sqlite";
|
||||
db_path = lib.mkDefault "${cfg.stateDirectory}/data/crowdsec.db";
|
||||
use_wal = lib.mkDefault true;
|
||||
flush = {
|
||||
max_items = lib.mkDefault 5000;
|
||||
max_age = lib.mkDefault "7d";
|
||||
};
|
||||
};
|
||||
|
||||
api = {
|
||||
cti = {
|
||||
enabled = cfg.ctiApiKey != null;
|
||||
key = cfg.ctiApiKey;
|
||||
};
|
||||
client.credentials_path = "${cfg.stateDirectory}/local_credentials.yaml";
|
||||
server = {
|
||||
listen_uri = lib.mkDefault "127.0.0.1:8080";
|
||||
profiles_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/profiles.yaml";
|
||||
console_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/console.yaml";
|
||||
|
||||
online_client = {
|
||||
# By default, we don't let crowdsec phone home, since
|
||||
# this is usually within NixOS users' concerns.
|
||||
sharing = lib.mkDefault false;
|
||||
credentials_path = cfg.centralApiCredentials;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# We enable prometheus by default, since cscli relies on it
|
||||
# for metrics
|
||||
prometheus = {
|
||||
enabled = lib.mkDefault true;
|
||||
level = lib.mkDefault "full";
|
||||
listen_addr = lib.mkDefault "127.0.0.1";
|
||||
listen_port = lib.mkDefault 6060;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.packages = [ cfg.package ];
|
||||
|
||||
environment = {
|
||||
systemPackages = [
|
||||
# To add completions; sadly need to hand-roll this since
|
||||
# neither `symlinkJoin` nor `buildEnv` have collision
|
||||
# handling.
|
||||
(pkgs.runCommandNoCCLocal "cscli" { } ''
|
||||
mkdir -p $out
|
||||
ln -s ${cscli}/bin $out/bin
|
||||
ln -s ${cfg.package}/share $out/share
|
||||
'')
|
||||
];
|
||||
|
||||
etc."crowdsec/config.yaml".source = settingsFormat.generate "crowdsec-settings.yaml" cfg.settings;
|
||||
};
|
||||
|
||||
systemd = {
|
||||
tmpfiles.settings."10-crowdsec" = {
|
||||
"${cfg.stateDirectory}".d = {
|
||||
user = "crowdsec";
|
||||
group = "crowdsec";
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
# This must be created for the setup service to work
|
||||
"${cfg.stateDirectory}/config".d = {
|
||||
user = "crowdsec";
|
||||
group = "crowdsec";
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
"${cfg.stateDirectory}/config/parsers".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
|
||||
user = "crowdsec";
|
||||
group = "crowdsec";
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
"${cfg.stateDirectory}/config/parsers/s02-enrich".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
|
||||
user = "crowdsec";
|
||||
group = "crowdsec";
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
"${cfg.stateDirectory}/config/parsers/s02-enrich/nixos-whitelist.yaml" =
|
||||
lib.mkIf (cfg.parserWhitelist != [ ])
|
||||
{
|
||||
"L+".argument =
|
||||
(settingsFormat.generate "crowdsec-nixos-whitelist.yaml" {
|
||||
name = "nixos/parser-whitelist";
|
||||
description = "Parser whitelist generated by the crowdsec NixOS module";
|
||||
whitelist = {
|
||||
reason = "Filtered by NixOS whitelist";
|
||||
ip = lib.lists.filter (ip: !(lib.hasInfix "/" ip)) cfg.parserWhitelist;
|
||||
cidr = lib.lists.filter (ip: lib.hasInfix "/" ip) cfg.parserWhitelist;
|
||||
};
|
||||
}).outPath;
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
crowdsec-setup = {
|
||||
# TODO(tlater): Depend on tmpfiles path for
|
||||
# /var/lib/crowdsec/config
|
||||
description = "Crowdsec database and config preparation";
|
||||
|
||||
script = ''
|
||||
if [ ! -e '${cfg.settings.config_paths.simulation_path}' ]; then
|
||||
cp '${cfg.package}/share/crowdsec/config/simulation.yaml' '${cfg.settings.config_paths.simulation_path}'
|
||||
fi
|
||||
|
||||
if [ ! -e '${cfg.settings.api.client.credentials_path}' ]; then
|
||||
${cfg.package}/bin/cscli machines add --auto --file '${cfg.settings.api.client.credentials_path}'
|
||||
fi
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
User = "crowdsec";
|
||||
Group = "crowdsec";
|
||||
StateDirectory = "crowdsec";
|
||||
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Note that the service basics are already defined upstream
|
||||
crowdsec = {
|
||||
enable = true;
|
||||
|
||||
after = [ "crowdsec-setup.service" ];
|
||||
bindsTo = [ "crowdsec-setup.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
User = "crowdsec";
|
||||
Group = "crowdsec";
|
||||
SupplementaryGroups = cfg.extraGroups;
|
||||
|
||||
StateDirectory = "crowdsec";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
users.crowdsec = {
|
||||
isSystemUser = true;
|
||||
home = cfg.stateDirectory;
|
||||
group = "crowdsec";
|
||||
};
|
||||
groups = {
|
||||
crowdsec = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
{
|
||||
flake-inputs,
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer;
|
||||
|
||||
crowdsecCfg = config.security.crowdsec;
|
||||
cfg = crowdsecCfg.remediationComponents.firewallBouncer;
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
in
|
||||
{
|
||||
options.security.crowdsec.remediationComponents.firewallBouncer = {
|
||||
enable = lib.mkEnableOption "cs-firewall-bouncer";
|
||||
|
||||
settings = lib.mkOption {
|
||||
inherit (settingsFormat) type;
|
||||
default = { };
|
||||
|
||||
description = ''
|
||||
The bouncer configuration. Refer to
|
||||
<https://docs.crowdsec.net/u/bouncers/firewall/> for details
|
||||
on supported values.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
security.crowdsec.remediationComponents.firewallBouncer.settings = {
|
||||
mode = lib.mkDefault "${if config.networking.nftables.enable then "nftables" else "iptables"}";
|
||||
log_mode = "stdout";
|
||||
iptables_chains = [ "nixos-fw" ];
|
||||
|
||||
# Don't let users easily override this; unfortunately we need to
|
||||
# set up this key through substitution at runtime.
|
||||
api_key = lib.mkForce "\${API_KEY}";
|
||||
api_url = lib.mkDefault "http://${crowdsecCfg.settings.api.server.listen_uri}";
|
||||
};
|
||||
|
||||
systemd = {
|
||||
packages = [ crowdsec-firewall-bouncer ];
|
||||
|
||||
services = {
|
||||
crowdsec-firewall-bouncer-setup = {
|
||||
description = "Crowdsec firewall bouncer config preparation";
|
||||
script = ''
|
||||
if [ ! -e '${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml' ]; then
|
||||
${crowdsecCfg.package}/bin/cscli -oraw bouncers add "cs-firewall-bouncer-$(${pkgs.coreutils}/bin/date +%s)" > \
|
||||
${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml
|
||||
fi
|
||||
|
||||
# Stdout redirection is deliberately used to forcibly
|
||||
# overwrite the file if it exists
|
||||
API_KEY="$(<${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml)" \
|
||||
${lib.getExe pkgs.envsubst} \
|
||||
-i ${settingsFormat.generate "crowdsec-firewall-bouncer.yaml" cfg.settings} \
|
||||
> /var/lib/crowdsec/config/crowdsec-firewall-bouncer.yaml
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
User = "crowdsec";
|
||||
Group = "crowdsec";
|
||||
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
|
||||
crowdsec-firewall-bouncer = {
|
||||
enable = true;
|
||||
|
||||
after = [ "crowdsec-firewall-bouncer-setup.service" ];
|
||||
bindsTo = [ "crowdsec-firewall-bouncer-setup.service" ];
|
||||
requiredBy = [ "crowdsec.service" ];
|
||||
|
||||
path =
|
||||
lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [ pkgs.ipset ]
|
||||
++ lib.optional (cfg.settings.mode == "iptables") pkgs.iptables
|
||||
++ lib.optional (cfg.settings.mode == "nftables") pkgs.nftables;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{ imports = [ ./cs-firewall-bouncer.nix ]; }
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
imports = [
|
||||
./crowdsec
|
||||
./nginxExtensions.nix
|
||||
];
|
||||
}
|
||||
|
|
|
@ -3,57 +3,156 @@
|
|||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
}: {
|
||||
options = {
|
||||
services.nginx.domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The base domain name to append to virtual domain names";
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts =
|
||||
let
|
||||
extraVirtualHostOptions =
|
||||
{ name, config, ... }:
|
||||
{
|
||||
options = {
|
||||
enableHSTS = lib.mkEnableOption "Enable HSTS";
|
||||
services.nginx.virtualHosts = let
|
||||
autheliaDomain = "auth.${config.services.nginx.domain}";
|
||||
extraLocationOptions = {config, ...}: {
|
||||
options = {
|
||||
enableAutheliaProxy = lib.mkEnableOption "Enable recommended authelia proxy settings";
|
||||
enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
|
||||
};
|
||||
|
||||
addAccessLog = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Add special logging to `/var/log/nginx/''${serverName}`
|
||||
'';
|
||||
};
|
||||
};
|
||||
config = {
|
||||
recommendedProxySettings = lib.mkIf config.enableAutheliaProxy false;
|
||||
|
||||
config = {
|
||||
extraConfig = lib.concatStringsSep "\n" [
|
||||
(lib.optionalString config.enableHSTS ''
|
||||
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
|
||||
'')
|
||||
(lib.optionalString config.addAccessLog ''
|
||||
access_log /var/log/nginx/${name}/access.log upstream_time;
|
||||
'')
|
||||
];
|
||||
extraConfig = lib.concatStringsSep "\n" [
|
||||
(lib.optionalString config.enableAutheliaProxy ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-URI $request_uri;
|
||||
proxy_set_header X-Forwarded-Ssl on;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
client_body_buffer_size 128k;
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_http_version 1.1;
|
||||
proxy_cache_bypass $cookie_session;
|
||||
proxy_no_cache $cookie_session;
|
||||
proxy_buffers 64 256k;
|
||||
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
send_timeout 5m;
|
||||
proxy_read_timeout 360;
|
||||
proxy_send_timeout 360;
|
||||
proxy_connect_timeout 360;
|
||||
'')
|
||||
(lib.optionalString config.enableAuthorization ''
|
||||
auth_request /authelia;
|
||||
|
||||
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
auth_request_set $user $upstream_http_remote_user;
|
||||
auth_request_set $groups $upstream_http_remote_groups;
|
||||
auth_request_set $name $upstream_http_remote_name;
|
||||
auth_request_set $email $upstream_http_remote_email;
|
||||
|
||||
proxy_set_header Remote-User $user;
|
||||
proxy_set_header Remote-Groups $groups;
|
||||
proxy_set_header Remote-Email $email;
|
||||
proxy_set_header Remote-Name $name;
|
||||
|
||||
error_page 401 =302 https://${autheliaDomain}/?rd=$target_url;
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
extraVirtualHostOptions = {
|
||||
name,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options = {
|
||||
enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
|
||||
enableHSTS = lib.mkEnableOption "Enable HSTS";
|
||||
|
||||
addAccessLog = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Add special logging to `/var/log/nginx/''${serverName}`
|
||||
'';
|
||||
};
|
||||
|
||||
locations = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule extraLocationOptions);
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
extraConfig = lib.concatStringsSep "\n" [
|
||||
(lib.optionalString config.enableHSTS ''
|
||||
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
|
||||
'')
|
||||
(lib.optionalString config.addAccessLog ''
|
||||
access_log /var/log/nginx/${name}/access.log upstream_time;
|
||||
'')
|
||||
];
|
||||
|
||||
locations = lib.mkIf config.enableAuthorization {
|
||||
"/".enableAuthorization = true;
|
||||
"/authelia" = {
|
||||
proxyPass = "http://127.0.0.1:9091/api/verify";
|
||||
recommendedProxySettings = false;
|
||||
extraConfig = ''
|
||||
internal;
|
||||
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
proxy_set_header X-Forwarded-Method $request_method;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Uri $request_uri;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header Connection "";
|
||||
|
||||
proxy_pass_request_body off;
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_http_version 1.1;
|
||||
proxy_cache_bypass $cookie_session;
|
||||
proxy_no_cache $cookie_session;
|
||||
proxy_buffers 4 32k;
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
send_timeout 5m;
|
||||
proxy_read_timeout 240;
|
||||
proxy_send_timeout 240;
|
||||
proxy_connect_timeout 240;
|
||||
'';
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.mkOption { type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions); };
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions);
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# Don't attempt to run acme if the domain name is not tlater.net
|
||||
systemd.services =
|
||||
let
|
||||
confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]'';
|
||||
in
|
||||
lib.mapAttrs' (
|
||||
cert: _:
|
||||
systemd.services = let
|
||||
confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]'';
|
||||
in
|
||||
lib.mapAttrs' (cert: _:
|
||||
lib.nameValuePair "acme-${cert}" {
|
||||
serviceConfig.ExecCondition = ''${pkgs.runtimeShell} -c '${confirm}' '';
|
||||
}
|
||||
) config.security.acme.certs;
|
||||
})
|
||||
config.security.acme.certs;
|
||||
};
|
||||
}
|
||||
|
|
86
pkgs/_sources_nextcloud/generated.json
Normal file
86
pkgs/_sources_nextcloud/generated.json
Normal file
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"bookmarks": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "bookmarks",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"sha256": "sha256-JXNQNnWXoii71QhtKktuEBEIqzmONVetULBhpSjM9xo=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/nextcloud/bookmarks/releases/download/v13.1.3/bookmarks-13.1.3.tar.gz"
|
||||
},
|
||||
"version": "13.1.3"
|
||||
},
|
||||
"calendar": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "calendar",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"sha256": "sha256-hZfjWAMi/0qs5xMMgOlcoSXG6kcZ2aeDaez+NqSZFKI=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/nextcloud-releases/calendar/releases/download/v4.6.7/calendar-v4.6.7.tar.gz"
|
||||
},
|
||||
"version": "v4.6.7"
|
||||
},
|
||||
"contacts": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "contacts",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"sha256": "sha256-HCEjiAqn6sTNXKW6O5X6Ta9Ll4ehvzmGZUj1c0ue2Xc=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/nextcloud-releases/contacts/releases/download/v5.5.3/contacts-v5.5.3.tar.gz"
|
||||
},
|
||||
"version": "v5.5.3"
|
||||
},
|
||||
"cookbook": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "cookbook",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"sha256": "sha256-TE/w8SgyIPaGl5wZUAsG234nxoPj25QoRPF3zjbMoRk=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.10.5/Cookbook-0.10.5.tar.gz"
|
||||
},
|
||||
"version": "0.10.5"
|
||||
},
|
||||
"news": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "news",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"sha256": "sha256-cfJkKRNSz15L4E3w1tnEb+t4MrVwVzb8lb6vCOA4cK4=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/nextcloud/news/releases/download/24.0.0/news.tar.gz"
|
||||
},
|
||||
"version": "24.0.0"
|
||||
},
|
||||
"notes": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "notes",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"sha256": "sha256-ydpxatwuZUz7XIgK8FMklZlxNQklpsP8Uqpxvt3iK0k=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/nextcloud/notes/releases/download/v4.10.0/notes.tar.gz"
|
||||
},
|
||||
"version": "v4.10.0"
|
||||
}
|
||||
}
|
52
pkgs/_sources_nextcloud/generated.nix
Normal file
52
pkgs/_sources_nextcloud/generated.nix
Normal file
|
@ -0,0 +1,52 @@
|
|||
# This file was generated by nvfetcher, please do not modify it manually.
|
||||
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
|
||||
{
|
||||
bookmarks = {
|
||||
pname = "bookmarks";
|
||||
version = "13.1.3";
|
||||
src = fetchTarball {
|
||||
url = "https://github.com/nextcloud/bookmarks/releases/download/v13.1.3/bookmarks-13.1.3.tar.gz";
|
||||
sha256 = "sha256-JXNQNnWXoii71QhtKktuEBEIqzmONVetULBhpSjM9xo=";
|
||||
};
|
||||
};
|
||||
calendar = {
|
||||
pname = "calendar";
|
||||
version = "v4.6.7";
|
||||
src = fetchTarball {
|
||||
url = "https://github.com/nextcloud-releases/calendar/releases/download/v4.6.7/calendar-v4.6.7.tar.gz";
|
||||
sha256 = "sha256-hZfjWAMi/0qs5xMMgOlcoSXG6kcZ2aeDaez+NqSZFKI=";
|
||||
};
|
||||
};
|
||||
contacts = {
|
||||
pname = "contacts";
|
||||
version = "v5.5.3";
|
||||
src = fetchTarball {
|
||||
url = "https://github.com/nextcloud-releases/contacts/releases/download/v5.5.3/contacts-v5.5.3.tar.gz";
|
||||
sha256 = "sha256-HCEjiAqn6sTNXKW6O5X6Ta9Ll4ehvzmGZUj1c0ue2Xc=";
|
||||
};
|
||||
};
|
||||
cookbook = {
|
||||
pname = "cookbook";
|
||||
version = "0.10.5";
|
||||
src = fetchTarball {
|
||||
url = "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.10.5/Cookbook-0.10.5.tar.gz";
|
||||
sha256 = "sha256-TE/w8SgyIPaGl5wZUAsG234nxoPj25QoRPF3zjbMoRk=";
|
||||
};
|
||||
};
|
||||
news = {
|
||||
pname = "news";
|
||||
version = "24.0.0";
|
||||
src = fetchTarball {
|
||||
url = "https://github.com/nextcloud/news/releases/download/24.0.0/news.tar.gz";
|
||||
sha256 = "sha256-cfJkKRNSz15L4E3w1tnEb+t4MrVwVzb8lb6vCOA4cK4=";
|
||||
};
|
||||
};
|
||||
notes = {
|
||||
pname = "notes";
|
||||
version = "v4.10.0";
|
||||
src = fetchTarball {
|
||||
url = "https://github.com/nextcloud/notes/releases/download/v4.10.0/notes.tar.gz";
|
||||
sha256 = "sha256-ydpxatwuZUz7XIgK8FMklZlxNQklpsP8Uqpxvt3iK0k=";
|
||||
};
|
||||
};
|
||||
}
|
21
pkgs/_sources_pkgs/generated.json
Normal file
21
pkgs/_sources_pkgs/generated.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"prometheus-fail2ban-exporter": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "prometheus-fail2ban-exporter",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"deepClone": false,
|
||||
"fetchSubmodules": false,
|
||||
"leaveDotGit": false,
|
||||
"name": null,
|
||||
"rev": "v0.10.1",
|
||||
"sha256": "sha256-zGEhDy3uXIbvx4agSA8Mx7bRtiZZtoDZGbNbHc9L+yI=",
|
||||
"type": "git",
|
||||
"url": "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter"
|
||||
},
|
||||
"version": "v0.10.1"
|
||||
}
|
||||
}
|
16
pkgs/_sources_pkgs/generated.nix
Normal file
16
pkgs/_sources_pkgs/generated.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
# This file was generated by nvfetcher, please do not modify it manually.
|
||||
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
|
||||
{
|
||||
prometheus-fail2ban-exporter = {
|
||||
pname = "prometheus-fail2ban-exporter";
|
||||
version = "v0.10.1";
|
||||
src = fetchgit {
|
||||
url = "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter";
|
||||
rev = "v0.10.1";
|
||||
fetchSubmodules = false;
|
||||
deepClone = false;
|
||||
leaveDotGit = false;
|
||||
sha256 = "sha256-zGEhDy3uXIbvx4agSA8Mx7bRtiZZtoDZGbNbHc9L+yI=";
|
||||
};
|
||||
};
|
||||
}
|
1430
pkgs/afvalcalendar/Cargo.lock
generated
Normal file
1430
pkgs/afvalcalendar/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
16
pkgs/afvalcalendar/Cargo.toml
Normal file
16
pkgs/afvalcalendar/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "afvalcalendar"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
icalendar = "0.16.0"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] }
|
||||
reqwest = { version = "0.11", features = ["cookies", "json"] }
|
||||
chrono = { version = "0.4.34", features = ["serde"] }
|
||||
serde_json = "1.0.114"
|
||||
serde_repr = "0.1.18"
|
||||
hostname = "0.3.1"
|
20
pkgs/afvalcalendar/default.nix
Normal file
20
pkgs/afvalcalendar/default.nix
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
pkgs,
|
||||
rustPlatform,
|
||||
...
|
||||
}:
|
||||
rustPlatform.buildRustPackage {
|
||||
pname = "afvalcalendar";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
openssl
|
||||
];
|
||||
|
||||
cargoHash = "sha256-JXx6aUKdKbUTBCwlBw5i1hZy8ofCfSrhLCwFzqdA8cI=";
|
||||
}
|
43
pkgs/afvalcalendar/src/calendar.rs
Normal file
43
pkgs/afvalcalendar/src/calendar.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use chrono::{Duration, NaiveDate};
|
||||
use icalendar::{Alarm, Calendar, Component, Event, EventLike, Property};
|
||||
|
||||
use crate::trash::TrashType;
|
||||
|
||||
pub(crate) fn calendar_from_pickup_dates(dates: Vec<(TrashType, NaiveDate)>) -> Calendar {
|
||||
let mut ical = Calendar::new();
|
||||
ical.name("Twente Milieu Afvalkalender");
|
||||
|
||||
let events = dates.iter().map(|date| {
|
||||
let description = match date.0 {
|
||||
TrashType::Grey => "Restafval wordt opgehaald",
|
||||
TrashType::Green => "GFT wordt opgehaald",
|
||||
TrashType::Paper => "Papier wordt opgehaald",
|
||||
TrashType::Packages => "Verpakkingen worden opgehaald",
|
||||
};
|
||||
|
||||
let color = Property::new(
|
||||
"COLOR",
|
||||
match date.0 {
|
||||
TrashType::Grey => "darkgray",
|
||||
TrashType::Green => "darkgreen",
|
||||
TrashType::Paper => "royalblue",
|
||||
TrashType::Packages => "darkorange",
|
||||
},
|
||||
);
|
||||
|
||||
let reminder = Alarm::display(description, -Duration::hours(5));
|
||||
|
||||
Event::new()
|
||||
.all_day(date.1)
|
||||
.summary(description)
|
||||
.append_property(color)
|
||||
.alarm(reminder)
|
||||
.done()
|
||||
});
|
||||
|
||||
for event in events {
|
||||
ical.push(event);
|
||||
}
|
||||
|
||||
ical.done()
|
||||
}
|
15
pkgs/afvalcalendar/src/main.rs
Normal file
15
pkgs/afvalcalendar/src/main.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
mod calendar;
|
||||
mod trash;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
match trash::get_pickup_dates().await {
|
||||
Ok(dates) => {
|
||||
let calendar = calendar::calendar_from_pickup_dates(dates);
|
||||
calendar.print().unwrap();
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("{}", error);
|
||||
}
|
||||
}
|
||||
}
|
59
pkgs/afvalcalendar/src/trash.rs
Normal file
59
pkgs/afvalcalendar/src/trash.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use chrono::{Months, NaiveDate, NaiveDateTime, Utc};
|
||||
use serde::Deserialize;
|
||||
use serde_repr::Deserialize_repr;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CalendarAPIDatum {
|
||||
pickup_dates: Vec<NaiveDateTime>,
|
||||
pickup_type: TrashType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CalendarAPIResponse {
|
||||
data_list: Vec<CalendarAPIDatum>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Deserialize_repr, Debug)]
|
||||
#[repr(u8)]
|
||||
pub(crate) enum TrashType {
|
||||
Grey = 0,
|
||||
Green = 1,
|
||||
Paper = 2,
|
||||
Packages = 10,
|
||||
}
|
||||
|
||||
pub(crate) async fn get_pickup_dates() -> Result<Vec<(TrashType, NaiveDate)>, reqwest::Error> {
|
||||
let today = Utc::now().date_naive();
|
||||
let next_month = (today + Months::new(1)).to_string();
|
||||
let today = today.to_string();
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let params = [
|
||||
("companyCode", "8d97bb56-5afd-4cbc-a651-b4f7314264b4"),
|
||||
("uniqueAddressID", "1300002485"),
|
||||
("startDate", &today),
|
||||
("endDate", &next_month),
|
||||
];
|
||||
|
||||
let calendar = client
|
||||
.post("https://twentemilieuapi.ximmio.com/api/GetCalendar")
|
||||
.form(¶ms)
|
||||
.send()
|
||||
.await?
|
||||
.json::<CalendarAPIResponse>()
|
||||
.await?;
|
||||
|
||||
Ok(calendar
|
||||
.data_list
|
||||
.iter()
|
||||
.flat_map(|datum| {
|
||||
datum
|
||||
.pickup_dates
|
||||
.iter()
|
||||
.map(|date| (datum.pickup_type, NaiveDate::from(*date)))
|
||||
})
|
||||
.collect::<Vec<(TrashType, NaiveDate)>>())
|
||||
}
|
4
pkgs/afvalcalendar/test.rest
Normal file
4
pkgs/afvalcalendar/test.rest
Normal file
|
@ -0,0 +1,4 @@
|
|||
POST https://twentemilieuapi.ximmio.com/api/GetCalendar
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
companyCode=8d97bb56-5afd-4cbc-a651-b4f7314264b4&uniqueAddressID=1300002485&startDate=2024-02-01&endDate=2024-02-29
|
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"crowdsec-firewall-bouncer": {
|
||||
"cargoLocks": null,
|
||||
"date": null,
|
||||
"extract": null,
|
||||
"name": "crowdsec-firewall-bouncer",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"deepClone": false,
|
||||
"fetchSubmodules": false,
|
||||
"leaveDotGit": false,
|
||||
"name": null,
|
||||
"owner": "crowdsecurity",
|
||||
"repo": "cs-firewall-bouncer",
|
||||
"rev": "v0.0.31",
|
||||
"sha256": "sha256-59MWll8v00CF4WA53gjHZSTFc8hpYaHENg9O7LgTCrA=",
|
||||
"type": "github"
|
||||
},
|
||||
"version": "v0.0.31"
|
||||
},
|
||||
"crowdsec-hub": {
|
||||
"cargoLocks": null,
|
||||
"date": "2025-05-17",
|
||||
"extract": null,
|
||||
"name": "crowdsec-hub",
|
||||
"passthru": null,
|
||||
"pinned": false,
|
||||
"src": {
|
||||
"deepClone": false,
|
||||
"fetchSubmodules": false,
|
||||
"leaveDotGit": false,
|
||||
"name": null,
|
||||
"owner": "crowdsecurity",
|
||||
"repo": "hub",
|
||||
"rev": "850614b9fcd4298f559b422c5ac685a69aa2e5ff",
|
||||
"sha256": "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=",
|
||||
"type": "github"
|
||||
},
|
||||
"version": "850614b9fcd4298f559b422c5ac685a69aa2e5ff"
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
# This file was generated by nvfetcher, please do not modify it manually.
|
||||
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
|
||||
{
|
||||
crowdsec-firewall-bouncer = {
|
||||
pname = "crowdsec-firewall-bouncer";
|
||||
version = "v0.0.31";
|
||||
src = fetchFromGitHub {
|
||||
owner = "crowdsecurity";
|
||||
repo = "cs-firewall-bouncer";
|
||||
rev = "v0.0.31";
|
||||
fetchSubmodules = false;
|
||||
sha256 = "sha256-59MWll8v00CF4WA53gjHZSTFc8hpYaHENg9O7LgTCrA=";
|
||||
};
|
||||
};
|
||||
crowdsec-hub = {
|
||||
pname = "crowdsec-hub";
|
||||
version = "850614b9fcd4298f559b422c5ac685a69aa2e5ff";
|
||||
src = fetchFromGitHub {
|
||||
owner = "crowdsecurity";
|
||||
repo = "hub";
|
||||
rev = "850614b9fcd4298f559b422c5ac685a69aa2e5ff";
|
||||
fetchSubmodules = false;
|
||||
sha256 = "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=";
|
||||
};
|
||||
date = "2025-05-17";
|
||||
};
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{ pkgs }:
|
||||
let
|
||||
sources = pkgs.callPackage ./_sources/generated.nix { };
|
||||
callPackage = pkgs.lib.callPackageWith (pkgs // { inherit sources; });
|
||||
in
|
||||
{
|
||||
hub = callPackage ./hub.nix { };
|
||||
firewall-bouncer = callPackage ./firewall-bouncer.nix { };
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
lib,
|
||||
sources,
|
||||
buildGoModule,
|
||||
envsubst,
|
||||
coreutils,
|
||||
}:
|
||||
let
|
||||
envsubstBin = lib.getExe envsubst;
|
||||
in
|
||||
buildGoModule {
|
||||
inherit (sources.crowdsec-firewall-bouncer) pname version src;
|
||||
|
||||
vendorHash = "sha256-7Jxvg8UEjUxnIz1llvXyI2AefJ31OVdNzhWD/C8wU/Y=";
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $out/lib/systemd/system
|
||||
|
||||
CFG=/var/lib/crowdsec/config BIN=$out/bin/cs-firewall-bouncer ${envsubstBin} \
|
||||
-i ./config/crowdsec-firewall-bouncer.service \
|
||||
-o $out/lib/systemd/system/crowdsec-firewall-bouncer.service
|
||||
|
||||
substituteInPlace $out/lib/systemd/system/crowdsec-firewall-bouncer.service \
|
||||
--replace-fail /bin/sleep ${coreutils}/bin/sleep
|
||||
'';
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{ sources }: sources.crowdsec-hub.src
|
|
@ -1,7 +0,0 @@
|
|||
[crowdsec-hub]
|
||||
src.git = "https://github.com/crowdsecurity/hub.git"
|
||||
fetch.github = "crowdsecurity/hub"
|
||||
|
||||
[crowdsec-firewall-bouncer]
|
||||
src.github = "crowdsecurity/cs-firewall-bouncer"
|
||||
fetch.github = "crowdsecurity/cs-firewall-bouncer"
|
|
@ -1,5 +1,22 @@
|
|||
{ pkgs }:
|
||||
{
|
||||
crowdsec = import ./crowdsec { inherit pkgs; };
|
||||
starbound = pkgs.callPackage ./starbound { };
|
||||
}
|
||||
pkgs,
|
||||
lib,
|
||||
}: let
|
||||
inherit (builtins) fromJSON mapAttrs readFile;
|
||||
inherit (pkgs) callPackage;
|
||||
in
|
||||
{
|
||||
starbound = callPackage ./starbound {};
|
||||
prometheus-fail2ban-exporter = callPackage ./prometheus/fail2ban-exporter.nix {
|
||||
sources = pkgs.callPackage ./_sources_pkgs/generated.nix {};
|
||||
};
|
||||
afvalcalendar = callPackage ./afvalcalendar {};
|
||||
}
|
||||
// (
|
||||
# Add nextcloud apps
|
||||
let
|
||||
mkNextcloudApp = pkgs.callPackage ./mkNextcloudApp.nix {};
|
||||
sources = fromJSON (readFile ./_sources_nextcloud/generated.json);
|
||||
in
|
||||
mapAttrs (_: source: mkNextcloudApp source) sources
|
||||
)
|
||||
|
|
9
pkgs/mkNextcloudApp.nix
Normal file
9
pkgs/mkNextcloudApp.nix
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
fetchNextcloudApp,
|
||||
lib,
|
||||
}: source:
|
||||
fetchNextcloudApp {
|
||||
url = source.src.url;
|
||||
sha256 = source.src.sha256;
|
||||
license = "unlicense"; # Blatant lie
|
||||
}
|
31
pkgs/nextcloud-apps.toml
Normal file
31
pkgs/nextcloud-apps.toml
Normal file
|
@ -0,0 +1,31 @@
|
|||
[bookmarks]
|
||||
# src.github = "nextcloud/bookmarks"
|
||||
src.prefix = "v"
|
||||
src.manual = "v13.1.3"
|
||||
fetch.tarball = "https://github.com/nextcloud/bookmarks/releases/download/v$ver/bookmarks-$ver.tar.gz"
|
||||
|
||||
[calendar]
|
||||
# src.github = "nextcloud-releases/calendar"
|
||||
src.manual = "v4.6.7"
|
||||
fetch.tarball = "https://github.com/nextcloud-releases/calendar/releases/download/$ver/calendar-$ver.tar.gz"
|
||||
|
||||
[contacts]
|
||||
# src.github = "nextcloud-releases/contacts"
|
||||
src.manual = "v5.5.3"
|
||||
fetch.tarball = "https://github.com/nextcloud-releases/contacts/releases/download/$ver/contacts-$ver.tar.gz"
|
||||
|
||||
[cookbook]
|
||||
# src.github = "christianlupus-nextcloud/cookbook-releases"
|
||||
src.prefix = "v"
|
||||
src.manual = "0.10.5"
|
||||
fetch.tarball = "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v$ver/Cookbook-$ver.tar.gz"
|
||||
|
||||
[news]
|
||||
# src.github = "nextcloud/news"
|
||||
# Update to 25 when angular rewrite is done/the alpha when I need to switch to nextcloud 28+
|
||||
src.manual = "24.0.0"
|
||||
fetch.tarball = "https://github.com/nextcloud/news/releases/download/$ver/news.tar.gz"
|
||||
|
||||
[notes]
|
||||
src.github = "nextcloud/notes"
|
||||
fetch.tarball = "https://github.com/nextcloud/notes/releases/download/$ver/notes.tar.gz"
|
3
pkgs/nvfetcher.toml
Normal file
3
pkgs/nvfetcher.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[prometheus-fail2ban-exporter]
|
||||
src.manual = "v0.10.1" # No gitlab support in nvfetcher
|
||||
fetch.git = "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter"
|
8
pkgs/prometheus/fail2ban-exporter.nix
Normal file
8
pkgs/prometheus/fail2ban-exporter.nix
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
buildGoModule,
|
||||
sources,
|
||||
}:
|
||||
buildGoModule {
|
||||
inherit (sources.prometheus-fail2ban-exporter) pname src version;
|
||||
vendorHash = "sha256-5o8p5p0U/c0WAIV5dACnWA3ThzSh2tt5LIFMb59i9GY=";
|
||||
}
|
|
@ -5,33 +5,30 @@
|
|||
patchelf,
|
||||
steamPackages,
|
||||
replace-secret,
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
# Use the directory in which starbound is installed so steamcmd
|
||||
# doesn't have to be reinstalled constantly (we're using DynamicUser
|
||||
# with StateDirectory to persist this).
|
||||
steamcmd = steamPackages.steamcmd.override { steamRoot = "/var/lib/starbound/.steamcmd"; };
|
||||
wrapperPath = lib.makeBinPath [
|
||||
patchelf
|
||||
steamcmd
|
||||
replace-secret
|
||||
];
|
||||
steamcmd = steamPackages.steamcmd.override {
|
||||
steamRoot = "/var/lib/starbound/.steamcmd";
|
||||
};
|
||||
wrapperPath = lib.makeBinPath [patchelf steamcmd replace-secret];
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "starbound-update-script";
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
dontUnpack = true;
|
||||
patchPhase = ''
|
||||
interpreter="$(cat $NIX_CC/nix-support/dynamic-linker)"
|
||||
substitute ${./launch-starbound.sh} launch-starbound --subst-var interpreter
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp launch-starbound $out/bin/launch-starbound
|
||||
chmod +x $out/bin/launch-starbound
|
||||
'';
|
||||
postFixup = ''
|
||||
wrapProgram $out/bin/launch-starbound \
|
||||
--prefix PATH : "${wrapperPath}"
|
||||
'';
|
||||
}
|
||||
stdenv.mkDerivation {
|
||||
name = "starbound-update-script";
|
||||
nativeBuildInputs = [makeWrapper];
|
||||
dontUnpack = true;
|
||||
patchPhase = ''
|
||||
interpreter="$(cat $NIX_CC/nix-support/dynamic-linker)"
|
||||
substitute ${./launch-starbound.sh} launch-starbound --subst-var interpreter
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp launch-starbound $out/bin/launch-starbound
|
||||
chmod +x $out/bin/launch-starbound
|
||||
'';
|
||||
postFixup = ''
|
||||
wrapProgram $out/bin/launch-starbound \
|
||||
--prefix PATH : "${wrapperPath}"
|
||||
'';
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue