HASH cc6f19b0cf62
DATE 2026-07-11
SUBJECT caddy: lock origin ingress to cloudflare ranges
FILES 4 CHANGED
HASH cc6f19b0cf62
DATE 2026-07-11
SUBJECT caddy: lock origin ingress to
cloudflare ranges
FILES 4 CHANGED
┌─ hosts/linode/default.nix ─────────────────────────────────────────────────┐
│ diff --git a/hosts/linode/default.nix b/hosts/linode/default.nix │
│ index aaa8981..0f68958 100644 │
│ --- a/hosts/linode/default.nix │
│ +++ b/hosts/linode/default.nix │
│ @@ -9,5 +9,7 @@ │
│ hideLogo = true; │
│ }; │
│ │
│ + ooknet.server.webserver.caddy.cloudflareOnly = true; │
│ + │
│ system.stateVersion = "24.11"; │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ hosts/linode/default.nix ───────────┐
│ diff --git a/hosts/linode/default.nix b/host │
│ s/linode/default.nix │
│ index aaa8981..0f68958 100644 │
│ --- a/hosts/linode/default.nix │
│ +++ b/hosts/linode/default.nix │
│ @@ -9,5 +9,7 @@ │
│ hideLogo = true; │
│ }; │
│ │
│ + ooknet.server.webserver.caddy.cloudflareO │
│ nly = true; │
│ + │
│ system.stateVersion = "24.11"; │
│ } │
└──────────────────────────────────────────────┘
┌─ modules/nixos/server/webserver/caddy.nix ─────────────────────────────────┐
│ diff --git a/modules/nixos/server/webserver/caddy.nix b/modules/nixos/server/webse │
│ rver/caddy.nix │
│ index 4eab869..f023347 100644 │
│ --- a/modules/nixos/server/webserver/caddy.nix │
│ +++ b/modules/nixos/server/webserver/caddy.nix │
│ @@ -4,8 +4,9 @@ │
│ pkgs, │
│ ... │
│ }: let │
│ - inherit (lib) mkIf mkMerge; │
│ + inherit (lib) mkIf mkMerge concatStringsSep; │
│ inherit (config.ooknet.server.webserver) caddy; │
│ + cf = import ./cloudflare-ips.nix; │
│ in { │
│ config = mkIf caddy.enable { │
│ users.groups.www = {}; │
│ @@ -13,6 +14,25 @@ in { │
│ # metrics scraping via tailscale │
│ networking.firewall.interfaces."tailscale0".allowedTCPPorts = [2019]; │
│ │
│ + # origin lockdown: drop direct (non-cloudflare) hits on 80/443 so the vps │
│ + # cant be reached bypassing cloudflare. dedicated chain at higher priority │
│ + # than the firewall (drop is terminal, and firewall extraInputRules run │
│ + # AFTER the port accepts so a drop there wouldnt fire). lo + tailscale0 │
│ + # exempt so loopback and tailnet admin access to the web UI still work. │
│ + networking.nftables.tables = mkIf caddy.cloudflareOnly { │
│ + cf-origin-lock = { │
│ + family = "inet"; │
│ + content = '' │
│ + chain input { │
│ + type filter hook input priority -10; policy accept; │
│ + iifname { "lo", "tailscale0" } accept │
│ + tcp dport { 80, 443 } ip saddr != { ${concatStringsSep ", " cf.v4} } │
│ drop │
│ + tcp dport { 80, 443 } ip6 saddr != { ${concatStringsSep ", " cf.v6} } │
│ drop │
│ + } │
│ + ''; │
│ + }; │
│ + }; │
│ + │
│ services.caddy = mkMerge [ │
│ { │
│ enable = true; │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...os/server/webserver/caddy.nix ───┐
│ diff --git a/modules/nixos/server/webserver/ │
│ caddy.nix b/modules/nixos/server/webserver/c │
│ addy.nix │
│ index 4eab869..f023347 100644 │
│ --- a/modules/nixos/server/webserver/caddy.n │
│ ix │
│ +++ b/modules/nixos/server/webserver/caddy.n │
│ ix │
│ @@ -4,8 +4,9 @@ │
│ pkgs, │
│ ... │
│ }: let │
│ - inherit (lib) mkIf mkMerge; │
│ + inherit (lib) mkIf mkMerge concatStringsS │
│ ep; │
│ inherit (config.ooknet.server.webserver) │
│ caddy; │
│ + cf = import ./cloudflare-ips.nix; │
│ in { │
│ config = mkIf caddy.enable { │
│ users.groups.www = {}; │
│ @@ -13,6 +14,25 @@ in { │
│ # metrics scraping via tailscale │
│ networking.firewall.interfaces."tailsca │
│ le0".allowedTCPPorts = [2019]; │
│ │
│ + # origin lockdown: drop direct (non-clo │
│ udflare) hits on 80/443 so the vps │
│ + # cant be reached bypassing cloudflare. │
│ dedicated chain at higher priority │
│ + # than the firewall (drop is terminal, │
│ and firewall extraInputRules run │
│ + # AFTER the port accepts so a drop ther │
│ e wouldnt fire). lo + tailscale0 │
│ + # exempt so loopback and tailnet admin │
│ access to the web UI still work. │
│ + networking.nftables.tables = mkIf caddy │
│ .cloudflareOnly { │
│ + cf-origin-lock = { │
│ + family = "inet"; │
│ + content = '' │
│ + chain input { │
│ + type filter hook input priority │
│ -10; policy accept; │
│ + iifname { "lo", "tailscale0" } │
│ accept │
│ + tcp dport { 80, 443 } ip saddr │
│ != { ${concatStringsSep ", " cf.v4} } drop │
│ + tcp dport { 80, 443 } ip6 saddr │
│ != { ${concatStringsSep ", " cf.v6} } drop │
│ + } │
│ + ''; │
│ + }; │
│ + }; │
│ + │
│ services.caddy = mkMerge [ │
│ { │
│ enable = true; │
└──────────────────────────────────────────────┘
┌─ modules/nixos/server/webserver/cloudflare-ips.nix ────────────────────────┐
│ diff --git a/modules/nixos/server/webserver/cloudflare-ips.nix b/modules/nixos/ser │
│ ver/webserver/cloudflare-ips.nix │
│ new file mode 100644 │
│ index 0000000..6af515a │
│ --- /dev/null │
│ +++ b/modules/nixos/server/webserver/cloudflare-ips.nix │
│ @@ -0,0 +1,30 @@ │
│ +# cloudflare edge ip ranges (https://www.cloudflare.com/ips/) │
│ +# used to lock the origin firewall to cloudflare; update if cloudflare changes th │
│ em │
│ +{ │
│ + v4 = [ │
│ + "173.245.48.0/20" │
│ + "103.21.244.0/22" │
│ + "103.22.200.0/22" │
│ + "103.31.4.0/22" │
│ + "141.101.64.0/18" │
│ + "108.162.192.0/18" │
│ + "190.93.240.0/20" │
│ + "188.114.96.0/20" │
│ + "197.234.240.0/22" │
│ + "198.41.128.0/17" │
│ + "162.158.0.0/15" │
│ + "104.16.0.0/13" │
│ + "104.24.0.0/14" │
│ + "172.64.0.0/13" │
│ + "131.0.72.0/22" │
│ + ]; │
│ + v6 = [ │
│ + "2400:cb00::/32" │
│ + "2606:4700::/32" │
│ + "2803:f800::/32" │
│ + "2405:b500::/32" │
│ + "2405:8100::/32" │
│ + "2a06:98c0::/29" │
│ + "2c0f:f248::/32" │
│ + ]; │
│ +} │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ .../webserver/cloudflare-ips.nix ───┐
│ diff --git a/modules/nixos/server/webserver/ │
│ cloudflare-ips.nix b/modules/nixos/server/we │
│ bserver/cloudflare-ips.nix │
│ new file mode 100644 │
│ index 0000000..6af515a │
│ --- /dev/null │
│ +++ b/modules/nixos/server/webserver/cloudfl │
│ are-ips.nix │
│ @@ -0,0 +1,30 @@ │
│ +# cloudflare edge ip ranges (https://www.cl │
│ oudflare.com/ips/) │
│ +# used to lock the origin firewall to cloud │
│ flare; update if cloudflare changes them │
│ +{ │
│ + v4 = [ │
│ + "173.245.48.0/20" │
│ + "103.21.244.0/22" │
│ + "103.22.200.0/22" │
│ + "103.31.4.0/22" │
│ + "141.101.64.0/18" │
│ + "108.162.192.0/18" │
│ + "190.93.240.0/20" │
│ + "188.114.96.0/20" │
│ + "197.234.240.0/22" │
│ + "198.41.128.0/17" │
│ + "162.158.0.0/15" │
│ + "104.16.0.0/13" │
│ + "104.24.0.0/14" │
│ + "172.64.0.0/13" │
│ + "131.0.72.0/22" │
│ + ]; │
│ + v6 = [ │
│ + "2400:cb00::/32" │
│ + "2606:4700::/32" │
│ + "2803:f800::/32" │
│ + "2405:b500::/32" │
│ + "2405:8100::/32" │
│ + "2a06:98c0::/29" │
│ + "2c0f:f248::/32" │
│ + ]; │
│ +} │
└──────────────────────────────────────────────┘
┌─ modules/options/server/default.nix ───────────────────────────────────────┐
│ diff --git a/modules/options/server/default.nix b/modules/options/server/default.n │
│ ix │
│ index b1a1395..6e77f21 100644 │
│ --- a/modules/options/server/default.nix │
│ +++ b/modules/options/server/default.nix │
│ @@ -27,6 +27,8 @@ in { │
│ caddy = { │
│ enable = mkEnableOption ""; │
│ cloudflare.enable = mkEnableOption ""; │
│ + # lock 80/443 to cloudflare ip ranges (origin cant be reached directly) │
│ + cloudflareOnly = mkEnableOption "restricting 80/443 ingress to Cloudflare │
│ ranges"; │
│ }; │
│ }; │
│ database = { │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...es/options/server/default.nix ───┐
│ diff --git a/modules/options/server/default. │
│ nix b/modules/options/server/default.nix │
│ index b1a1395..6e77f21 100644 │
│ --- a/modules/options/server/default.nix │
│ +++ b/modules/options/server/default.nix │
│ @@ -27,6 +27,8 @@ in { │
│ caddy = { │
│ enable = mkEnableOption ""; │
│ cloudflare.enable = mkEnableOption │
│ ""; │
│ + # lock 80/443 to cloudflare ip rang │
│ es (origin cant be reached directly) │
│ + cloudflareOnly = mkEnableOption "re │
│ stricting 80/443 ingress to Cloudflare range │
│ s"; │
│ }; │
│ }; │
│ database = { │
└──────────────────────────────────────────────┘
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET