diff options
author | Linnnus <[email protected]> | 2023-10-21 19:09:27 +0200 |
---|---|---|
committer | Linnnus <[email protected]> | 2023-10-21 19:09:27 +0200 |
commit | cb0697f14a5a13985710e8e2824158c038e03d83 (patch) | |
tree | 8a68731cddfea39835b12bf95f5fb8e64ccb536b | |
parent | d8727c5a832fa760e1f62a0fb2aa00b6667727af (diff) |
cloudflare-proxy: Block non-cloudflare HTTP(S) connections
-rw-r--r-- | hosts/ahmed/configuration.nix | 6 | ||||
-rw-r--r-- | metadata.toml | 4 | ||||
-rw-r--r-- | modules/nixos/cloudflare-proxy/default.nix | 95 |
3 files changed, 90 insertions, 15 deletions
diff --git a/hosts/ahmed/configuration.nix b/hosts/ahmed/configuration.nix index 945f29c..f95f449 100644 --- a/hosts/ahmed/configuration.nix +++ b/hosts/ahmed/configuration.nix @@ -3,6 +3,7 @@ { config, pkgs, + metadata, ... }: { imports = [ @@ -82,7 +83,10 @@ networking.firewall.allowedTCPPorts = [80 443]; # We are running behind CF proxy. - modules.cloudflare-proxy.enable = true; + modules.cloudflare-proxy = { + enable = true; + firewall.IPv4Whitelist = [metadata.hosts.muhammed.ipAddress]; + }; # Use as main driver temporarily. # modules.graphics.enable = true; diff --git a/metadata.toml b/metadata.toml index 1d804c8..7c6887b 100644 --- a/metadata.toml +++ b/metadata.toml @@ -1,9 +1,9 @@ # This file specifies metadata for each host. [hosts.ahmed] -ipAddress = "192.168.68.126" +ipAddress = "192.168.68.222" sshPubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL3/DjOuKMN18fs/0ZHI3kKLHGytXOFEDBbx+09ZrS3G" [hosts.muhammed] -ipAddress = "???" +ipAddress = "192.168.68.108" sshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDcmUCfFA/arYpT0zBWoOXcyxN5bgk5cMrWgTIol5RsHB82VzoS+LG3IV4IwBz4QALaCj5DlhfbasGKMkFRgFvLerEtBleIb58RtOXIOf6TIUaqpyHB3h2CjdwrbmyjjWEl9W2BTpadrR5uPr0HoeED8dCFYE5cPjrSELtrYxEW0o1DBJw8bXfpgyYB21loBzrcOhRsrPSaS0gYHZLGY7Av7FGfncVZDLNYL0/pZ/t0UWD6JF+6FgOdGWAuuwSt5WR9DVxGilVG5aFktDB14fNPEBIVf7tkT4/McAihR/u344yaiUWA4bV7w039Ubhn9NdnoBSvGrP6jTy/zDgq5ywFj8aqcdlahxtELNWgxYYrI8HZzvITKo1FU7BOcUN1vNS4npOvyWBl7s3jFCO+R2E/BoyjfsjYTylacpepf26D87U32jNsh39OKdHxRF3/qmMGYa1L7N4M0iT9WFEMCcKB/MMAcHgE25vWPQaY1orU8X8NZPhxjfIVcw1rqcjwCryNwb1ZOMTIEc9kbGiP99MhE7ZA0yvHZfMezeymSwg1kN+iJDTp24gSsFtYuz5vm9lRu/PzfU9lNlp2KHdaLISUouSCCHPgF7zZSWtXa1B920zrAg2Fco8/Iymh+Fa0UNnrbnfyQTgLeNT12SLD4Y5gHimUsuq8tFkxjR6WffmrRw==" diff --git a/modules/nixos/cloudflare-proxy/default.nix b/modules/nixos/cloudflare-proxy/default.nix index 216a31d..657722d 100644 --- a/modules/nixos/cloudflare-proxy/default.nix +++ b/modules/nixos/cloudflare-proxy/default.nix @@ -6,11 +6,40 @@ pkgs, ... }: let - inherit (lib) mkEnableOption mkIf; + inherit (lib.options) mkEnableOption mkOption; + inherit (lib.modules) mkIf; + inherit (lib.types) listOf nonEmptyStr port; + + # TODO: What happens when these get out of date??? Huh??? You little pissbaby + fileToList = x: lib.strings.splitString "\n" (builtins.readFile x); + cfipv4 = fileToList (pkgs.fetchurl { + url = "https://www.cloudflare.com/ips-v4"; + hash = "sha256-8Cxtg7wBqwroV3Fg4DbXAMdFU1m84FTfiE5dfZ5Onns="; + }); + cfipv6 = fileToList (pkgs.fetchurl { + url = "https://www.cloudflare.com/ips-v6"; + hash = "sha256-np054+g7rQDE3sr9U8Y/piAp89ldto3pN9K+KCNMoKk="; + }); cfg = config.modules.cloudflare-proxy; in { - options.modules.cloudflare-proxy.enable = mkEnableOption "Cloudflare proxy IP extraction for NGINX"; + options.modules.cloudflare-proxy = { + enable = mkEnableOption "Cloudflare proxy IP extraction for NGINX"; + + firewall = { + IPv4Whitelist = mkOption { + description = "List of IPv4 addresses (or ranges) added to the whitelist."; + type = listOf nonEmptyStr; + default = []; + }; + + IPv6Whitelist = mkOption { + description = "List of IPv6 addresses (or ranges) added to the whitelist."; + type = listOf nonEmptyStr; + default = []; + }; + }; + }; config = mkIf cfg.enable { # Teach NGINX how to extract the proxied IP from proxied requests. @@ -18,21 +47,63 @@ in { # See: https://nixos.wiki/wiki/Nginx#Using_realIP_when_behind_CloudFlare_or_other_CDN services.nginx.commonHttpConfig = let realIpsFromList = lib.strings.concatMapStringsSep "\n" (x: "set_real_ip_from ${x};"); - fileToList = x: lib.strings.splitString "\n" (builtins.readFile x); - cfipv4 = fileToList (pkgs.fetchurl { - url = "https://www.cloudflare.com/ips-v4"; - sha256 = "0ywy9sg7spafi3gm9q5wb59lbiq0swvf0q3iazl0maq1pj1nsb7h"; - }); - cfipv6 = fileToList (pkgs.fetchurl { - url = "https://www.cloudflare.com/ips-v6"; - sha256 = "1ad09hijignj6zlqvdjxv7rjj8567z357zfavv201b9vx3ikk7cy"; - }); in '' ${realIpsFromList cfipv4} ${realIpsFromList cfipv6} real_ip_header CF-Connecting-IP; ''; - # TODO: Only allow incomming HTTP{,S} requests from non-Cloudflare IPs. + # Block non-Cloudflare IP addresses. + networking.firewall = let + chain = "cloudflare-whitelist"; + in { + extraCommands = let + allow-interface = lib.strings.concatMapStringsSep "\n" (i: ''ip46tables --append ${chain} --in-interface ${i} --jump RETURN''); + allow-ip = cmd: lib.strings.concatMapStringsSep "\n" (r: ''${cmd} --append ${chain} --source ${r} --jump RETURN''); + in '' + # Flush the old firewall rules. This behavior mirrors the default firewall service. + # See: https://github.com/NixOS/nixpkgs/blob/ac911bf685eecc17c2df5b21bdf32678b9f88c92/nixos/modules/services/networking/firewall-iptables.nix#L59-L66 + # TEMP: Removed 2>/dev/null + ip46tables --delete INPUT --protocol tcp --destination-port 80 --syn --jump ${chain} || true + ip46tables --delete INPUT --protocol tcp --destination-port 443 --syn --jump ${chain} || true + ip46tables --flush ${chain} || true + ip46tables --delete-chain ${chain} || true + + # Create a chain that only allows whitelisted IPs through. + ip46tables --new-chain ${chain} + + # Allow trusted interfaces through. + ${allow-interface config.networking.firewall.trustedInterfaces} + + # Allow local whitelisted IPs through + ${allow-ip "iptables" cfg.firewall.IPv4Whitelist} + ${allow-ip "ip6tables" cfg.firewall.IPv6Whitelist} + + # Allow Cloudflare's IP ranges through. + ${allow-ip "iptables" cfipv4} + ${allow-ip "ip6tables" cfipv6} + + # Everything else is dropped. + # + # TODO: I would like to use `nixos-fw-log-refuse` here, but I keep + # running into weird issues when reloading the firewall. + # Something about the table not being deleted properly. + ip46tables --append ${chain} --jump DROP + + # Inject our chain as the first check in INPUT (before nixos-fw). + # We want to capture any new incomming TCP connections. + ip46tables --insert INPUT 1 --protocol tcp --destination-port 80 --syn --jump ${chain} + ip46tables --insert INPUT 1 --protocol tcp --destination-port 443 --syn --jump ${chain} + ''; + extraStopCommands = '' + # Clean up added rulesets (${chain}). This mirrors the behavior of the + # default firewall at the time of writing. + # + # See: https://github.com/NixOS/nixpkgs/blob/ac911bf685eecc17c2df5b21bdf32678b9f88c92/nixos/modules/services/networking/firewall-iptables.nix#L218-L219 + # TEMP: Removed 2>/dev/null + ip46tables --delete INPUT --protocol tcp --destination-port 80 --syn --jump ${chain} || true + ip46tables --delete INPUT --protocol tcp --destination-port 443 --syn --jump ${chain} || true + ''; + }; }; } |