feat(copyparty): trust reverse proxy headers
This commit was merged in pull request #54.
This commit is contained in:
73
flake.nix
73
flake.nix
@@ -118,6 +118,79 @@
|
||||
touch $out
|
||||
'';
|
||||
|
||||
copyparty-reverse-proxy-trust-config =
|
||||
let
|
||||
pikachuConfig = self.nixosConfigurations.pikachu.config;
|
||||
serviceConfig = pikachuConfig.containers.copyparty.config.systemd.services.copyparty.serviceConfig;
|
||||
configScript = builtins.readFile serviceConfig.ExecStartPre;
|
||||
plainConfig =
|
||||
(lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs.my = self.lib;
|
||||
modules = [
|
||||
./modules/services/copyparty.nix
|
||||
{
|
||||
modules.services.copyparty = {
|
||||
enable = true;
|
||||
passwordFile = "/run/password";
|
||||
volumes."/" = {
|
||||
path = "/srv/files";
|
||||
access.A = [ "alexion" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
}).config;
|
||||
plainScript = builtins.readFile plainConfig.systemd.services.copyparty.serviceConfig.ExecStartPre;
|
||||
in
|
||||
pkgs.runCommand "copyparty-reverse-proxy-trust-config-check" { } ''
|
||||
${
|
||||
lib.optionalString (
|
||||
!(
|
||||
lib.hasInfix " rproxy: 1" configScript
|
||||
&& lib.hasInfix " xff-hdr: x-forwarded-for" configScript
|
||||
&& lib.hasInfix " xff-src: 10.23.20.109/32" configScript
|
||||
&& !(lib.hasInfix "rproxy" plainScript)
|
||||
&& !(lib.hasInfix "xff-hdr" plainScript)
|
||||
&& !(lib.hasInfix "xff-src" plainScript)
|
||||
)
|
||||
) "exit 1"
|
||||
}
|
||||
touch $out
|
||||
'';
|
||||
|
||||
copyparty-reverse-proxy-trust-requires-source =
|
||||
let
|
||||
testConfig =
|
||||
(lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs.my = self.lib;
|
||||
modules = [
|
||||
./modules/services/copyparty.nix
|
||||
{
|
||||
modules.services.copyparty = {
|
||||
enable = true;
|
||||
passwordFile = "/run/password";
|
||||
reverseProxy.enable = true;
|
||||
volumes."/" = {
|
||||
path = "/srv/files";
|
||||
access.A = [ "alexion" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
}).config;
|
||||
hasFailedTrustedSourcesAssertion = lib.any (
|
||||
assertion:
|
||||
assertion.assertion == false
|
||||
&& assertion.message == "modules.services.copyparty.reverseProxy.trustedSources must name at least one trusted proxy source when reverse-proxy support is enabled."
|
||||
) testConfig.assertions;
|
||||
in
|
||||
pkgs.runCommand "copyparty-reverse-proxy-trust-requires-source-check" { } ''
|
||||
${lib.optionalString (!hasFailedTrustedSourcesAssertion) "exit 1"}
|
||||
touch $out
|
||||
'';
|
||||
|
||||
reverse-proxy-basic-route =
|
||||
let
|
||||
testConfig =
|
||||
|
||||
@@ -43,6 +43,10 @@ in
|
||||
enable = true;
|
||||
inherit (cfg) accountName stateDir;
|
||||
passwordFile = "/run/secrets/${cfg.passwordSecret}";
|
||||
reverseProxy = {
|
||||
enable = cfg.reverseProxy.enable;
|
||||
inherit (cfg.reverseProxy) trustedSources;
|
||||
};
|
||||
volumes = lib.mapAttrs (_urlPath: volume: {
|
||||
inherit (volume) path access flags;
|
||||
}) cfg.volumes;
|
||||
@@ -77,6 +81,12 @@ in
|
||||
description = "Host path bind-mounted as Copyparty's state directory.";
|
||||
};
|
||||
|
||||
reverseProxy.trustedSources = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "IP ranges that may supply Copyparty's forwarded client IP header.";
|
||||
};
|
||||
|
||||
volumes = lib.mkOption {
|
||||
type = lib.types.attrsOf volumeType;
|
||||
default = { };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
# pikachu — AZW ME Pro server.
|
||||
# Disk layout is in ./disk.nix.
|
||||
# `fileSystems` for the root disk are derived from it.
|
||||
@@ -91,6 +91,7 @@
|
||||
enable = true;
|
||||
vlan = 20;
|
||||
mac = "02:98:64:5a:07:4f";
|
||||
endpoint.address = "10.23.20.109";
|
||||
};
|
||||
|
||||
guests.copyparty = {
|
||||
@@ -103,6 +104,7 @@
|
||||
enable = true;
|
||||
host = "files.alexion.dev";
|
||||
port = 3923;
|
||||
trustedSources = [ "${config.modules.network.endpoints."reverse-proxy".address}/32" ];
|
||||
};
|
||||
volumes = {
|
||||
"/inbox" = {
|
||||
|
||||
@@ -48,6 +48,14 @@ let
|
||||
''}
|
||||
'';
|
||||
|
||||
renderTrustedSource = source: " xff-src: ${source}";
|
||||
|
||||
renderedReverseProxy = lib.optionalString cfg.reverseProxy.enable ''
|
||||
rproxy: 1
|
||||
xff-hdr: x-forwarded-for
|
||||
${lib.concatStringsSep "\n" (map renderTrustedSource cfg.reverseProxy.trustedSources)}
|
||||
'';
|
||||
|
||||
renderedVolumes = lib.concatStringsSep "\n" (lib.mapAttrsToList renderVolume cfg.volumes);
|
||||
|
||||
configScript = pkgs.writeShellScript "copyparty-config" ''
|
||||
@@ -68,7 +76,7 @@ let
|
||||
ses-db: ${cfg.stateDir}/sessions.db
|
||||
chpw-db: ${cfg.stateDir}/chpw.json
|
||||
shr-db: ${cfg.stateDir}/shares.db
|
||||
|
||||
${renderedReverseProxy}
|
||||
[accounts]
|
||||
${cfg.accountName}: $password
|
||||
|
||||
@@ -128,6 +136,16 @@ in
|
||||
default = { };
|
||||
description = "Copyparty volumes keyed by their URL path.";
|
||||
};
|
||||
|
||||
reverseProxy = {
|
||||
enable = lib.mkEnableOption "reverse-proxy trust settings for Copyparty";
|
||||
|
||||
trustedSources = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "IP ranges that may supply Copyparty's forwarded client IP header.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
@@ -136,6 +154,10 @@ in
|
||||
assertion = cfg.volumes != { };
|
||||
message = "modules.services.copyparty.volumes must declare at least one served volume.";
|
||||
}
|
||||
{
|
||||
assertion = !cfg.reverseProxy.enable || cfg.reverseProxy.trustedSources != [ ];
|
||||
message = "modules.services.copyparty.reverseProxy.trustedSources must name at least one trusted proxy source when reverse-proxy support is enabled.";
|
||||
}
|
||||
];
|
||||
|
||||
users.users.${cfg.user} = {
|
||||
|
||||
Reference in New Issue
Block a user