update robi and started with orbi

This commit is contained in:
Ingolf Wagner 2023-12-09 17:15:50 +01:00
parent 3366e9e65e
commit 2a2edf13f9
Signed by: palo
GPG key ID: 76BF5F1928B9618B
46 changed files with 3525 additions and 8 deletions

View file

@ -84,7 +84,7 @@ in
#fractal
#legacy_2205.mirage-im
#cinny-desktop
#fluffychat
fluffychat
#(fluffychat.overrideAttrs
# (old: rec {
# version = "1.13.0";

View file

@ -0,0 +1,18 @@
{ lib, config, pkgs, ... }: {
backup.enable = true;
# provide repository
services.borgbackup.repos = {
default = {
quota = "300G";
allowSubRepos = true;
authorizedKeys = [
(lib.fileContents ../../assets/ssh/borg_access.pub)
(lib.fileContents ../../assets/ssh/palo_rsa.pub)
];
};
};
}

View file

@ -0,0 +1,13 @@
{ ... }:
{
services.nix-serve = {
enable = true;
# needed if i want to trust my own build packages and dirivations
# nix-store --generate-binary-cache-key key-name secret-key-file public-key-file
# secretKeyFile = sops.nixServeSecretKeyFile.path
};
}

View file

@ -0,0 +1,34 @@
{ config, lib, pkgs, ... }:
let
domain = "md.ingolf-wagner.de";
in
{
services.nginx.enable = true;
services.nginx.virtualHosts.hedgedoc = {
enableACME = true;
forceSSL = true;
serverName = domain;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.hedgedoc.settings.port}";
proxyWebsockets = true;
};
};
services.hedgedoc = {
enable = true;
settings = {
db = {
dialect = "sqlite";
storage = "/var/lib/hedgedoc/db.sqlite";
useCDN = false;
};
allowFreeURL = true;
domain = domain;
port = 3091;
useCDN = false;
};
};
}

View file

@ -0,0 +1,112 @@
{ lib, config, pkgs, ... }: {
imports = [
../../system/all/nginx.nix
../../system/all/borg-jobs.nix
../../system/all/defaults.nix
../../system/all/syncthing.nix
../../system/server/netdata.nix
../../system/server/packages.nix
../../components
../../modules
./hetzner.nix
# services
./borg.nix
./codimd.nix
./gitea.nix
./nextcloud.nix
./packages.nix
./taskserver.nix
./vaultwarden.nix
./nginx.nix
./nginx-wkd.nix
./network-tinc.nix
./network-wireguard.nix
./media-share.nix
./media-jellyfin.nix
./media-syncthing.nix
./media-transmission.nix
./media-transmission2.nix
./media-arr.nix
#./media-tdarr.nix
#./media-unmanic.nix
./sync-opentracker.nix
./sync-torrent.nix
#./social-jitsi.nix
# matrix
./terranix-dendrite.nix
# logging
./loki.nix
./loki-promtail.nix
./prometheus.nix
./grafana.nix
./telegraf.nix
./cache.nix
];
components.terminal.enable = true;
components.mainUser.enable = true;
components.gui.enable = false;
components.network.enable = true;
components.network.wifi.enable = false;
# 2 hours = 2 * 60 * 60 = 7200 seconds
#services.netdata.config.global.history = 7200;
# 4 hours = 4 * 60 * 60 = 14440 seconds
services.netdata.config.global.history = 14440;
# 24 hours = 24 * 60 * 60 = 86400 seconds
#services.netdata.config.global.history = 86400;
services.sshguard.enable = true;
# Shell configuration
# -------------------
#programs.custom. zsh.enable = true;
users.users.root.shell = pkgs.zsh;
security.acme.acceptTerms = true;
security.acme.defaults.email = "contact@ingolf-wagner.de";
sops.defaultSopsFile = ../../secrets/robi.yaml;
# virtualisation.docker.enable = false;
services.printing.enable = false;
services.smartd.enable = true;
# chungus rsync
users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJkqVvuJSvRMO5pG2CHNNBxjB7HlJudK4TQs3BhbOWOD" ];
fileSystems."/var/lib/nextcloud" =
{
device = "/dev/vg/nextcloud";
fsType = "ext4";
};
fileSystems."/var/lib/borgbackup" =
{
device = "/dev/vg/borg";
fsType = "ext4";
};
fileSystems."/media" =
{
device = "/dev/vg/media";
fsType = "ext4";
};
# usually part of the wifi.nix module
# because Networkd-wait-online is just failing.
#systemd.services.systemd-networkd-wait-online.enable = false;
systemd.services.NetworkManager-wait-online.enable = false;
}

View file

@ -0,0 +1,150 @@
# nix run github:nix-community/disko -- --mode zap_create_mount ./disko-config.nix
# nixos-generate-config --no-filesystems --root /mnt
# vim /mnt/configuration.nix
# nixos-install
{ config, lib, ... }:
let
disks = [ "sda" "sdb" ];
in
{
# ZFS already has its own scheduler. Without this my(@Artturin) computer froze for a second when i nix build something.
# copied from : https://github.com/numtide/srvos/blob/main/nixos/common/zfs.nix
services.udev.extraRules = lib.optionalString (config.boot.zfs.enabled) ''
ACTION=="add|change", KERNEL=="sd[a-z]*[0-9]*|mmcblk[0-9]*p[0-9]*|nvme[0-9]*n[0-9]*p[0-9]*", ENV{ID_FS_TYPE}=="zfs_member", ATTR{../queue/scheduler}="none"
'';
disko.devices = {
disk =
lib.genAttrs disks (disk: {
name = disk;
type = "disk";
device = "/dev/${disk}";
content = {
type = "table";
format = "gpt";
partitions = [
{
name = "ESP";
start = "0";
end = "500MiB";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot_${disk}";
mountOptions = [ "defaults" ];
};
}
{
name = "zfs";
start = "500MiB";
size = "500GB";
content = {
type = "luks";
name = "root_${disk}";
settings = {
# if you want to use the key for interactive login be sure there is no trailing newline
# for example use `echo -n "password" > /tmp/secret.key`
keyFile = "/tmp/secret.key";
allowDiscards = true;
};
content = {
type = "zfs";
pool = "zroot";
};
};
}
{
name = "zfs";
size = "100%";
content = {
type = "luks";
settings = {
# if you want to use the key for interactive login be sure there is no trailing newline
# for example use `echo -n "password" > /tmp/secret.key`
keyFile = "/tmp/secret.key";
allowDiscards = true;
};
name = "media_${disk}";
content = {
type = "zfs";
pool = "zmedia";
};
};
}
];
};
}
);
zpool = {
zroot = {
type = "zpool";
mode = "mirror";
rootFsOptions = {
mountpoint = "none";
canmount = "off";
compression = "lz4";
};
datasets = {
"root" = {
type = "zfs_fs";
mountpoint = "/";
options = {
mountpoint = "legacy";
compression = "lz4";
};
};
"store" = {
type = "zfs_fs";
mountpoint = "/nix/store";
options = {
mountpoint = "legacy";
compression = "lz4";
};
};
};
};
# `zpool import -f zraid` once on the first boot and reboot
zmedia = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
canmount = "off";
};
datasets = {
"media" = {
type = "zfs_fs";
mountpoint = "/media";
options = {
mountpoint = "legacy";
compression = "lz4";
"com.sun:auto-snapshot:daily" = false;
"com.sun:auto-snapshot:weekly" = false;
"com.sun:auto-snapshot:monthly" = false;
};
};
# todo make sure this disk has some minimum space
"nextcloud" = {
type = "zfs_fs";
mountpoint = "/var/lib/nextcloud/";
options = {
mountpoint = "legacy";
compression = "lz4";
"com.sun:auto-snapshot:hourly" = true;
"com.sun:auto-snapshot:daily" = true;
"com.sun:auto-snapshot:weekly" = false;
"com.sun:auto-snapshot:monthly" = false;
};
};
};
};
};
};
}

View file

@ -0,0 +1,58 @@
{ lib, config, pkgs, ... }:
let
# find symbols with
# https://www.alphavantage.co/query?function=SYMBOL_SEARCH&apikey=<api_key>&keywords=<keywords>
# as described here : https://www.alphavantage.co/documentation/#symbolsearch
#
# example:
# --------
# stocks = [
# {
# friendly_name = "google";
# symbol = "GOOGL.DEX";
# name = "google";
# currency = "$";
# }
# ];
# results in
# P 2020-01-30 GOOGL $123
stocks = import ../../private_assets/finance/stocks;
stocksFile = toString /home/syncthing/finance/hledger/stocks.journal;
in
{
systemd.services.pull_stocks = {
enable = true;
description = "pull stocks for hledger";
serviceConfig = {
User = "syncthing";
Type = "oneshot";
};
script =
let
command = { symbol, name, currency, ... }: ''
APIKEY=${lib.fileContents ../../private_assets/finance/alphavantage/apiKey}
SYMBOL="${symbol}"
${pkgs.curl}/bin/curl --location --silent \
"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$SYMBOL&apikey=$APIKEY" \
| ${pkgs.jq}/bin/jq --raw-output '.["Global Quote"]
| "P \(.["07. latest trading day"]) ${name} ${currency}\(.["05. price"] | tonumber)"' \
>> ${stocksFile}
sleep 1
'';
in
lib.concatStringsSep "\n" (map command stocks);
};
systemd.timers.pull_stocks = {
enable = true;
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnCalendar = "weekly";
Persistent = "true";
};
};
}

View file

@ -0,0 +1,37 @@
{ config, lib, pkgs, ... }:
{
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"git.ingolf-wagner.de" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.gogs.httpPort}";
};
};
};
};
services.gitea = {
enable = true;
appName = "git.ingolf-wagner.de";
#cookieSecure = true;
#disableRegistration = true;
settings = {
server.ROOT_URL = "https://git.ingolf-wagner.de/";
server.DOMAIN = "git.ingolf-wagner.de";
service.DISABLE_REGISTRATION = true;
session.COOKIE_SECURE = true;
log.LEVEL = "Warn";
other = {
SHOW_FOOTER_VERSION = false;
};
};
};
backup.dirs = [ "/var/lib/gitea" ];
}

View file

@ -0,0 +1,24 @@
{ config, ... }:
{
services.nginx.virtualHosts.${config.services.grafana.settings.server.domain} = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
proxyWebsockets = true;
};
};
services.grafana = {
enable = true;
settings.server = {
domain = "grafana.robi.private";
http_port = 2342;
http_addr = "localhost";
};
};
}

View file

@ -0,0 +1,130 @@
{ config, lib, pkgs, ... }:
let port = 9000;
in {
# configure nginx
services.nginx = {
enable = true;
virtualHosts = {
"graylog.workhorse.private" = {
locations."/" = {
proxyPass = "http://localhost:${toString port}";
extraConfig = ''
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90;
proxy_redirect http://localhost:${
toString port
} https://graylog.workhorse.private/;
'';
};
};
};
};
services.mongodb.enable = true;
services.elasticsearch = {
enable = true;
listenAddress = "${config.networking.hostName}.private";
extraJavaOptions = [ "-Des.http.cname_in_publish_address=true" ];
};
services.graylog.enable = true;
services.graylog.elasticsearchHosts =
[ "http://${config.services.elasticsearch.listenAddress}:9200" ];
# https://docs.graylog.org/en/3.0/pages/configuration/server.conf.html
services.graylog.extraConfig = ''
http_bind_address = 0.0.0.0:${toString port}
http_publish_uri = http://workhorse.private:${toString port}/
'';
# other wise this does not work
services.graylog.nodeIdFile = "/var/lib/graylog/node-id";
# pwgen -N 1 -s 96
services.graylog.passwordSecret =
lib.fileContents ../../private_assets/graylog/password-secret;
# echo -n yourpassword | shasum -a 256
services.graylog.rootPasswordSha2 =
lib.fileContents ../../private_assets/graylog/root-password-hash;
services.graylog.plugins = [ pkgs.graylogPlugins.slack ];
# not working at the moment
#services.geoip-updater.enable = true;
# https://wiki.splunk.com/Http_status.csv
environment.etc."graylog/server/httpCodes.csv" = {
enable = true;
text = ''
status,status_description,status_type
100,Continue,Informational
101,Switching Protocols,Informational
200,OK,Successful
201,Created,Successful
202,Accepted,Successful
203,Non-Authoritative Information,Successful
204,No Content,Successful
205,Reset Content,Successful
206,Partial Content,Successful
300,Multiple Choices,Redirection
301,Moved Permanently,Redirection
302,Found,Redirection
303,See Other,Redirection
304,Not Modified,Redirection
305,Use Proxy,Redirection
307,Temporary Redirect,Redirection
400,Bad Request,Client Error
401,Unauthorized,Client Error
402,Payment Required,Client Error
403,Forbidden,Client Error
404,Not Found,Client Error
405,Method Not Allowed,Client Error
406,Not Acceptable,Client Error
407,Proxy Authentication Required,Client Error
408,Request Timeout,Client Error
409,Conflict,Client Error
410,Gone,Client Error
411,Length Required,Client Error
412,Precondition Failed,Client Error
413,Request Entity Too Large,Client Error
414,Request-URI Too Long,Client Error
415,Unsupported Media Type,Client Error
416,Requested Range Not Satisfiable,Client Error
417,Expectation Failed,Client Error
500,Internal Server Error,Server Error
501,Not Implemented,Server Error
502,Bad Gateway,Server Error
503,Service Unavailable,Server Error
504,Gateway Timeout,Server Error
505,HTTP Version Not Supported,Server Error
'';
};
environment.etc."graylog/server/known_servers.csv" = {
enable = true;
text = ''
"ip","host_name"
"95.216.1.150","lassul.us"
'';
};
environment.etc."graylog/systemd/loglevel.csv" = {
enable = true;
text = ''
"value","Servity","Description"
"0","emergency","System is unusable"
"1","alert","Should be corrected immediately"
"2","cirtical","Critical conditions"
"3","error","Error Condition"
"4","warning","May indicate that an error will occur if action is not taken."
"5","notice","Events that are unusual, but not error conditions."
"6","info","Normal operational messages that require no action."
"7","debug","Information useful to developers for debugging the application."
'';
};
}

View file

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
{
services.grocy = {
enable = true;
settings = {
culture = "de";
currency = "EUR";
};
hostName = "grocy.ingolf-wagner.de";
nginx.enableSSL = true;
};
backup.dirs = [ config.services.grocy.dataDir ];
}

View file

@ -0,0 +1,38 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "ahci" "sd_mod" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/d6a794d2-1da4-4457-9a9a-a39bf9521ae4";
fsType = "ext4";
};
fileSystems."/boot-1" =
{
device = "/dev/disk/by-uuid/519D-F289";
fsType = "vfat";
};
fileSystems."/boot-2" =
{
device = "/dev/disk/by-uuid/519E-6EF1";
fsType = "vfat";
};
swapDevices = [ ];
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
}

View file

@ -0,0 +1,123 @@
{ config, pkgs, modulesPath, lib, ... }:
let
hostName = "robi";
# apt install -y lshw
# lshw -C network | grep -Poh 'driver=[[:alnum:]]+'
networkInterfaceModule = "r8169";
networkInterface = "enp3s0";
# From the Hetzner control panel
ipv4 = {
address = "144.76.13.147"; # the ip address
gateway = "144.76.13.129"; # the gateway ip address
netmask = "255.255.255.224"; # the netmask -- might not be the same for you!
prefixLength = 27; # must match the netmask, see <https://www.pawprint.net/designresources/netmask-converter.php>
};
ipv6 = {
address = "2a01:4f8:190:9147::1"; # the ipv6 addres
gateway = "fe80::1"; # the ipv6 gateway
prefixLength = 64; # shown in the control panel
};
in
{
imports =
[
# Include the results of the hardware scan.
./hardware-configuration.nix
];
# needed lvm for raid
boot.initrd.kernelModules = [
"dm-snapshot"
"dm_mirror"
"dm_raid"
"dm_region_hash"
];
# Use GRUB2 as the boot loader.
# We don't use systemd-boot because Hetzner uses BIOS legacy boot.
boot.loader.systemd-boot.enable = false;
boot.loader.grub = {
enable = true;
efiSupport = false;
};
# This will mirror all UEFI files, kernels, grub menus and
# things needed to boot to the other drive.
boot.loader.grub.mirroredBoots = [
{ path = "/boot-1"; devices = [ "/dev/sda" ]; }
{ path = "/boot-2"; devices = [ "/dev/sdb" ]; }
];
# We want to still be able to boot without one of these
fileSystems."/boot-1".options = [ "nofail" ];
fileSystems."/boot-2".options = [ "nofail" ];
boot.initrd.luks.reusePassphrases = true;
boot.initrd.luks.devices = {
a_encrypted = {
device = "/dev/sda3";
preLVM = true;
};
b_encrypted = {
device = "/dev/sdb3";
preLVM = true;
};
};
networking.hostName = hostName;
# Network configuration (Hetzner uses static IP assignments, and we don't use DHCP here)
networking.useDHCP = false;
networking.interfaces.${networkInterface} = {
ipv4 = { addresses = [{ address = ipv4.address; prefixLength = ipv4.prefixLength; }]; };
ipv6 = { addresses = [{ address = ipv6.address; prefixLength = ipv6.prefixLength; }]; };
};
networking.defaultGateway = ipv4.gateway;
networking.defaultGateway6 = { address = ipv6.gateway; interface = networkInterface; };
networking.nameservers = [ "8.8.8.8" ];
# Initial empty root password for easy login:
users.users.root.initialHashedPassword = "";
services.openssh.settings.PermitRootLogin = "prohibit-password";
services.openssh.settings.PasswordAuthentication = false;
environment.systemPackages = [ pkgs.mosh ];
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6uza62+Go9sBFs3XZE2OkugBv9PJ7Yv8ebCskE5WYPcahMZIKkQw+zkGI8EGzOPJhQEv2xk+XBf2VOzj0Fto4nh8X5+Llb1nM+YxQPk1SVlwbNAlhh24L1w2vKtBtMy277MF4EP+caGceYP6gki5+DzlPUSdFSAEFFWgN1WPkiyUii15Xi3QuCMR8F18dbwVUYbT11vwNhdiAXWphrQG+yPguALBGR+21JM6fffOln3BhoDUp2poVc5Qe2EBuUbRUV3/fOU4HwWVKZ7KCFvLZBSVFutXCj5HuNWJ5T3RuuxJSmY5lYuFZx9gD+n+DAEJt30iXWcaJlmUqQB5awcB1S2d9pJ141V4vjiCMKUJHIdspFrI23rFNYD9k2ZXDA8VOnQE33BzmgF9xOVh6qr4G0oEpsNqJoKybVTUeSyl4+ifzdQANouvySgLJV/pcqaxX1srSDIUlcM2vDMWAs3ryCa0aAlmAVZIHgRhh6wa+IXW8gIYt+5biPWUuihJ4zGBEwkyVXXf2xsecMWCAGPWPDL0/fBfY9krNfC5M2sqxey2ShFIq+R/wMdaI7yVjUCF2QIUNiIdFbJL6bDrDyHnEXJJN+rAo23jUoTZZRv7Jq3DB/A5H7a73VCcblZyUmwMSlpg3wos7pdw5Ctta3zQPoxoAKGS1uZ+yTeZbPMmdbw=="
];
services.openssh.enable = true;
system.stateVersion = "21.05";
# enable ssh on init
# ------------------
boot.kernelParams = [
# See <https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt> for docs on this
# ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>
# The server ip refers to the NFS server -- we don't need it.
"ip=${ipv4.address}::${ipv4.gateway}:${ipv4.netmask}:${hostName}-initrd:${networkInterface}:off:8.8.8.8"
];
boot.initrd.availableKernelModules = [ networkInterfaceModule ];
boot.initrd.network.enable = true;
boot.initrd.network.ssh = {
enable = true;
authorizedKeys = config.users.users.root.openssh.authorizedKeys.keys;
port = 2222;
hostKeys = [
/etc/secrets/initrd/ssh_host_rsa_key
/etc/secrets/initrd/ssh_host_ed25519_key
];
};
}

View file

@ -0,0 +1,25 @@
{ config, ... }: {
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"kibana.${config.networking.hostName}.private" = {
serverAliases = [ ];
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.kibana.port
}";
};
};
};
};
services.elasticsearch.enable = true;
services.elasticsearch.listenAddress = "workhorse.private";
services.kibana.enable = true;
services.kibana.elasticsearch.hosts = [ "http://workhorse.private:9200" ];
services.kibana.listenAddress = "workhorse.private";
services.kibana.port = 5601;
}

View file

@ -0,0 +1,41 @@
{ config, ... }:
{
services.promtail = {
enable = true;
configuration = {
server = {
http_listen_port = 28183;
grpc_listen_port = 0;
};
positions.filename = "/tmp/positions.yaml";
clients = [
{ url = "http://127.0.0.1:3100/loki/api/v1/push"; }
];
scrape_configs = [
{
job_name = "journal";
journal = {
max_age = "12h";
labels = {
job = "systemd-journal";
host = config.networking.hostName;
};
};
relabel_configs = [
{
source_labels = [ "__journal__systemd_unit" ];
target_label = "unit";
}
{
source_labels = [ "__journal__transport" ];
target_label = "transport";
}
];
}
];
};
};
}

View file

@ -0,0 +1,99 @@
{ config, pkgs, ... }:
{
services.loki = {
enable = true;
configuration = {
server = {
http_listen_port = 3100;
log_level = "warn";
};
auth_enabled = false;
ingester = {
lifecycler = {
address = "127.0.0.1";
ring = {
kvstore = {
store = "inmemory";
};
replication_factor = 1;
};
};
chunk_idle_period = "1h";
max_chunk_age = "1h";
chunk_target_size = 999999;
chunk_retain_period = "30s";
max_transfer_retries = 0;
};
schema_config = {
configs = [{
from = "2022-06-06";
store = "boltdb-shipper";
object_store = "filesystem";
schema = "v11";
index = {
prefix = "index_";
period = "24h";
};
}];
};
storage_config = {
boltdb_shipper = {
active_index_directory = "/var/lib/loki/boltdb-shipper-active";
cache_location = "/var/lib/loki/boltdb-shipper-cache";
cache_ttl = "24h";
shared_store = "filesystem";
};
filesystem = {
directory = "/var/lib/loki/chunks";
};
};
limits_config = {
reject_old_samples = true;
reject_old_samples_max_age = "168h";
};
chunk_store_config = {
max_look_back_period = "0s";
};
table_manager = {
retention_deletes_enabled = false;
retention_period = "0s";
};
compactor = {
working_directory = "/var/lib/loki";
shared_store = "filesystem";
compactor_ring = {
kvstore = {
store = "inmemory";
};
};
};
};
# user, group, dataDir, extraFlags, (configFile)
};
#services.nginx = {
# enable = true;
# virtualHosts.loki = {
# serverName = "loki.pepe.private";
# locations."/" = {
# proxyWebsockets = true;
# proxyPass = "http://127.0.0.1:3100";
# #extraConfig = ''
# # access_log off;
# # allow ${config.tinc.private.subnet};
# # deny all;
# #'';
# };
# };
#};
}

View file

@ -0,0 +1,663 @@
# fetches mails for me
{ lib, pkgs, config, ... }:
let
junk_filter = [
"from:booking.com"
"subject:Gewinn"
"from:brompton.com"
"from:circleci.com OR (from:noreply@github.com AND to:audio-overlay@googlegroups.com)"
"from:codepen.io"
"from:congstarnews.de"
"from:cronullasurfingacademy.com"
"from:cryptohopper.com"
"from:digitalo.de"
"from:facebook.com OR from:facebookmail.com"
"from:fitnessfirst.de"
"from:flixbus.de"
"from:getdigital.de"
"from:getpocket.com"
"from:ghostinspector.com"
"from:globetrotter.de"
"from:hackster.io"
"from:hostelworld.com"
"from:immobilienscout24.de"
"from:kvraudio.com"
"from:letterboxd.com"
"from:linkedin.com"
"from:magix.net"
"from:mailings.gmx.net"
"from:mailings.web.de"
"from:matrix.org"
"from:menospese.com"
"from:microsoftstoreemail.com"
"from:mixcloudmail.com AND subject:Weekly Update"
"from:oknotify2.com AND NOT subject:New message"
"from:paulaschoice.com"
"from:puppet.com"
"from:runtastic.com"
"from:samplemagic.com OR from:wavealchemy.co.uk OR from:creators.gumroad.com"
"from:ticketmaster.de"
"from:trade4less.de"
"from:tumblr.com"
"from:turners.co.nz"
"from:twitch.tv"
"from:vstbuzz.com"
];
filters = [
{
query = "from:hv-geelen.de";
tags = [ "+wohnung" ];
}
{
query = "from:computerfutures.com OR from:computerfutures.de";
tags = [ "+jobs" "-inbox" ];
}
{
query = "from:seek.com.au or from:seek.co.nz";
tags = [ "+jobs" ];
}
{
query = "from:xing.com";
tags = [ "+jobs" "-inbox" ];
}
{
query = "from:no-reply@backtrace.io OR to:sononym@noreply.github.com";
tags = [ "+sononym" "-inbox" ];
}
{
query = "from:ebay.com OR from:ebay.de OR from:ebay.net";
tags = [ "+ebay" "+shop" "+billing" ];
}
{
query = "from:bahn.de";
tags = [ "+billing" "+bahn" ];
}
{
query =
"from:fysitech.atlassian.net OR to:engiadina-pwa@noreply.github.com";
tags = [ "+mia" "+work" "-unread" "-inbox" ];
}
{
query =
"from:space-left.org OR to:space-left.org OR subject:/\\[space-left\\]/";
tags = [ "+spaceleft" "+space-left" ];
}
{
query = "from:landr.com";
tags = [ "+landr" "+music" ];
}
{
query = "tag:landr and tag:billing";
tags = [ "+billing" ];
}
{
query = "from:oknotify2.com";
tags = [ "+okcupid" ];
}
{
query = "from:taxback.de OR to:taxback.de";
tags = [ "+steuer" ];
}
{
query = "from:campact.de";
tags = [ "+campact" "+politics" ];
}
{
query = "from:aliexpress.com";
tags = [ "+shop" "+aliexpress" ];
}
{
query = "from:congstar.de";
tags = [ "+billing" "+congstar" "-inbox" "-unread" ];
}
{
query =
"from:steampowered.com AND NOT ( subject:purchase OR subject:received )";
tags = [ "-inbox" "-unread" ];
}
{
query =
"from:steampowered.com AND ( subject:purchase OR subject:received )";
tags = [ "+billing" "+steam" ];
}
{
query = "from:gog.com AND NOT subject:Bestellung";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:gog.com AND subject:Bestellung";
tags = [ "+billing" "+gog" ];
}
{
query = "from:stadtmobil.de";
tags = [ "+billing" "+stadtmobil" "-inbox" "-unread" ];
}
{
query = "from:drive-now.com";
tags = [ "+billing" "+drivenow" "-inbox" "-unread" ];
}
{
query = "from:data-treuhand.de";
tags = [ "+mindcurv" "+work" "-inbox" "-unread" "-junk" ];
}
{
query = "from:immocation.de";
tags = [ "+immobilien" "-inbox" ];
}
{
query = "from:tinc-vpn.org";
tags = [ "+tinc" ];
}
{
query = "from:mindfactory.de";
tags = [ "+shop" "+billing" ];
}
{
query = "from:zalando.de";
tags = [ "+shop" "+billing" "+zalando" ];
}
{
query = "from:ing.de";
tags = [ "+bank" "+ingdiba" ];
}
{
query = "from:nab.com.au";
tags = [ "+bank" "+nab" "-inbox" "-unread" ];
}
{
query = "from:dkb.de";
tags = [ "+bank" "+dkb" ];
}
{
query = "from:o2online.de";
tags = [ "+billing" "+o2" ];
}
{
query = "from:betfair.com";
tags = [ "+work" "+betfair" ];
}
{
query = "from:notifications@github.com";
tags = [ "+github" ];
}
{
query = "to:NUR@noreply.github.com";
tags = [ "+nur" "+nixos" "+list" ];
}
{
query = "to:nixpkgs@noreply.github.com";
tags = [ "+nixpkgs" "+nixos" "+list" ];
}
{
query = "from:travis-ci.org AND subject:mrVanDalo/navi";
tags = [ "+development" "+navi" ];
}
{
query = "from:travis-ci.org AND subject:nur-packages";
tags = [ "+development" "+nixos" "+nur-packages" ];
}
{
query = "from:travis-ci.org AND subject:csv-to-qif";
tags = [ "+development" "+csv-to-qif" ];
}
{
query = "to:proaudio@lists.tuxfamily.org";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:nixos1@discoursemail.com";
tags = [ "+nixos" "+discourse" "+list" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Development";
tags = [ "+nixos" "+discourse" "+development" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Français";
tags = [ "+nixos" "+discourse" "-inbox" "-unread" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Announcements";
tags = [ "+nixos" "+discourse" "+announcements" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Links";
tags = [ "+nixos" "+discourse" "+links" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Games";
tags = [ "+nixos" "+discourse" "+games" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Meta";
tags = [ "+nixos" "+discourse" "+meta" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Events";
tags = [ "+nixos" "+discourse" "+events" ];
}
{
query = "from:limebike.com AND (subject:Funds OR subject:Receipt)";
tags = [ "-inbox" "-unread" "+billing" "+limebike" ];
}
{
query = "from:freemusicarchive.org";
tags = [ "+FMA" ];
}
{
query = "from:namecheap.com and subject:auto-renewal";
tags = [ "+namecheap" "+billing" ];
}
{
query = "from:namecheap.com and subject:order";
tags = [ "+namecheap" "+billing" ];
}
{
query = "tag:namecheap.com and tag:billing and body:gaykraft.com";
tags = [ "+namecheap" "+billing" ];
}
{
query = "from:nintendo.com";
tags = [ "+nintendo" "+billing" ];
}
{
query = "from:oculus.com AND subject:receipt";
tags = [ "+oculus" "+billing" ];
}
{
query = "from:car2go.com";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:sixt.de";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:meetup.com";
tags = [ "-inbox" "-unread" "+meetup" ];
}
{
query = "from:slack.com";
tags = [ "+slack" ];
}
{
query = "from:keybase.io";
tags = [ "+keybase" ];
}
{
query = "from:jobs2web.com";
tags = [ "+newzealand" "+jobs" "-inbox" ];
}
{
query = "from:paypal.de AND subject:Bestätigung";
tags = [ "-unread" "+paypal" "+billing" ];
}
{
query = "to:c-base.org";
tags = [ "+cbase" "+list" ];
}
{
query = "to:c-base.org AND subject=[auto-report]";
tags = [ "-unread" "-inbox" ];
}
{
query = "from:browserstack.com";
tags = [ "+browserstack" ];
}
{
query =
"to:renoise@ingolf-wagner.de OR to:root@renoise.com OR from:renoise.com OR to:admin@renoise.com";
tags = [ "+renoise" ];
}
{
query = "from:amazon.de OR from:amazon.com AND NOT to:renoise.com";
tags = [ "+shop" "+amazon" "+billing" ];
}
{
query = "from:hetzner.com OR from:hetzner.de";
tags = [ "+hetzner" ];
}
{
query =
"to:renoise.com AND NOT ( from:renoise.com OR from:root OR from:hetzner.com OR from:hetzner.de OR from:amazon.com OR from:gmail.com )";
tags = [ "-inbox" "-unread" "+junk" "+renoise" ];
}
{
query = "tag:hetzner and subject:Invoice";
tags = [ "+billing" ];
}
# final rules to make imap sync stuff easier
# there can only be one output folder tag, and theses rules are prioritized
{
query = "tag:fraud";
tags = [ "-inbox" "-archive" "-junk" "-unread" ];
message = "clean up tag fraud";
}
{
query = "tag:junk";
tags = [ "-inbox" "-archive" "-fraud" "-unread" ];
message = "clean up tag junk";
}
{
query = "tag:archive";
tags = [ "-inbox" "-junk" "-fraud" "-unread" ];
message = "clean up tag archive";
}
{
query = "tag:inbox";
tags = [ "-archive" "-junk" "-fraud" ];
message = "clean up inbox";
}
{
query = "tag:killed";
tags = [ "-inbox" "-unread" ];
message = "clean up tag killed";
}
{
query = "tag:muted";
tags = [ "-inbox" "-unread" ];
}
# remove new tag at the end
{
query = "tag:new";
tags = [ "-new" ];
message = "remove new tag at the end";
}
];
notmuchTagging =
let
template = index:
{ tags, query, message ? "generic", ... }:
let
command = ''
${pkgs.notmuch}/bin/notmuch tag ${lib.concatStringsSep " " tags} -- "${query}"
'';
in
''
echo '${command}'
${command}
'';
junk_template = index: query:
template index {
tags = [ "+junk" "-unread" "-inbox" ];
query = query;
message = "generic junk filter";
};
in
pkgs.writers.writeBash "notmuch-tagging" (lib.concatStringsSep "\n"
((lib.imap0 junk_template junk_filter) ++ (lib.imap0 template filters)));
notmuchTaggingNew =
let
template = index:
{ tags, query, message ? "generic", ... }:
let
command = ''
${pkgs.notmuch}/bin/notmuch tag ${
lib.concatStringsSep " " tags
} -- "${query} AND tag:new"
'';
in
''
echo '${command}'
${command}
'';
junk_template = index: query:
template index {
tags = [ "+junk" "-unread" "-inbox" ];
query = query;
message = "generic junk filter";
};
in
pkgs.writers.writeBash "notmuch-tagging-new" (lib.concatStringsSep "\n"
((lib.imap0 junk_template junk_filter) ++ (lib.imap0 template filters)));
in
{
backup.dirs = [ "/home/mailfetcher" ];
users.users.mailUser = {
isNormalUser = true;
description = "collects mails for me";
hashedPassword = "!";
name = "mailfetcher";
home = "/home/mailfetcher";
openssh.authorizedKeys.keyFiles =
config.users.users.root.openssh.authorizedKeys.keyFiles;
group = "mailfetcher";
};
users.groups.mailUser = {
name = "mailfetcher";
};
sops.secrets.mail_terranix = {
owner = config.users.users.mailUser.name;
group = config.users.users.mailUser.group;
};
sops.secrets.mail_gmail = {
owner = config.users.users.mailUser.name;
group = config.users.users.mailUser.group;
};
sops.secrets.mail_gmx_palo = {
owner = config.users.users.mailUser.name;
group = config.users.users.mailUser.group;
};
sops.secrets.mail_gmx_ingolf = {
owner = config.users.users.mailUser.name;
group = config.users.users.mailUser.group;
};
sops.secrets.mail_web = {
owner = config.users.users.mailUser.name;
group = config.users.users.mailUser.group;
};
sops.secrets.mail_siteground = {
owner = config.users.users.mailUser.name;
group = config.users.users.mailUser.group;
};
environment.systemPackages = [ pkgs.muchsync ];
# configure accounts
home-manager.users.mailUser.accounts.email = {
accounts = {
palo_van_dalo-gmx = {
primary = false;
address = "palo_van_dalo@gmx.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palo_van_dalo@gmx.de";
passwordCommand =
"cat ${toString config.sops.secrets.mail_gmx_palo.path }";
imap = {
host = "imap.gmx.net";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
ingolf-wagner-gmx = {
primary = false;
address = "ingolf.wagner@gmx.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "ingolf.wagner@gmx.de";
passwordCommand =
"cat ${toString config.sops.secrets.mail_gmx_ingolf.path }";
imap = {
host = "imap.gmx.net";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
pali_palo = {
primary = false;
address = "pali_palo@web.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "pali_palo@web.de";
passwordCommand =
"cat ${toString config.sops.secrets.mail_web.path }";
imap = {
host = "imap.web.de";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
gmail = {
# for google accounts you have to allow 'less secure apps' in accounts.google.com
primary = true;
address = "palipalo9@googlemail.com";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palipalo9@googlemail.com";
passwordCommand =
"cat ${toString config.sops.secrets.mail_gmail.path }";
imap = {
host = "imap.gmail.com";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
terranix_org = {
primary = false;
address = "palo@terranix.org";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palo@terranix.org";
passwordCommand = "cat ${toString config.sops.secrets.mail_terranix.path }";
imap = {
host = "mail.privateemail.com";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
ingolf-wagner-de = {
primary = false;
address = "contact@ingolf-wagner.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "contact@ingolf-wagner.de";
passwordCommand =
"cat ${toString config.sops.secrets.mail_siteground.path }";
imap = {
host = "securees5.sgcpanel.com";
port = 993;
tls.enable = true;
#tls.useStartTls = true;
};
# make sure the upstream mail is deleted
getmail = {
enable = true;
delete = true;
readAll = false;
mailboxes = [ "ALL" ];
};
notmuch.enable = true;
};
};
};
home-manager.users.mailUser.home.stateVersion = "22.11";
# configure mbsync
home-manager.users.mailUser.programs.mbsync.enable = true;
# re-tag everything once a day
systemd.services.retagmail = {
enable = true;
serviceConfig = { User = config.users.users.mailUser.name; };
environment.NOTMUCH_CONFIG =
"${config.users.users.mailUser.home}/.config/notmuch/notmuchrc";
script = "${notmuchTagging}";
};
systemd.timers.retagmail = {
enable = true;
timerConfig = {
OnCalendar = "daily";
Persistent = "true";
};
wantedBy = [ "multi-user.target" ];
};
# fetch mails every 10 minutes
systemd.services.fetchmail =
let
threadTag = tag: ''
echo "tag threads with ${tag}"
${pkgs.notmuch}/bin/notmuch tag +${tag} $(${pkgs.notmuch}/bin/notmuch search --output=threads tag:${tag})
'';
in
{
enable = true;
serviceConfig = { User = config.users.users.mailUser.name; };
environment.NOTMUCH_CONFIG =
"${config.users.users.mailUser.home}/.config/notmuch/notmuchrc";
script = ''
echo "run mbsync"
${pkgs.isync}/bin/mbsync \
--all
echo "run getmail"
${pkgs.getmail}/bin/getmail \
--quiet \
--rcfile getmailingolf-wagner-de
echo "run notmuch"
${pkgs.notmuch}/bin/notmuch new
${notmuchTaggingNew}
${threadTag "muted"}
${threadTag "wohnung"}
${threadTag "flagged"}
'';
};
systemd.timers.fetchmail = {
enable = true;
# timerConfig.OnCalendar = " *-*-* *:00:00";
timerConfig.OnCalendar = "*:0/10";
wantedBy = [ "multi-user.target" ];
};
# configure notmuch
home-manager.users.mailUser.programs.notmuch = {
enable = true;
new.tags = [ "unread" "inbox" "new" ];
};
}

View file

@ -0,0 +1,80 @@
{ config, ... }:
{
networking.firewall.interfaces.wg0.allowedTCPPorts = [ 7878 8989 ];
# download series
services.sonarr = {
enable = true;
group = "media";
user = "media";
};
# download movies
services.radarr = {
enable = true;
group = "media";
user = "media";
};
# better indexer apis
services.prowlarr = {
enable = true;
#group = "media";
#user = "media";
};
services.jellyseerr = {
enable = true;
};
services.permown."/media/arr" = {
owner = "media";
group = "media";
directory-mode = "770";
file-mode = "770";
};
services.nginx.virtualHosts = {
"radarr.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:7878";
proxyWebsockets = true;
};
};
"sonarr.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:8989";
proxyWebsockets = true;
};
};
"prowlarr.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:9696";
proxyWebsockets = true;
};
};
"jellyseerr.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:${toString config.services.jellyseerr.port}";
proxyWebsockets = true;
};
};
};
}

View file

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
{
services.jellyfin = {
enable = true;
openFirewall = true;
group = "media";
user = "media";
};
hardware.opengl = {
enable = true;
driSupport = true;
driSupport32Bit = true;
};
services.nginx =
let
flixConfig = {
forceSSL = true;
enableACME = true;
extraConfig = ''
# Security / XSS Mitigation Headers
# NOTE: X-Frame-Options may cause issues with the webOS app
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
'';
locations."/" = {
recommendedProxySettings = true;
proxyWebsockets = true;
proxyPass = "http://localhost:8096";
};
};
in
{
enable = true;
virtualHosts. "flix.ingolf-wagner.de" = flixConfig;
virtualHosts. "video.ingolf-wagner.de" = flixConfig;
};
}

View file

@ -0,0 +1,21 @@
{ config, ... }:
{
users.groups."media".gid = config.ids.gids.transmission;
users.users."media" = {
uid = config.ids.uids.transmission;
useDefaultShell = true;
home = "/home/media";
createHome = true;
group = "media";
};
services.permown."/media/media" = {
owner = "media";
group = "media";
directory-mode = "770";
file-mode = "770";
};
}

View file

@ -0,0 +1,79 @@
{ config, pkgs, lib, ... }: {
imports = [ ../../system/all/syncthing.nix ];
sops.secrets.syncthing_cert = { };
sops.secrets.syncthing_key = { };
networking.firewall.allowedTCPPorts = [ 22000 ];
networking.firewall.allowedUDPPorts = [ 22000 ];
services.syncthing = {
enable = true;
openDefaultPorts = false;
dataDir = "/media/syncthing/config";
configDir = "/media/syncthing/config";
overrideDevices = true;
overrideFolders = true;
cert = toString config.sops.secrets.syncthing_cert.path;
key = toString config.sops.secrets.syncthing_key.path;
settings.folders = {
# on media hard drive (not encrypted)
# -----------------------------------
#borg-mirror = {
# enable = true;
# path = "/media/syncthing/borg";
# rescanInterval = 36 * 3600;
# type = "sendonly";
#};
#video-material = {
# enable = true;
# path = "/home/syncthing/video-material";
#};
books = {
enable = true;
path = "/media/syncthing/books";
};
lost-fotos = {
enable = true;
path = "/media/syncthing/lost-fotos.ct";
rescanIntervalS = 40 * 24 * 3600;
};
#media = {
# enable = true;
# watch = false;
# type = "sendonly";
# path = "/media/syncthing/media";
# rescanInterval = 27 * 24 * 3600;
#};
music-projects = {
enable = true;
#watch = true;
path = "/media/syncthing/music-projects";
};
nextcloud_backup = {
enable = true;
#watch = true;
path = "/media/syncthing/nextcloud_backup";
rescanIntervalS = 23 * 3600;
};
};
};
services.permown."/media/syncthing" = {
# not managed by syncthing anymore
owner = "syncthing";
group = "syncthing";
umask = "0002";
};
systemd.services."permown._media_syncthing" = {
bindsTo = [ "media.mount" ];
after = [ "media.mount" ];
};
systemd.services."syncthing" = {
bindsTo = [ "media.mount" ];
after = [ "media.mount" ];
};
}

View file

@ -0,0 +1,54 @@
{ config, lib, pkgs, ... }:
{
# https://docs.tdarr.io/docs/installation/docker/run-compose
virtualisation.oci-containers = {
containers.tdarr = {
volumes = [
"/media/arr/tdarr/server:/app/server"
"/media/arr/tdarr/configs:/app/configs"
"/media/arr/tdarr/logs:/app/logs"
"/media/arr/tdarr/transcode_cache:/temp"
"/media:/media"
];
environment = {
serverIP = "0.0.0.0";
serverPort = "8266";
webUIPort = "8265";
internalNode = "true";
inContainer = "true";
nodeName = "robi";
TZ = "Europe/Berlin";
PUID = toString config.users.users.media.uid;
PGID = toString config.users.groups.media.gid;
};
ports = [
"127.0.0.1:8265:8265" # WebUI
# "8266:8266" # server port
];
image = "ghcr.io/haveagitgat/tdarr:latest"; # Warning: if the tag does not change, the image will not be updated
extraOptions = [
#"--network=bridge"
#"--privileged"
];
};
};
#networking.firewall.interfaces.wq0.allowedTCPPorts = [ 8266 ];
#networking.firewall.interfaces.wq0.allowedUDPPorts = [ 8266 ];
#networking.firewall.interfaces.enp0s31f6.allowedTCPPorts = [ 8266 ];
#networking.firewall.interfaces.enp0s31f6.allowedUDPPorts = [ 8266 ];
services.nginx.virtualHosts."tdarr.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:8265";
proxyWebsockets = true;
};
};
}

View file

@ -0,0 +1,259 @@
{ lib, pkgs, config, ... }:
let
hostInterface = "enp3s0";
hostAddress = "192.168.100.30";
containerAddress = "192.168.100.31";
uiPort = 9091;
in
{
sops.secrets.nordvpn = { };
sops.secrets.transmissionPushover = { };
containers.torrent = {
# mount host folders
bindMounts = {
#password = {
# hostPath = "/run/secrets/transmission_password";
# mountPoint = "/run/secrets/transmission_password";
# isReadOnly = true;
#};
transmissionPushover = {
hostPath = "/run/secrets/transmissionPushover";
mountPoint = "/run/secrets/transmissionPushover";
isReadOnly = true;
};
nordvpnPassword = {
hostPath = "/run/secrets/nordvpn";
mountPoint = "/run/secrets/nordvpn";
isReadOnly = true;
};
media = {
hostPath = "/media";
mountPoint = "/media"; # must be here otherwise transmission can't see the folder
isReadOnly = false;
};
lib = {
hostPath = "/media/torrent/torrent1_config";
mountPoint = "/var/lib/transmission/.config";
isReadOnly = false;
};
};
# container network setup
# see also nating on host system.
privateNetwork = true;
hostAddress = hostAddress;
localAddress = containerAddress;
autoStart = true;
# needed for openvpn
enableTun = true;
config = { config, pkgs, lib, ... }: {
system.stateVersion = "21.05";
services.journald.extraConfig = "SystemMaxUse=1G";
# allow transmission to write in syncthing folders
users.groups.syncthing = {
gid = config.ids.gids.syncthing;
members = [ "transmission" ];
};
services.transmission = {
enable = true;
settings = {
download-dir = "/media/torrent/downloads";
incomplete-dir = "/media/torrent/incomplete";
incomplete-dir-enabled = true;
message-level = 1;
umask = 2;
rpc-whitelist-enabled = false;
rpc-host-whitelist-enabled = false;
rpc-port = uiPort;
rpc-enable = true;
rpc-bind-address = "0.0.0.0";
# "normal" speed limits
speed-limit-down-enabled = false;
speed-limit-down = 800;
speed-limit-up-enabled = true;
speed-limit-up = 3000;
upload-slots-per-torrent = 8;
# Queuing
# When true, Transmission will only download
# download-queue-size non-stalled torrents at once.
download-queue-enabled = true;
download-queue-size = 3;
# When true, torrents that have not shared data for
# queue-stalled-minutes are treated as 'stalled'
# and are not counted against the queue-download-size
# and seed-queue-size limits.
queue-stalled-enabled = true;
queue-stalled-minutes = 60;
# When true. Transmission will only seed seed-queue-size
# non-stalled torrents at once.
seed-queue-enabled = false;
seed-queue-size = 10;
# Enable UPnP or NAT-PMP.
peer-port = 51413;
port-forwarding-enabled = false;
# Start torrents as soon as they are added
start-added-torrents = true;
};
};
networking.firewall = {
allowedTCPPorts = [ 51413 ];
allowedUDPPorts = [ 51413 ];
# only allow access via nginx (proxy to localhost)
interfaces.eth0 = {
allowedTCPPorts = [ uiPort ];
allowedUDPPorts = [ uiPort ];
};
};
# bind transmission to openvpn
systemd.services.transmission = {
bindsTo = [ "openvpn-nordvpn.service" ];
after = [ "openvpn-nordvpn.service" ];
serviceConfig = {
Restart = "always";
EnvironmentFile = [
"/run/secrets/transmissionPushover"
];
BindPaths = lib.mkForce [
"/media" # this is needed otherwise cp -l is not working
"/var/lib/transmission/.config/transmission-daemon"
];
};
};
services.openvpn.servers.nordvpn.updateResolvConf = true;
services.openvpn.servers.nordvpn.config = ''
client
dev tun
proto udp
remote 152.89.163.99 1194
dhcp-option DNS 8.8.8.8
remote-random
nobind
tun-mtu 1500
tun-mtu-extra 32
mssfix 1450
persist-key
persist-tun
ping 15
ping-restart 0
ping-timer-rem
reneg-sec 0
comp-lzo no
remote-cert-tls server
auth-user-pass /run/secrets/nordvpn
verb 3
pull
resolv-retry infinite
fast-io
cipher AES-256-CBC
auth SHA512
<ca>
-----BEGIN CERTIFICATE-----
MIIFCjCCAvKgAwIBAgIBATANBgkqhkiG9w0BAQ0FADA5MQswCQYDVQQGEwJQQTEQ
MA4GA1UEChMHTm9yZFZQTjEYMBYGA1UEAxMPTm9yZFZQTiBSb290IENBMB4XDTE2
MDEwMTAwMDAwMFoXDTM1MTIzMTIzNTk1OVowOTELMAkGA1UEBhMCUEExEDAOBgNV
BAoTB05vcmRWUE4xGDAWBgNVBAMTD05vcmRWUE4gUm9vdCBDQTCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBAMkr/BYhyo0F2upsIMXwC6QvkZps3NN2/eQF
kfQIS1gql0aejsKsEnmY0Kaon8uZCTXPsRH1gQNgg5D2gixdd1mJUvV3dE3y9FJr
XMoDkXdCGBodvKJyU6lcfEVF6/UxHcbBguZK9UtRHS9eJYm3rpL/5huQMCppX7kU
eQ8dpCwd3iKITqwd1ZudDqsWaU0vqzC2H55IyaZ/5/TnCk31Q1UP6BksbbuRcwOV
skEDsm6YoWDnn/IIzGOYnFJRzQH5jTz3j1QBvRIuQuBuvUkfhx1FEwhwZigrcxXu
MP+QgM54kezgziJUaZcOM2zF3lvrwMvXDMfNeIoJABv9ljw969xQ8czQCU5lMVmA
37ltv5Ec9U5hZuwk/9QO1Z+d/r6Jx0mlurS8gnCAKJgwa3kyZw6e4FZ8mYL4vpRR
hPdvRTWCMJkeB4yBHyhxUmTRgJHm6YR3D6hcFAc9cQcTEl/I60tMdz33G6m0O42s
Qt/+AR3YCY/RusWVBJB/qNS94EtNtj8iaebCQW1jHAhvGmFILVR9lzD0EzWKHkvy
WEjmUVRgCDd6Ne3eFRNS73gdv/C3l5boYySeu4exkEYVxVRn8DhCxs0MnkMHWFK6
MyzXCCn+JnWFDYPfDKHvpff/kLDobtPBf+Lbch5wQy9quY27xaj0XwLyjOltpiST
LWae/Q4vAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqG
SIb3DQEBDQUAA4ICAQC9fUL2sZPxIN2mD32VeNySTgZlCEdVmlq471o/bDMP4B8g
nQesFRtXY2ZCjs50Jm73B2LViL9qlREmI6vE5IC8IsRBJSV4ce1WYxyXro5rmVg/
k6a10rlsbK/eg//GHoJxDdXDOokLUSnxt7gk3QKpX6eCdh67p0PuWm/7WUJQxH2S
DxsT9vB/iZriTIEe/ILoOQF0Aqp7AgNCcLcLAmbxXQkXYCCSB35Vp06u+eTWjG0/
pyS5V14stGtw+fA0DJp5ZJV4eqJ5LqxMlYvEZ/qKTEdoCeaXv2QEmN6dVqjDoTAo
k0t5u4YRXzEVCfXAC3ocplNdtCA72wjFJcSbfif4BSC8bDACTXtnPC7nD0VndZLp
+RiNLeiENhk0oTC+UVdSc+n2nJOzkCK0vYu0Ads4JGIB7g8IB3z2t9ICmsWrgnhd
NdcOe15BincrGA8avQ1cWXsfIKEjbrnEuEk9b5jel6NfHtPKoHc9mDpRdNPISeVa
wDBM1mJChneHt59Nh8Gah74+TM1jBsw4fhJPvoc7Atcg740JErb904mZfkIEmojC
VPhBHVQ9LHBAdM8qFI2kRK0IynOmAZhexlP/aT/kpEsEPyaZQlnBn3An1CRz8h0S
PApL8PytggYKeQmRhl499+6jLxcZ2IegLfqq41dzIjwHwTMplg+1pKIOVojpWA==
-----END CERTIFICATE-----
</ca>
key-direction 1
<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
e685bdaf659a25a200e2b9e39e51ff03
0fc72cf1ce07232bd8b2be5e6c670143
f51e937e670eee09d4f2ea5a6e4e6996
5db852c275351b86fc4ca892d78ae002
d6f70d029bd79c4d1c26cf14e9588033
cf639f8a74809f29f72b9d58f9b8f5fe
fc7938eade40e9fed6cb92184abb2cc1
0eb1a296df243b251df0643d53724cdb
5a92a1d6cb817804c4a9319b57d53be5
80815bcfcb2df55018cc83fc43bc7ff8
2d51f9b88364776ee9d12fc85cc7ea5b
9741c4f598c485316db066d52db4540e
212e1518a9bd4828219e24b20d88f598
a196c9de96012090e333519ae18d3509
9427e7b372d348d352dc4c85e18cd4b9
3f8a56ddb2e64eb67adfc9b337157ff4
-----END OpenVPN Static key V1-----
</tls-auth>
'';
};
};
# give containers internet access
networking.nat.enable = true;
networking.nat.internalInterfaces = [ "ve-torrent" ];
networking.nat.externalInterface = hostInterface;
# open ports for logging
#networking.firewall.interfaces."ve-torrent".allowedTCPPorts =
# [ 5044 12304 12305 ];
#networking.firewall.interfaces."ve-torrent".allowedUDPPorts =
# [ 5044 12304 12305 ];
# host nginx setup
# curl -H "Host: transmission.robi.private" https://robi.private/ < will work
# curl -H "Host: transmission.robi.private" https://144.76.13.147/ < wont work
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"transmission.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://${containerAddress}:${toString uiPort}";
};
};
};
};
}

View file

@ -0,0 +1,136 @@
{ lib, pkgs, config, ... }:
let
uiPort = 9091;
in
{
containers.torrent2 = {
# mount host folders
bindMounts = {
media = {
hostPath = "/media";
mountPoint = "/media"; # must be here otherwise transmission can't see the folder
isReadOnly = false;
};
lib = {
hostPath = "/media/torrent/torrent2_config";
mountPoint = "/var/lib/transmission/.config";
isReadOnly = false;
};
};
autoStart = true;
config = { config, pkgs, lib, ... }: {
system.stateVersion = "21.05";
services.journald.extraConfig = "SystemMaxUse=1G";
# allow transmission to write in syncthing folders
users.groups.syncthing = {
gid = config.ids.gids.syncthing;
members = [ "transmission" ];
};
services.transmission = {
enable = true;
settings = {
download-dir = "/media/torrent/downloads";
incomplete-dir = "/media/torrent/incomplete";
incomplete-dir-enabled = true;
message-level = 1;
umask = 2;
rpc-whitelist-enabled = false;
rpc-host-whitelist-enabled = false;
rpc-port = uiPort;
rpc-enable = true;
rpc-bind-address = "127.0.0.1";
# "normal" speed limits
speed-limit-down-enabled = false;
speed-limit-down = 800;
speed-limit-up-enabled = true;
speed-limit-up = 3000;
upload-slots-per-torrent = 8;
# Queuing
# When true, Transmission will only download
# download-queue-size non-stalled torrents at once.
download-queue-enabled = true;
download-queue-size = 3;
# When true, torrents that have not shared data for
# queue-stalled-minutes are treated as 'stalled'
# and are not counted against the queue-download-size
# and seed-queue-size limits.
queue-stalled-enabled = true;
queue-stalled-minutes = 60;
# When true. Transmission will only seed seed-queue-size
# non-stalled torrents at once.
seed-queue-enabled = false;
seed-queue-size = 10;
# Enable UPnP or NAT-PMP.
peer-port = 51413;
port-forwarding-enabled = false;
# Start torrents as soon as they are added
start-added-torrents = true;
# Encryption preference.
# 0 = Prefer unencrypted connections,
# 1 = Prefer encrypted connections,
# 2 = Require encrypted connections;
# default = 1
# Encryption may help get around some ISP filtering, but at the cost of slightly
# higher CPU use
encryption = 2;
};
};
networking.firewall = {
allowedTCPPorts = [ 51413 ];
allowedUDPPorts = [ 51413 ];
};
# bind transmission to openvpn
systemd.services.transmission = {
serviceConfig = {
Restart = "always";
BindPaths = lib.mkForce [
"/media" # this is needed otherwise cp -l is not working
"/var/lib/transmission/.config/transmission-daemon"
];
};
};
};
};
networking.firewall = {
allowedTCPPorts = [ 51413 ];
allowedUDPPorts = [ 51413 ];
};
# host nginx setup
# ----------------
# curl -H "Host: transmission.robi.private" https://robi.private/ < will work
# curl -H "Host: transmission.robi.private" https://144.76.13.147/ < wont work
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"transmission2.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://127.0.0.1:${toString uiPort}";
};
};
};
};
}

View file

@ -0,0 +1,40 @@
{ config, lib, pkgs, ... }:
{
virtualisation.oci-containers = {
containers.unmanic = {
volumes = [
"/media/arr/unmanic/config:/config"
#"/media/arr/unmanic/library:/library"
"/media/arr/unmanic/tmp:/tmp/unmanic"
"/media:/library"
];
environment = {
PUID = toString config.users.users.media.uid;
PGID = toString config.users.groups.media.gid;
};
ports = [
"127.0.0.1:8889:8888"
];
image = "josh5/unmanic:latest";
};
};
#networking.firewall.interfaces.wq0.allowedTCPPorts = [ 8266 ];
#networking.firewall.interfaces.wq0.allowedUDPPorts = [ 8266 ];
#networking.firewall.interfaces.enp0s31f6.allowedTCPPorts = [ 8266 ];
#networking.firewall.interfaces.enp0s31f6.allowedUDPPorts = [ 8266 ];
services.nginx.virtualHosts."unmanic.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://localhost:8889";
proxyWebsockets = true;
};
};
}

View file

@ -0,0 +1,20 @@
{ pkgs, lib, config, ... }: {
services.mysql = {
enable = true;
package = pkgs.mysql80;
initialScript = pkgs.writeText "initScript" ''
CREATE USER 'admin'@'%' IDENTIFIED BY 'admin';
GRANT ALL PRIVILEGES ON * . * TO 'admin'@'%';
'';
};
services.mysqlBackup = {
enable = true;
databases = [ "property" ];
#user = "admin";
};
backup.dirs = [ config.services.mysqlBackup.location ];
}

View file

@ -0,0 +1,15 @@
{
networking.firewall = {
allowedTCPPorts = [ 655 721 ];
allowedUDPPorts = [ 655 721 ];
};
tinc.private.enable = true;
tinc.private.ipv4 = "10.23.42.111";
tinc.secret.enable = true;
tinc.secret.ipv4 = "10.123.42.123";
}

View file

@ -0,0 +1,55 @@
{ pkgs, config, ... }:
{
# networking.firewall.trustedInterfaces = [ "wg0" ];
networking.firewall.allowedUDPPorts = [ 51820 ];
sops.secrets.wireguard_private = { };
boot.kernel.sysctl."net.ipv4.ip_forward" = true;
# Enable WireGuard
networking.wg-quick.interfaces = {
# Hub and Spoke Setup
# https://www.procustodibus.com/blog/2020/11/wireguard-hub-and-spoke-config/
wg0 = {
address = [ "10.100.0.1/32" ];
listenPort = 51820; # to match firewall allowedUDPPorts (without this wg uses random port numbers)
privateKeyFile = config.sops.secrets.wireguard_private.path;
mtu = 1280;
postUp = ''
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
'';
postDown = ''
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT
'';
# clients
peers = [
{
# chungus
publicKey = "wb54y/fG8ocSH9QrDmfajez/fUcJBZK369xLu37XBHk=";
allowedIPs = [ "10.100.0.2/32" ];
}
{
# sterni
publicKey = "SdMRgC5IM7dywzZxLAHm45cpj9J3IENTMClZm1BxbV4=";
allowedIPs = [ "10.100.0.3/32" ];
}
{
# iphone
publicKey = "XPVzH+wBLsqukTHHjngkGJhYN0nRdQ7esadiimMJQnI=";
allowedIPs = [ "10.100.0.4/32" ];
}
{
# tina
publicKey = "RZsuQfWfAQLMm45ZiuNLBwcpL+GEbPYTRTrASFzMCFQ=";
allowedIPs = [ "10.100.0.5/32" ];
}
{
# cream
publicKey = "R1Vk1DDG/LsVU0HHRDmOJshXOVnNzPVbuv5hP7ZSGEQ=";
allowedIPs = [ "10.100.0.6/32" ];
}
];
};
};
}

View file

@ -0,0 +1,301 @@
{ pkgs, config, ... }:
# don't forget the database backup before upgrading
# -------------------------------------------------
# https://docs.nextcloud.com/server/stable/admin_manual/maintenance/backup.html
# https://docs.nextcloud.com/server/stable/admin_manual/maintenance/upgrade.html
#
# ! use lvm snapshots to do rollback !
let
hostInterface = "enp3s0";
hostAddress = "192.168.100.10";
containerAddress = "192.168.100.11";
nextcloudUid = 1000;
borg_backup_folder = "/media/syncthing/nextcloud_backup/robi";
in
{
# Host Setup
# ==========
# give containers internet access
networking.nat.enable = true;
networking.nat.internalInterfaces = [ "ve-nextcloud" ];
networking.nat.externalInterface = hostInterface;
# don't let networkmanager manger container network
networking.networkmanager.unmanaged = [ "interface-name:ve-*" ];
# open ports for logging
#networking.firewall.interfaces."ve-nextcloud".allowedTCPPorts =
# [ 5044 12304 12305 ];
#networking.firewall.interfaces."ve-nextcloud".allowedUDPPorts =
# [ 5044 12304 12305 ];
# host nginx
# ----------
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.firewall.allowedUDPPorts = [ 80 443 ];
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"nextcloud.ingolf-wagner.de" = {
forceSSL = true;
enableACME = true;
locations = {
"/" = {
proxyPass = "http://${containerAddress}";
extraConfig = ''
sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de";
# used for view/edit office file via Office Online Server
client_max_body_size 0;
proxy_buffering off; # to download files bigger than 1GB
'';
};
"= /.well-known/carddav" = {
priority = 210;
extraConfig = "return 301 $scheme://$host/remote.php/dav;";
};
"= /.well-known/caldav" = {
priority = 210;
extraConfig = "return 301 $scheme://$host/remote.php/dav;";
};
#"~ .(?:css|js|svg|gif)$" = {
# proxyPass = "http://${containerAddress}$request_uri";
# extraConfig = ''
# expires 6M; # Cache-Control policy borrowed from `.htaccess`
# access_log off; # Optional: Don't log access to assets
# sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de";
# sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de";
# # used for view/edit office file via Office Online Server
# client_max_body_size 0;
# '';
#};
#"~ .woff2?$" = {
# proxyPass = "http://${containerAddress}$request_uri";
# extraConfig = ''
# expires 7d; # Cache-Control policy borrowed from `.htaccess`
# access_log off; # Optional: Don't log access to assets
# sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de";
# sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de";
# # used for view/edit office file via Office Online Server
# client_max_body_size 0;
# '';
#};
};
};
};
};
sops.secrets.nextcloud_database_password.owner = "nextcloud";
sops.secrets.nextcloud_root_password.owner = "nextcloud";
users.users.nextcloud = {
isSystemUser = true;
uid = nextcloudUid;
group = "nextcloud";
};
users.groups.nextcloud = { };
# Container Setup
# ===============
#
# running:
# * nextcloud (php)
# * mysql
containers.nextcloud = {
# mount host folders
bindMounts = {
rootpassword = {
hostPath = "/run/secrets/nextcloud_root_password";
mountPoint = "/run/secrets/nextcloud_root_password";
isReadOnly = true;
};
databasepassword = {
hostPath = "/run/secrets/nextcloud_database_password";
mountPoint = "/run/secrets/nextcloud_database_password";
isReadOnly = true;
};
home = {
# make sure this folder exist on the host
hostPath = toString "/var/lib/nextcloud";
mountPoint = "/var/lib/nextcloud";
isReadOnly = false;
};
db = {
# make sure this folder exist on the host
hostPath = toString "/var/lib/nextcloud_mysql";
mountPoint = "/var/lib/mysql";
isReadOnly = false;
};
media = {
#mountPoint = toString config.services.syncthing.folders.media.path;
#hostPath = toString config.services.syncthing.folders.media.path;
mountPoint = "/media/syncthing/media";
hostPath = "/media/media";
isReadOnly = true;
};
};
# container network setup
# see also nating on host system.
privateNetwork = true;
hostAddress = hostAddress;
localAddress = containerAddress;
autoStart = true;
config = { config, pkgs, lib, ... }: {
# Configuring nameservers for containers is currently broken.
# Therefore in some cases internet connectivity can be broken inside the containers.
# A temporary workaround is to manually write the /etc/nixos/resolv.conf file like this:
environment.etc."resolv.conf".text = "nameserver 8.8.8.8";
system.stateVersion = "21.05";
users.users.nextcloud.uid = nextcloudUid;
services.nginx = {
# Use recommended settings
recommendedGzipSettings = lib.mkDefault true;
recommendedOptimisation = lib.mkDefault true;
recommendedProxySettings = lib.mkDefault true;
recommendedTlsSettings = lib.mkDefault true;
};
networking.firewall.allowedTCPPorts = [ 80 ];
networking.firewall.allowedUDPPorts = [ 80 ];
# nextcloud database
# ==================
#
# set user password:
# -----------------
# #> mysql
# mysql> ALTER USER 'nextcloud'@'localhost' IDENTIFIED BY 'nextcloud-password';
#
# recreate database:
# ------------------
# mysql> DROP DATABASE nextcloud;
# mysql> CREATE DATABASE nextcloud;
#
# migration:
# ----------
# nextcloud-occ db:convert-type --all-apps mysql nextcloud 127.0.0.1 nextcloud
#
# 4-byte stuff:
# -------------
# https://docs.nextcloud.com/server/18/admin_manual/configuration_database/mysql_4byte_support.html
# if you do this don't forget --default-character-set=utf8mb4 for mysqldump
services.mysql = {
enable = true;
package = pkgs.mysql;
# https://nixos.org/manual/nixos/stable/release-notes.html#sec-release-20.09-incompatibilities
ensureDatabases = [ "nextcloud" ];
ensureUsers = [{
name = "nextcloud";
ensurePermissions = { "nextcloud.*" = "ALL PRIVILEGES"; };
}];
settings.mysqld = {
innodb_large_prefix = true;
innodb_file_format = "barracuda";
innodb_file_per_table = 1;
innodb_read_only_compressed = 0;
};
};
# Backup database
# ---------------
services.mysqlBackup = {
enable = true;
databases = config.services.mysql.ensureDatabases;
singleTransaction = true;
location = "/var/lib/nextcloud/database_backups";
};
systemd.services."mysql-backup".serviceConfig = {
ExecStartPre = [ "+/run/current-system/sw/bin/nextcloud-occ maintenance:mode --on" ];
ExecStopPost = [ "+/run/current-system/sw/bin/nextcloud-occ maintenance:mode --off" ];
};
# in php
services.phpfpm = {
phpPackage = pkgs.php73;
phpOptions = ''
opcache.revalidate_freq = 10
'';
};
# nextcloud setup
services.nextcloud = {
enable = true;
package = pkgs.nextcloud27;
autoUpdateApps.enable = true;
# nginx.enable = true;
# enableBrokenCiphersForSSE = false; # see https://github.com/NixOS/nixpkgs/pull/198470
hostName = "nextcloud.ingolf-wagner.de";
logLevel = 2;
https = true;
config = {
adminpassFile = "/run/secrets/nextcloud_root_password";
overwriteProtocol = "https";
trustedProxies = [ "144.76.13.147" hostAddress ];
dbtype = "mysql";
dbpassFile = "/run/secrets/nextcloud_database_password";
dbport = 3306;
defaultPhoneRegion = "DE";
};
};
};
};
# Backup Config
# -------------
#backup.dirs = [
# "/home/nextcloud/config"
# "/home/nextcloud/database_backups" # created by mysqlBackup
#];
# Backup Files
# ------------
services.borgbackup.jobs = {
"nextcloud-to-media" = {
repo = borg_backup_folder;
# make sure syncthing is capable of reading the files
postHook = ''
chown -R syncthing:syncthing ${borg_backup_folder}
'';
compression = "lz4";
paths = [
"/var/lib/nextcloud/data/tina/files"
"/var/lib/nextcloud/data/palo/files"
"/var/lib/nextcloud/data/palo-windows/files"
];
doInit = true;
encryption = {
mode = "repokey-blake2";
passCommand = "cat ${config.sops.secrets.backup_repository_passphrase.path}";
};
startAt = "0/3:00:00";
prune.keep = {
within = "2d"; # Keep all backups in the last 10 days.
daily = 10; # Keep 10 additional end of day archives
weekly = 8; # Keep 8 additional end of week archives.
month = 8; # Keep 8 additional end of month archives.
};
};
};
}

View file

@ -0,0 +1,30 @@
{ config, pkgs, lib, assets, ... }:
let
server_name = "ingolf-wagner.de";
in
{
# check :
# - https://metacode.biz/openpgp/web-key-directory
# - $> gpg --homedir "$(mktemp -d)" -v --auto-key-locate clear,wkd,nodefault --locate-key contact@ingolf-wagner.de
services.nginx.virtualHosts.${server_name}.locations =
let
wkd =
{
extraConfig = ''
default_type application/octet-stream;
add_header Access-Control-Allow-Origin * always;
'';
#alias = pkgs.runCommand "contact@ingolf-wagner.de" { } ''
# cat ${assets}/contact@ingolf-wagner.de.gpg | ${pkgs.gnupg}/bin/gpg --dearmor > $out
#'';
alias = toString "${assets}/contact@ingolf-wagner.de.gpg";
};
in
{
"= /.well-known/openpgpkey/policy".return = "200";
# hashes generated by : gpg --with-wkd-hash --fingerprint contact@ingolf-wagner.de
"= /.well-known/openpgpkey/hu/dj3498u4hyyarh35rkjfnghbjxug6b19" = wkd;
};
# todo openpgpkey.ingolf-wagner.de noch einrichten
}

View file

@ -0,0 +1,158 @@
{ config, lib, pkgs, private_assets, ... }:
let
# todo create flake for this
errorPages = pkgs.fetchFromGitHub {
owner = "mrvandalo";
repo = "http-errors";
rev = "74b8e4c1d9bbba3db6ad858b888e1867318af1f0";
sha256 = "0czdzafx4k76q773lyf3vsjm74g1995iz542dhw15kpy5xbivsrg";
};
error = {
extraConfig = ''
error_page 400 /errors/400.html;
error_page 401 /errors/401.html;
error_page 402 /errors/402.html;
error_page 403 /errors/403.html;
error_page 404 /errors/404.html;
error_page 405 /errors/405.html;
error_page 406 /errors/406.html;
error_page 500 /errors/500.html;
error_page 501 /errors/501.html;
error_page 502 /errors/502.html;
error_page 503 /errors/503.html;
error_page 504 /errors/504.html;
'';
locations."^~ /errors/" = {
extraConfig = "internal;";
root = "${errorPages}/";
};
};
in
{
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.firewall.allowedUDPPorts = [ 80 443 ];
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"ingolf-wagner.de" = {
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = pkgs.landingpage.override {
jsonConfig = [
{
title = "Ingolf Wagner";
text = ''
I'm a freelancing mathematician, musician and programmer.
My PGP fingerprint is 42AC 51C9 482D 0834 CF48 8AF1 389E C2D6 4AC7 1EAC
'';
}
{
text = ''
Here are some of my projects you might enjoy:
'';
items = [
{
label = "terranix";
href = "https://terranix.org";
image = "https://raw.githubusercontent.com/terranix/terranix-artwork/main/terranix-logo.svg";
}
{
label = "Sononym";
href = "https://www.sononym.net/";
image = "https://www.sononym.net/press/logos/sononym-logo-symbol-black.png";
}
{
label = "LandingPage";
href = "https://github.com/mrVanDalo/landingpage";
image = "https://media.giphy.com/media/2vNGq1w3nsJri/giphy.gif";
}
{
label = "My Blog";
href = "https://tech.ingolf-wagner.de/";
image = "https://media.giphy.com/media/11I8v5lE8uq79C/giphy.gif";
}
];
}
];
title = "Ingolf Wagner";
max-width = "1010px";
background-color = "#FEFAE0";
title-color = "black";
title-background-color = "#E9EDC9";
text-color = "black";
text-background-color = "#FAEDCD";
item-color = "black";
item-background-color = "#E9EDC9";
image-width = "250px";
image-height = "200px";
};
};
"= /palo.pgp" = {
root = pkgs.writeText "key" (lib.fileContents ../../assets/pgp.key);
};
"= /palo.gpg" = {
root = pkgs.writeText "key" (lib.fileContents ../../assets/pgp.key);
};
"= /palo_rsa.pub" = {
root = pkgs.writeText "key" (lib.fileContents ../../assets/ssh/palo_rsa.pub);
};
};
};
"travel.ingolf-wagner.de" = {
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = "/srv/www/travel";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
"tech.ingolf-wagner.de" = {
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = "/srv/www/tech";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
"terranix.org" = {
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = "/srv/www/terranix";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
};
};
}

View file

@ -0,0 +1,14 @@
{ config, pkgs, ... }: {
environment.systemPackages = with pkgs; [
mosh
mediainfo
youtube-dl
ipset # for sshguard
unstable.vulnix
(pkgs.writers.writeBashBin "vulnix-system" ''
${pkgs.unstable.vulnix}/bin/vulnix --profile /nix/var/nix/profiles/system
'')
];
}

View file

@ -0,0 +1,70 @@
{ config, pkgs, lib, ... }: {
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"prometheus.robi.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = { proxyPass = "http://localhost:${toString config.services.prometheus.port}"; };
};
};
};
services.prometheus = {
enable = true;
# keep data for 30 days
extraFlags = [ "--storage.tsdb.retention.time=30d" ];
exporters = {
node = {
enable = true;
enabledCollectors = [ "systemd" ];
port = 9002;
};
};
scrapeConfigs = [
{
job_name = "netdata";
metrics_path = "/api/v1/allmetrics";
params.format = [ "prometheus" ];
scrape_interval = "5s";
static_configs = [
{
targets = [ "localhost:19999" ];
labels = {
service = "netdata";
server = "robi";
};
}
];
}
{
job_name = "systemd";
static_configs = [{
targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ];
labels = {
service = "node-exporter";
server = "robi";
};
}];
}
{
# see https://www.home-assistant.io/integrations/prometheus/
job_name = "telgraf";
metrics_path = "/metrics";
static_configs = [{
targets = [ "localhost:9273" ];
labels = {
service = "telegraf";
server = "robi";
};
}];
}
];
};
}

View file

@ -0,0 +1,43 @@
{ lib, pkgs, config, ... }: {
users.users.property = { isSystemUser = true; };
systemd.services.property = {
enable = true;
wantedBy = [ "multi-user.target" ];
path = [
(pkgs.python3.withPackages (ps:
with ps; [
flask
selenium
beautifulsoup4
urllib3
sqlalchemy
mysqlclient
pytest
dateparser
geopy
nltk
click
]))
];
serviceConfig = { User = "property"; };
script = ''
FLASK_APP=${<property>}/server.py \
FLASK_RUN_PORT=7888 \
flask run --host 0.0.0.0 \
"$@"
'';
};
services.nginx = {
enable = true;
virtualHosts = {
"property.workhorse.private" = {
locations."/" = { proxyPass = "http://localhost:7888"; };
};
};
};
}

View file

@ -0,0 +1,73 @@
{ config, lib, pkgs, ... }:
with lib;
let
debug = true;
#version = "latest";
# version = "142c079"; # 2 years ago.
# version = "v1.14.0"; # 2 years ago.
version = "v1.13.2"; # 2 years ago.
in
{
virtualisation.oci-containers = {
containers.screeps = {
volumes = [
"/srv/screeps:/screeps"
(optionalString debug "/srv/screeps-tmp:/tmp")
];
environment.TZ = "Europe/Berlin";
image = "screepers/screeps-launcher:${version}";
ports = [ "21025:21025" ];
};
};
systemd.services.docker-screeps =
let
configuration = builtins.toJSON {
steamKey = "keyFromStep3";
version = "latest";
mods = [
#"screepsmod-auth"
#"screepsmod-admin-utils"
#"screepsmod-mongo"
];
bots = {
simplebot = "screepsbot-zeswarm";
};
serverConfig = {
welcomeText = "<h1 style=\"text-align: center;\">My Cool Server</h1>";
constants = {
"TEST_CONSTANT" = 123;
};
tickRate = 1000;
};
};
in
{
unitConfig = {
StartLimitInterval = 200;
StartLimitBurst = 2;
};
serviceConfig = {
Restart = mkForce (if debug then "no" else "always");
RestartSec = 30;
ExecStartPre = [
(toString (pkgs.writers.writeDash "create-screeps-config" ''
mkdir -p /srv/screeps/
chown 1000:1000 -R /srv/screeps
${optionalString debug "mkdir -p /srv/screeps-tmp"}
${optionalString debug "chown 1000:1000 -R /srv/screeps-tmp"}
echo '${configuration}' > /srv/screeps/config.yaml
''))
];
};
};
#networking.firewall.allowedTCPPorts = [ 8123 ];
#networking.firewall.allowedUDPPorts = [ 8123 ];
#networking.firewall.interfaces.wg0.allowedTCPPorts = [ 8123 ];
#networking.firewall.interfaces.wg0.allowedUDPPorts = [ 8123 ];
}

View file

@ -0,0 +1,60 @@
{ config, ... }: {
# + +
# | |
# | |
# v v
# 80, 443 TCP 443 TCP, 10000 UDP
# +--------------+ +---------------------+
# | nginx | 5222, 5347 TCP | |
# | jitsi-meet |<-------------------+| jitsi-videobridge |
# | prosody | | | |
# | jicofo | | +---------------------+
# +--------------+ |
# | +---------------------+
# | | |
# +----------+| jitsi-videobridge |
# | | |
# | +---------------------+
# |
# | +---------------------+
# | | |
# +----------+| jitsi-videobridge |
# | |
# +---------------------+
# This is a one server setup
services.jitsi-meet = {
enable = true;
hostName = "meet.ingolf-wagner.de";
# JItsi COnference FOcus is a server side focus component used in Jitsi Meet conferences.
# https://github.com/jitsi/jicofo
jicofo.enable = true;
# Whether to enable nginx virtual host that will serve the javascript application and act as a proxy for the XMPP server.
# Further nginx configuration can be done by adapting services.nginx.virtualHosts.<hostName>. When this is enabled, ACME
# will be used to retrieve a TLS certificate by default. To disable this, set the
# services.nginx.virtualHosts.<hostName>.enableACME to false and if appropriate do the same for
# services.nginx.virtualHosts.<hostName>.forceSSL.
nginx.enable = true;
# https://github.com/jitsi/jitsi-meet/blob/master/config.js
config = {
enableWelcomePage = false;
defaultLang = "en";
};
# https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js
interfaceConfig = {
SHOW_JITSI_WATERMARK = false;
SHOW_WATERMARK_FOR_GUESTS = false;
};
};
networking.firewall = {
allowedTCPPorts = [ 80 443 ];
allowedUDPPorts = [ 10000 ];
};
}

View file

@ -0,0 +1,5 @@
{
services.opentracker = {
enable = true;
};
}

View file

@ -0,0 +1,111 @@
{ lib, pkgs, config, ... }:
let
uiPort = 9099;
announceIp = "10.23.42.111";
peerPort = 51433;
in
{
containers.sync-torrent = {
# mount host folders
bindMounts = {
media = {
hostPath = "/media/new";
mountPoint = "/media"; # must be here otherwise transmission can't see the folder
isReadOnly = false;
};
lib = {
hostPath = "/srv/sync-torrent";
mountPoint = "/var/lib/transmission";
isReadOnly = false;
};
};
autoStart = true;
config = { config, pkgs, lib, ... }: {
system.stateVersion = "22.11";
services.journald.extraConfig = "SystemMaxUse=1G";
services.transmission = {
enable = true;
settings = {
download-dir = "/media";
incomplete-dir = "/var/lib/transmission/incomplete"; # todo put this somewhere with frequent snapshots but low keep.
incomplete-dir-enabled = true;
message-level = 1;
umask = 2;
rpc-whitelist-enabled = false;
rpc-host-whitelist-enabled = false;
rpc-port = uiPort;
rpc-enable = true;
rpc-bind-address = "0.0.0.0";
# "normal" speed limits
speed-limit-down-enabled = false;
speed-limit-down = 800;
speed-limit-up-enabled = true;
speed-limit-up = 3000;
upload-slots-per-torrent = 8;
# Queuing
# When true, Transmission will only download
# download-queue-size non-stalled torrents at once.
download-queue-enabled = true;
download-queue-size = 3;
# When true, torrents that have not shared data for
# queue-stalled-minutes are treated as 'stalled'
# and are not counted against the queue-download-size
# and seed-queue-size limits.
queue-stalled-enabled = true;
queue-stalled-minutes = 60;
# When true. Transmission will only seed seed-queue-size
# non-stalled torrents at once.
seed-queue-enabled = false;
seed-queue-size = 10;
# Enable UPnP or NAT-PMP.
peer-port = peerPort;
port-forwarding-enabled = false;
announce-ip = announceIp;
announce-ip-enabled = true;
# Start torrents as soon as they are added
start-added-torrents = true;
};
};
};
};
# open ports for logging
#networking.firewall.interfaces."ve-torrent".allowedTCPPorts =
# [ 5044 12304 12305 ];
#networking.firewall.interfaces."ve-torrent".allowedUDPPorts =
# [ 5044 12304 12305 ];
# host nginx setup
# ----------------
# curl -H "Host: sync.robi.private" https://robi.private/ < will work
# curl -H "Host: sync.robi.private" https://144.76.13.147/ < wont work
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"sync.${config.networking.hostName}.private" = {
extraConfig = ''
allow ${config.tinc.private.subnet};
deny all;
'';
locations."/" = {
proxyPass = "http://127.0.0.1:${toString uiPort}";
};
};
};
};
}

View file

@ -0,0 +1,19 @@
{ config, lib, pkgs, ... }: {
services.taskserver = {
enable = true;
fqdn = "taskd.ingolf-wagner.de";
listenHost = "0.0.0.0";
requestLimit = 104857600;
trust = "strict";
dataDir = "/var/lib/taskserver";
organisations."1337".users = [ "palo" "beta" ];
ciphers = "SECURE256";
};
networking.firewall.allowedTCPPorts = [ config.services.taskserver.listenPort ];
networking.firewall.allowedUDPPorts = [ config.services.taskserver.listenPort ];
backup.dirs = [ config.services.taskserver.dataDir ];
}

View file

@ -0,0 +1,28 @@
{
services.telegraf = {
enable = true;
extraConfig = {
outputs.prometheus_client = {
listen = ":9273";
metric_version = 2;
};
# https://github.com/influxdata/telegraf/tree/master/plugins/inputs < all them plugins
inputs = {
cpu = {
percpu = true;
totalcpu = true;
};
disk = { };
diskio = { };
kernel = { };
mem = { };
processes = { };
netstat = { };
net = { };
system = { };
systemd_units = { };
nginx.urls = [ "http://localhost/nginx_status" ];
};
};
};
}

View file

@ -0,0 +1,109 @@
{ config, pkgs, ... }:
let
inherit (config.services.dendrite.settings.global) server_name;
nginx-vhost = "matrix.terranix.org";
element-web-terranix.org =
pkgs.runCommand "element-web-with-config"
{
nativeBuildInputs = [ pkgs.buildPackages.jq ];
} ''
cp -r ${pkgs.element-web} $out
chmod -R u+w $out
jq '."default_server_config"."m.homeserver" = { "base_url": "https://${nginx-vhost}:443", "server_name": "${server_name}" }' \
> $out/config.json < ${pkgs.element-web}/config.json
ln -s $out/config.json $out/config.${nginx-vhost}.json
'';
in
{
# $ nix-shell -p dendrite --run 'generate-keys --private-key /tmp/key'
sops.secrets.matrix-server-key = { };
services.dendrite = {
enable = true;
httpPort = 8043;
settings = {
global = {
server_name = "terranix.org";
# `private_key` has the type `path`
# prefix a `/` to make `path` happy
private_key = "/$CREDENTIALS_DIRECTORY/matrix-server-key";
trusted_third_party_id_servers = [
"matrix.org"
"vector.im"
"xaos.space"
"lassul.us"
"thalheim.io"
"nixos.org"
];
metrics.enabled = false;
};
logging = [
{
type = "std";
level = "warn";
}
];
client_api = {
registration_disabled = true;
rate_limiting.enabled = false;
# set only for the first admin account, than remove.
#registration_shared_secret = ""; # disable once first admin account is created
};
media_api = {
dynamic_thumbnails = true;
};
mscs = {
mscs = [ "msc2836" "msc2946" ];
};
sync_api = {
real_ip_header = "X-Real-IP";
};
federation_api = {
key_perspectives = [
{
server_name = "matrix.org";
keys = [
{
key_id = "ed25519:auto";
public_key = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
}
{
key_id = "ed25519:a_RXGa";
public_key = "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ";
}
];
}
];
prefer_direct_fetch = false;
};
};
};
systemd.services.dendrite.serviceConfig.LoadCredential = [
"matrix-server-key:${config.sops.secrets.matrix-server-key.path}"
];
services.nginx.virtualHosts.${nginx-vhost} = {
forceSSL = true;
enableACME = true;
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 600;
'';
locations."/_matrix".proxyPass = "http://127.0.0.1:${toString config.services.dendrite.httpPort}";
# for remote admin access
locations."/_synapse".proxyPass = "http://127.0.0.1:${toString config.services.dendrite.httpPort}";
locations."/".root = element-web-terranix.org;
};
services.nginx.virtualHosts.${server_name} = {
locations."= /.well-known/matrix/server".alias =
pkgs.writeText "matrix-server" (builtins.toJSON { "m.server" = "${nginx-vhost}:443"; });
locations."= /.well-known/matrix/client".alias =
pkgs.writeText "matrix-client" (builtins.toJSON { "m.homeserver".base_url = "https://${nginx-vhost}"; });
};
}

View file

@ -0,0 +1,32 @@
{ config, pkgs, lib, ... }: {
services.vaultwarden = {
enable = true;
# backupDir =
config = {
domain = "https://bitwarden.ingolf-wagner.de";
signupsAllowed = false;
rocketPort = 8222;
rocketLog = "critical";
};
};
backup.dirs = [ "/var/lib/bitwarden_rs" ];
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"bitwarden.ingolf-wagner.de" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${
toString config.services.vaultwarden.config.rocketPort
}";
};
};
};
};
}

View file

@ -0,0 +1,45 @@
{ config, pkgs, ... }:
# To create a sign at the door
# "Sorry Doorbell is broken, please scan this QR Code
#
# create QR Code with:
# qrencode -o ./test.png http://ring.ingolf-wagner.de
#
# for secure urls check
# https://www.nginx.com/blog/securing-urls-secure-link-module-nginx-plus/
{
sops.secrets.ringPushover = {
owner = config.services.webhook.user;
};
services.webhook = {
enable = true;
hooks = {
ring = {
execute-command =
let
script = pkgs.writers.writeBash "ring-script" ''
. ${config.sops.secrets.ringPushover.path}
${pkgs.curl}/bin/curl -s \
--form-string "token=$API_KEY" \
--form-string "user=$USER_KEY" \
--form-string "title=Klingeling" \
--form-string "message=Jemand an der Tür" \
https://api.pushover.net/1/messages.json
'';
in
toString script;
response-message = "It's ringing";
};
};
};
services.nginx.virtualHosts."ring.ingolf-wagner.de" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.webhook.port}/${config.services.webhook.urlPrefix}/ring";
};
};
}

View file

@ -0,0 +1,38 @@
{ config, pkgs, lib, ... }:
# how to setup a relay
# * ssh on the maching
# * sudo -u weechat screen -r
# /set relay.network.password "mypassword"
# /relay add weechat 10000
{
# configure weechat
services.weechat = { enable = true; };
# configure bitlbee
services.bitlbee = {
enable = true;
libpurple_plugins = [
#pkgs.pidgin-otr
#pkgs.purple-facebook
#pkgs.purple-discord
#pkgs.purple-matrix
#pkgs.purple-hangouts
#pkgs.pidgin-latex
#pkgs.pidgin-opensteamworks
#pkgs.pidgin-skypeweb
pkgs.telegram-purple
#pkgs.purple-lurch
];
plugins =
[ pkgs.bitlbee-facebook pkgs.bitlbee-steam pkgs.bitlbee-mastodon ];
};
# otherwise xterm is the only thing that works
environment.systemPackages = [ pkgs.rxvt_unicode ];
backup.dirs = [ config.services.weechat.root ];
}

View file

@ -18,7 +18,7 @@
cert = toString config.sops.secrets.syncthing_cert.path;
key = toString config.sops.secrets.syncthing_key.path;
folders = {
settings.folders = {
# on media hard drive (not encrypted)
# -----------------------------------
#borg-mirror = {
@ -38,7 +38,7 @@
lost-fotos = {
enable = true;
path = "/media/syncthing/lost-fotos.ct";
rescanInterval = 40 * 24 * 3600;
rescanIntervalS = 40 * 24 * 3600;
};
#media = {
# enable = true;
@ -49,14 +49,14 @@
#};
music-projects = {
enable = true;
watch = true;
#watch = true;
path = "/media/syncthing/music-projects";
};
nextcloud_backup = {
enable = true;
watch = true;
#watch = true;
path = "/media/syncthing/nextcloud_backup";
rescanInterval = 23 * 3600;
rescanIntervalS = 23 * 3600;
};
};
};

View file

@ -242,8 +242,8 @@ in
enable = true;
package = pkgs.nextcloud27;
autoUpdateApps.enable = true;
#nginx.enable = true;
enableBrokenCiphersForSSE = false; # see https://github.com/NixOS/nixpkgs/pull/198470
# nginx.enable = true;
# enableBrokenCiphersForSSE = false; # see https://github.com/NixOS/nixpkgs/pull/198470
hostName = "nextcloud.ingolf-wagner.de";
logLevel = 2;
https = true;