diff --git a/flake.nix b/flake.nix
index 173b6c6..afdced8 100644
--- a/flake.nix
+++ b/flake.nix
@@ -11,6 +11,7 @@
imports = [
modules/authelia.nix
modules/backup.nix
+ modules/deluge.nix
modules/hledger.nix
modules/home-assistant.nix
modules/jellyfin.nix
@@ -19,6 +20,8 @@
modules/nextcloud-server.nix
modules/nginx.nix
modules/ssl.nix
+ modules/tinyproxy.nix
+ modules/vpn.nix
];
};
diff --git a/modules/deluge.nix b/modules/deluge.nix
new file mode 100644
index 0000000..d287f1c
--- /dev/null
+++ b/modules/deluge.nix
@@ -0,0 +1,206 @@
+{ config, pkgs, lib, ... }:
+
+let
+ cfg = config.shb.deluge;
+
+ fqdn = "${cfg.subdomain}.${cfg.domain}";
+in
+{
+ options.shb.deluge = {
+ enable = lib.mkEnableOption "selfhostblocks.deluge";
+
+ subdomain = lib.mkOption {
+ type = lib.types.str;
+ description = "Subdomain under which deluge will be served.";
+ example = "ha";
+ };
+
+ domain = lib.mkOption {
+ type = lib.types.str;
+ description = "domain under which deluge will be served.";
+ example = "mydomain.com";
+ };
+
+ daemonPort = lib.mkOption {
+ type = lib.types.int;
+ description = "Deluge daemon port";
+ default = 58846;
+ };
+
+ daemonListenPorts = lib.mkOption {
+ type = lib.types.listOf lib.types.int;
+ description = "Deluge daemon listen ports";
+ default = [ 6881 6889 ];
+ };
+
+ webPort = lib.mkOption {
+ type = lib.types.int;
+ description = "Deluge web port";
+ default = 8112;
+ };
+
+ proxyPort = lib.mkOption {
+ description = lib.mdDoc "If not null, sets up a deluge to forward all traffic to the Proxy listening at that port.";
+ type = lib.types.nullOr lib.types.int;
+ default = null;
+ };
+
+ downloadLocation = lib.mkOption {
+ type = lib.types.str;
+ description = "Folder where torrents gets downloaded";
+ example = "/srv/torrents";
+ };
+
+ oidcEndpoint = lib.mkOption {
+ type = lib.types.str;
+ description = "OIDC endpoint for SSO";
+ example = "https://authelia.example.com";
+ };
+
+ sopsFile = lib.mkOption {
+ type = lib.types.path;
+ description = "Sops file location.";
+ example = "secrets/torrent.yaml";
+ };
+
+ additionalPlugins = lib.mkOption {
+ type = lib.types.attrsOf lib.types.path;
+ description = "Location of additional plugins.";
+ default = {};
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ services.deluge = {
+ enable = true;
+ declarative = true;
+ openFirewall = true;
+ config = {
+ download_location = cfg.downloadLocation;
+ max_upload_speed = -1.0;
+ allow_remote = true;
+ daemon_port = cfg.daemonPort;
+ listen_ports = cfg.daemonListenPorts;
+ proxy = lib.optionalAttrs (cfg.proxyPort != null) {
+ force_proxy = true;
+ hostname = "127.0.0.1";
+ port = cfg.proxyPort;
+ proxy_hostnames = true;
+ proxy_peer_connections = true;
+ proxy_tracker_connections = true;
+ type = 4; # HTTP
+ };
+ };
+ authFile = "/run/secrets/deluge/auth";
+
+ web.enable = true;
+ web.port = cfg.webPort;
+ };
+
+
+ systemd.tmpfiles.rules = lib.attrsets.mapAttrsToList (name: path:
+ "L+ ${config.services.deluge.dataDir}/.config/deluge/plugins/${name} - - - - ${path}"
+ ) cfg.additionalPlugins;
+
+ sops.secrets."deluge/auth" = {
+ inherit (cfg) sopsFile;
+ mode = "0440";
+ owner = config.services.deluge.user;
+ group = config.services.deluge.group;
+ restartUnits = [ "deluged.service" "delugeweb.service" ];
+ };
+
+ services.nginx = {
+ enable = true;
+
+ virtualHosts.${fqdn} = {
+ forceSSL = true;
+ sslCertificate = "/var/lib/acme/${cfg.domain}/cert.pem";
+ sslCertificateKey = "/var/lib/acme/${cfg.domain}/key.pem";
+
+ # Taken from https://github.com/authelia/authelia/issues/178
+ locations."/".extraConfig = ''
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+ add_header X-Content-Type-Options nosniff;
+ add_header X-Frame-Options "SAMEORIGIN";
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive";
+ add_header X-Download-Options noopen;
+ add_header X-Permitted-Cross-Domain-Policies none;
+
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_cache_bypass $http_upgrade;
+
+ auth_request /authelia;
+ auth_request_set $user $upstream_http_remote_user;
+ auth_request_set $groups $upstream_http_remote_groups;
+ proxy_set_header X-Forwarded-User $user;
+ proxy_set_header X-Forwarded-Groups $groups;
+ # TODO: Are those needed?
+ # auth_request_set $name $upstream_http_remote_name;
+ # auth_request_set $email $upstream_http_remote_email;
+ # proxy_set_header Remote-Name $name;
+ # proxy_set_header Remote-Email $email;
+ # TODO: Would be nice to have this working, I think.
+ # set $new_cookie $http_cookie;
+ # if ($http_cookie ~ "(.*)(?:^|;)\s*example\.com\.session\.id=[^;]+(.*)") {
+ # set $new_cookie $1$2;
+ # }
+ # proxy_set_header Cookie $new_cookie;
+
+ auth_request_set $redirect $scheme://$http_host$request_uri;
+ error_page 401 =302 ${cfg.oidcEndpoint}?rd=$redirect;
+ error_page 403 = ${cfg.oidcEndpoint}/error/403;
+
+ proxy_pass http://127.0.0.1:${toString config.services.deluge.web.port};
+ '';
+
+ # Virtual endpoint created by nginx to forward auth requests.
+ locations."/authelia".extraConfig = ''
+ internal;
+ proxy_pass ${cfg.oidcEndpoint}/api/verify;
+
+ proxy_set_header X-Forwarded-Host $host;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header X-Original-URL $scheme://$host$request_uri;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Content-Length "";
+ proxy_pass_request_body off;
+ # TODO: Would be nice to be able to enable this.
+ # proxy_ssl_verify on;
+ # proxy_ssl_trusted_certificate "/etc/ssl/certs/DST_Root_CA_X3.pem";
+ proxy_ssl_protocols TLSv1.2;
+ proxy_ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
+ proxy_ssl_verify_depth 2;
+ proxy_ssl_server_name on;
+ '';
+
+ };
+ };
+
+ shb.authelia.rules = [
+ {
+ domain = fqdn;
+ policy = "two_factor";
+ subject = ["group:deluge_user"];
+ }
+ ];
+
+ users.groups.deluge = {
+ members = [ "backup" ];
+ };
+
+ shb.backup.instances.deluge = {
+ sourceDirectories = [
+ config.services.deluge.dataDir
+ ];
+ };
+ };
+}
diff --git a/modules/tinyproxy.nix b/modules/tinyproxy.nix
new file mode 100644
index 0000000..ca400f1
--- /dev/null
+++ b/modules/tinyproxy.nix
@@ -0,0 +1,147 @@
+# Inspired from https://github.com/NixOS/nixpkgs/pull/231152 but made it so we can have multiple instances.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.tinyproxy;
+
+ mkValueStringTinyproxy = with lib; v:
+ if true == v then "yes"
+ else if false == v then "no"
+ else generators.mkValueStringDefault {} v;
+
+ mkKeyValueTinyproxy = {
+ mkValueString ? mkValueStringDefault {}
+ }: sep: k: v:
+ if null == v then ""
+ else "${lib.strings.escape [sep] k}${sep}${mkValueString v}";
+
+ settingsFormat = (pkgs.formats.keyValue {
+ mkKeyValue = mkKeyValueTinyproxy {
+ mkValueString = mkValueStringTinyproxy;
+ } " ";
+ listsAsDuplicateKeys= true;
+ });
+
+ configFile = name: cfg: settingsFormat.generate "tinyproxy-${name}.conf" cfg.settings;
+
+ someEnabled = any (mapAttrsToList (name: c: c.enable) cfg);
+in
+{
+ options =
+ let
+ instanceOption = types.submodule {
+ options = {
+ enable = mkEnableOption (lib.mdDoc "Tinyproxy daemon");
+
+ package = mkPackageOptionMD pkgs "tinyproxy" {};
+
+ dynamicBindFile = mkOption {
+ description = lib.mdDoc ''
+ File holding the IP to bind to.
+ '';
+ default = "";
+ };
+
+ settings = mkOption {
+ description = lib.mdDoc ''
+ Configuration for [tinyproxy](https://tinyproxy.github.io/).
+ '';
+ default = { };
+ example = literalExpression ''{
+ Port 8888;
+ Listen 127.0.0.1;
+ Timeout 600;
+ Allow 127.0.0.1;
+ Anonymous = ['"Host"' '"Authorization"'];
+ ReversePath = '"/example/" "http://www.example.com/"';
+ }'';
+ type = types.submodule ({name, ...}: {
+ freeformType = settingsFormat.type;
+ options = {
+ Listen = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = lib.mdDoc ''
+ Specify which address to listen to.
+ '';
+ };
+ Port = mkOption {
+ type = types.int;
+ default = 8888;
+ description = lib.mdDoc ''
+ Specify which port to listen to.
+ '';
+ };
+ Anonymous = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = lib.mdDoc ''
+ If an `Anonymous` keyword is present, then anonymous proxying is enabled. The
+ headers listed with `Anonymous` are allowed through, while all others are denied.
+ If no Anonymous keyword is present, then all headers are allowed through. You must
+ include quotes around the headers.
+ '';
+ };
+ Filter = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = lib.mdDoc ''
+ Tinyproxy supports filtering of web sites based on URLs or domains. This option
+ specifies the location of the file containing the filter rules, one rule per line.
+ '';
+ };
+ };
+ });
+ };
+ };
+ };
+ in
+ {
+ services.tinyproxy = mkOption {
+ description = "Tinyproxy instances.";
+ default = {};
+ type = types.attrsOf instanceOption;
+ };
+ };
+
+ config = {
+ systemd.services =
+ let
+ instanceConfig = name: c: mkIf c.enable {
+ "tinyproxy-${name}" = {
+ description = "TinyProxy daemon - instance ${name}";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ User = "tinyproxy";
+ Group = "tinyproxy";
+ Type = "simple";
+ ExecStart = "${getExe c.package} -d -c /etc/tinyproxy/${name}.conf";
+ ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
+ KillSignal = "SIGINT";
+ TimeoutStopSec = "30s";
+ Restart = "on-failure";
+ ConfigurationDirectory = "tinyproxy";
+ };
+ preStart = concatStringsSep "\n" ([
+ "cat ${configFile name c} > /etc/tinyproxy/${name}.conf"
+ ] ++ optionals (c.dynamicBindFile != "") [
+ "echo -n 'Bind ' >> /etc/tinyproxy/${name}.conf"
+ "cat ${c.dynamicBindFile} >> /etc/tinyproxy/${name}.conf"
+ ]);
+ };
+ };
+ in
+ mkMerge (mapAttrsToList instanceConfig cfg);
+
+ users.users.tinyproxy = {
+ group = "tinyproxy";
+ isSystemUser = true;
+ };
+ users.groups.tinyproxy = {};
+ };
+
+ meta.maintainers = with maintainers; [ tcheronneau ];
+}
diff --git a/modules/vpn.nix b/modules/vpn.nix
new file mode 100644
index 0000000..90ad379
--- /dev/null
+++ b/modules/vpn.nix
@@ -0,0 +1,268 @@
+{ config, pkgs, lib, ... }:
+
+let
+ cfg = config.shb.vpn;
+
+ quoteEach = lib.concatMapStrings (x: ''"${x}"'');
+
+ nordvpnConfig =
+ { name
+ , dev
+ , authFile
+ , remoteServerIP
+ , dependentServices ? []
+ }: ''
+ client
+ dev ${dev}
+ proto tcp
+ remote ${remoteServerIP} 443
+ resolv-retry infinite
+ 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 ${authFile}
+ verb 3
+ pull
+ fast-io
+ cipher AES-256-CBC
+ auth SHA512
+
+ script-security 2
+ route-noexec
+ route-up ${routeUp name dependentServices}/bin/routeUp.sh
+ down ${routeDown name dependentServices}/bin/routeDown.sh
+
+
+ -----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-----
+
+ key-direction 1
+
+ #
+ # 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-----
+
+ '';
+
+ routeUp = name: dependentServices: pkgs.writeShellApplication {
+ name = "routeUp.sh";
+
+ runtimeInputs = [ pkgs.iproute2 pkgs.systemd ];
+
+ text = ''
+ echo "dev=''${dev:?}"
+ echo "ifconfig_local=''${ifconfig_local:?}"
+ echo "route_vpn_gateway=''${route_vpn_gateway:?}"
+
+ ip rule add from "''${ifconfig_local:?}/32" table ${name}
+ ip rule add to "''${route_vpn_gateway:?}/32" table ${name}
+
+ ip route add default via "''${route_vpn_gateway:?}" dev "''${dev:?}" table ${name}
+ ip route flush cache
+
+ echo "''${ifconfig_local:?}" > /run/openvpn/${name}/ifconfig_local
+
+ dependencies=(${quoteEach dependentServices})
+ for i in "''${dependencies[@]}"; do
+ systemctl restart "$i"
+ done
+ '';
+ };
+
+ routeDown = name: dependentServices: pkgs.writeShellApplication {
+ name = "routeDown.sh";
+
+ runtimeInputs = [ pkgs.iproute2 pkgs.systemd ];
+
+ text = ''
+ echo "dev=''${dev:?}"
+ echo "ifconfig_local=''${ifconfig_local:?}"
+ echo "route_vpn_gateway=''${route_vpn_gateway:?}"
+
+ ip rule del from "''${ifconfig_local:?}/32" table ${name}
+ ip rule del to "''${route_vpn_gateway:?}/32" table ${name}
+
+ ip route del default via "''${route_vpn_gateway:?}" dev "''${dev:?}" table ${name}
+ ip route flush cache
+
+ rm /run/openvpn/${name}/ifconfig_local
+
+ dependencies=(${quoteEach dependentServices})
+ for i in "''${dependencies[@]}"; do
+ systemctl stop "$i"
+ done
+ '';
+ };
+
+ someEnabled = lib.any (lib.mapAttrsToList (name: c: c.enable) cfg);
+in
+{
+ options =
+ let
+ instanceOption = lib.types.submodule {
+ options = {
+ enable = lib.mkEnableOption (lib.mdDoc "OpenVPN config");
+
+ package = lib.mkPackageOptionMD pkgs "openvpn" {};
+
+ provider = lib.mkOption {
+ description = lib.mdDoc "VPN provider, if given uses ready-made configuration.";
+ type = lib.types.nullOf (lib.types.enum [ "nordvpn" ]);
+ default = null;
+ };
+
+ dev = lib.mkOption {
+ description = lib.mdDoc "Name of the interface.";
+ type = lib.types.str;
+ example = "tun0";
+ };
+
+ remoteServerIP = lib.mkOption {
+ description = lib.mdDoc "IP of the VPN server to connect to.";
+ type = lib.types.str;
+ };
+
+ sopsFile = lib.mkOption {
+ description = lib.mdDoc "Location of file holding authentication secrets for provider.";
+ type = lib.types.anything;
+ };
+
+ proxyPort = lib.mkOption {
+ description = lib.mdDoc "If not null, sets up a proxy that listens on the given port and sends traffic to the VPN.";
+ type = lib.types.nullOr lib.types.int;
+ default = null;
+ };
+ };
+ };
+ in
+ {
+ shb.vpn = lib.mkOption {
+ description = "OpenVPN instances.";
+ default = {};
+ type = lib.types.attrsOf instanceOption;
+ };
+ };
+
+ config = {
+ services.openvpn.servers =
+ let
+ instanceConfig = name: c: lib.mkIf c.enable {
+ ${name} = {
+ autoStart = true;
+
+ up = "mkdir -p /run/openvpn/${name}";
+
+ config = nordvpnConfig {
+ inherit name;
+ inherit (c) dev remoteServerIP;
+ authFile = config.sops.secrets."${name}/auth".path;
+ dependentServices = lib.optional (c.proxyPort != null) "tinyproxy-${name}.service";
+ };
+ };
+ };
+ in
+ lib.mkMerge (lib.mapAttrsToList instanceConfig cfg);
+
+ sops.secrets =
+ let
+ instanceConfig = name: c: lib.mkIf c.enable {
+ "${name}/auth" = {
+ sopsFile = c.sopsFile;
+ mode = "0440";
+ restartUnits = [ "openvpn-${name}" ];
+ };
+ };
+ in
+ lib.mkMerge (lib.mapAttrsToList instanceConfig cfg);
+
+ networking.iproute2.enable = true;
+ networking.iproute2.rttablesExtraConfig =
+ lib.concatStringsSep "\n" (lib.mapAttrsToList (name: c: "10 ${name}") cfg);
+
+ services.tinyproxy =
+ let
+ instanceConfig = name: c: lib.mkIf (c.enable && c.proxyPort != null) {
+ ${name} = {
+ enable = true;
+ # package = pkgs.tinyproxy.overrideAttrs (old: {
+ # withDebug = false;
+ # patches = old.patches ++ [
+ # (pkgs.fetchpatch {
+ # name = "";
+ # url = "https://github.com/tinyproxy/tinyproxy/pull/494/commits/2532ba09896352b31f3538d7819daa1fc3f829f1.patch";
+ # sha256 = "sha256-Q0MkHnttW8tH3+hoCt9ACjHjmmZQgF6pC/menIrU0Co=";
+ # })
+ # ];
+ # });
+ dynamicBindFile = "/run/openvpn/${name}/ifconfig_local";
+ settings = {
+ Port = c.proxyPort;
+ Listen = "127.0.0.1";
+ Syslog = "On";
+ LogLevel = "Info";
+ Allow = [ "127.0.0.1" "::1" ];
+ ViaProxyName = ''"tinyproxy"'';
+ };
+ };
+ };
+ in
+ lib.mkMerge (lib.mapAttrsToList instanceConfig cfg);
+ };
+}