From caef3bb4305d317be32c8e18ff6a8467bc6e896e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 6 Aug 2025 09:36:53 -0500 Subject: [PATCH] add new post (closes #11) --- .../content/posts/edge-reverse-proxy/index.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 container/site/content/posts/edge-reverse-proxy/index.md diff --git a/container/site/content/posts/edge-reverse-proxy/index.md b/container/site/content/posts/edge-reverse-proxy/index.md new file mode 100644 index 0000000..5f37b17 --- /dev/null +++ b/container/site/content/posts/edge-reverse-proxy/index.md @@ -0,0 +1,162 @@ ++++ +categories = ["software"] +tags = ["proxy","infrastructure","traefik","crowdsec"] +date = 2025-08-06T08:00:00-05:00 +description = "" +draft = false +slug = "edge-reverse-proxy" +title = "๐Ÿงญ Centralized, Secure Reverse Proxy at the edge" +author = "nicholas" ++++ + +In a [previous post ]({{< relref "posts/infrastructure-overhaul" >}}) I recorded the state of my network at the time, which included no centralized reverse proxy. There was no need. I accessed my services **locally** and each of the service host ran its own reverse proxy on its own IP. I show this configuration in the diagram below. + +**โœ… Multiple Reverse Proxies (LOCAL ONLY)** +```mermaid +graph TD + +subgraph lan[๐Ÿ LAN] + client[๐Ÿง‘โ€๐Ÿ’ปClient] + subgraph hostA["๐Ÿ–ฅ๏ธDocker Host"] + reverseproxyA["๐ŸšชReverse Proxy"] + serviceAA["๐Ÿง LLM"] + end + subgraph hostB["๐Ÿ–ฅ๏ธKubernetes Host"] + reverseproxyB["๐ŸšชReverse Proxy"] + serviceBA["๐Ÿ–ผ๏ธPhotos"] + serviceBB["๐Ÿ“Blog"] + end +end + +client -->|192.168.1.10:443|reverseproxyA +client -->|192.168.1.11:443|reverseproxyB + +reverseproxyA -->|llm.domain.com|serviceAA +reverseproxyB -->|photos.domain.com|serviceBA +reverseproxyB -->|blog.domain.com|serviceBB +``` + +However, now I would like to access certain services from beyond my local network, and not just for myself. I want them to be accessible to all web users, otherwise I would simply set up a client-to-site VPN and be done. I want them to be accessible over HTTPS. + +If I want to expose a service on **two different local hosts** on a **single port** (:443, HTTPS) then I need a **single host** to bind to the single port. I show what this would look like in the diagram below. + +> โš ๏ธ A single public IP and port 443 can only be forwarded to one internal IP at a time. + + +**โœ… Single Edge Reverse Proxy** +```mermaid +graph TD + subgraph wan[๐ŸŒWAN] + client[๐Ÿง‘โ€๐Ÿ’ปClient] + publicip["172.153.14.23:443"] + + end + subgraph router[๐Ÿ›ก๏ธRouter] + forward["WAN:443 โ†’ LAN:443"] + end + + subgraph lan[๐Ÿ LAN] + subgraph hostEdge["๐Ÿ–ฅ๏ธEdge Host"] + reverseproxy["๐ŸšชEdge Reverse Proxy"] + end + hostA["๐Ÿ–ฅ๏ธ Docker Host"] + hostB["๐Ÿ–ฅ๏ธ Kubernetes Host"] + end + + client -->|HTTPS to domain| publicip + publicip -->|HTTPS traffic| router + + router -->|NAT| reverseproxy + + reverseproxy -->|192.168.1.10:443|hostA + reverseproxy --> |192.168.1.11:443|hostB +``` + +## โžก๏ธ Effects +**New host / VM** + +I will need a new host to run my reverse proxy. + +**Unencrypted traffic** + +Moving from having multiple reverse proxies (each terminating TLS) to a single edge reverse proxy means TLS termination will happen at the edge and all traffic inside the LAN is now unencrypted. This is fine. + +**DNS Configuration** + +DNS entries will need to be updated to point to new proxy host. + +**Automatic TLS cert renewal** + +Kubernetes ingress controllers will no longer manage TLS certificates. This responsibility will shift to Traefik to simplify things. + +**IPS** + +Since I am allowing traffic through port 443 into my LAN, I will want a way to prevent rampant bots from molesting my network. I will use an IPS called **CrowdSec**. + +**Firewall** + +Sometime later, I may configure a router / firewall in front of the reverse proxy to preemptively block IPs from outside of the USA. Much of the suspected malicious traffic originates from strange foreign countries like Bulgaria, China, etc. + +## ๐Ÿ–ฅ๏ธ New VM / Edge Host +The VM which will run my edge services (reverse proxy, IPS) will need very few resources. I set it up with the following specs: +| Resource | Specification | +|-|-| +| OS | Ubuntu Server 24 (minimal) | +| vCPU | 1 | +| RAM | 2048 MB | +| Storage | 10 GB | + +All I need to do is: +- Install Docker +- Install, configure `ufw` + +## ๐ŸšชReverse Proxy + IPS +I run these two programs in Docker containers. The diagram below shows the directory structure of this stack. I do not bother adding configuration files to this post. **Security through obscurity** + +**Directory structure** +```plaintext +๐Ÿ“ฆege-stack-config + โ”œโ”€๐Ÿ“‚crowdsec + โ”‚ โ””โ”€๐Ÿ“œacquis.yaml + โ”œโ”€๐Ÿ“‚traefik + โ”‚ โ”œโ”€๐Ÿ“‚dynamic + โ”‚ โ”‚ โ”œโ”€๐Ÿ“œmiddlewares.yaml + โ”‚ โ”‚ โ”œโ”€๐Ÿ“œrouters.yaml + โ”‚ โ”‚ โ””โ”€๐Ÿ“œservices.yaml + โ”‚ โ””โ”€๐Ÿ“œtraefik.yaml + โ”œโ”€๐Ÿ“‚logs + โ”‚ โ””โ”€๐Ÿ“œaccess.log + โ””โ”€๐Ÿ‹docker-compose.yaml + ``` + +Traefik performs its normal functions as a reverse proxy, generating access logs for incoming HTTPS traffic. CrowdSec reads these logs, parses them, and generates a decision as to whether an IP should be banned. CrowdSec sends this along to a **bouncer** to enforce the decision. For my bouncer, I am using a Traefik plugin aptly named *Crowdsec Bouncer Traefik plugin*. + +## ๐Ÿงช Test it +After some time, I go check whether CrowdSec has banned any bots. + +`docker exec -it crowdsec cscli alerts list` + +```plaintext +โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ +โ”‚ ID โ”‚ value โ”‚ reason โ”‚ country โ”‚ as โ”‚ decisions โ”‚ created_at โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ 86 โ”‚ Ip:129.146.52.207 โ”‚ crowdsecurity/thinkphp-cve-2018-20062 โ”‚ US โ”‚ 31898 ORACLE-BMC-31898 โ”‚ ban:1 โ”‚ 2025-08-06 07:51:14.59356465 +0000 UTC โ”‚ +โ”‚ 85 โ”‚ Ip:129.146.52.207 โ”‚ crowdsecurity/http-probing โ”‚ US โ”‚ 31898 ORACLE-BMC-31898 โ”‚ ban:1 โ”‚ 2025-08-06 07:50:27.497858459 +0000 UTC โ”‚ +โ”‚ 84 โ”‚ Ip:129.146.52.207 โ”‚ crowdsecurity/CVE-2017-9841 โ”‚ US โ”‚ 31898 ORACLE-BMC-31898 โ”‚ ban:1 โ”‚ 2025-08-06 07:50:31.986742743 +0000 UTC โ”‚ +โ”‚ 80 โ”‚ Ip:47.89.154.16 โ”‚ crowdsecurity/http-probing โ”‚ US โ”‚ 45102 Alibaba US Technology Co., Ltd. โ”‚ ban:1 โ”‚ 2025-08-06 02:14:47.399363246 +0000 UTC โ”‚ +โ”‚ 78 โ”‚ Ip:43.166.136.113 โ”‚ crowdsecurity/thinkphp-cve-2018-20062 โ”‚ US โ”‚ 132203 Tencent Building, Kejizhongyi Avenue โ”‚ ban:1 โ”‚ 2025-08-06 01:28:25.249998707 +0000 UTC โ”‚ +โ”‚ 77 โ”‚ Ip:43.166.136.113 โ”‚ crowdsecurity/http-probing โ”‚ US โ”‚ 132203 Tencent Building, Kejizhongyi Avenue โ”‚ ban:1 โ”‚ 2025-08-06 01:27:55.834522627 +0000 UTC โ”‚ +โ”‚ 76 โ”‚ Ip:43.166.136.113 โ”‚ crowdsecurity/CVE-2017-9841 โ”‚ US โ”‚ 132203 Tencent Building, Kejizhongyi Avenue โ”‚ ban:1 โ”‚ 2025-08-06 01:28:08.746875988 +0000 UTC โ”‚ +โ”‚ 74 โ”‚ Ip:8.222.225.103 โ”‚ crowdsecurity/thinkphp-cve-2018-20062 โ”‚ SG โ”‚ 45102 Alibaba US Technology Co., Ltd. โ”‚ ban:1 โ”‚ 2025-08-05 22:04:44.325377414 +0000 UTC โ”‚ +โ”‚ 73 โ”‚ Ip:8.222.225.103 โ”‚ crowdsecurity/http-probing โ”‚ SG โ”‚ 45102 Alibaba US Technology Co., Ltd. โ”‚ ban:1 โ”‚ 2025-08-05 22:04:30.396505319 +0000 UTC โ”‚ +โ”‚ 72 โ”‚ Ip:8.222.225.103 โ”‚ crowdsecurity/CVE-2017-9841 โ”‚ SG โ”‚ 45102 Alibaba US Technology Co., Ltd. โ”‚ ban:1 โ”‚ 2025-08-05 22:04:34.878409129 +0000 UTC โ”‚ +โ”‚ 69 โ”‚ Ip:20.163.10.187 โ”‚ crowdsecurity/http-cve-probing โ”‚ US โ”‚ 8075 MICROSOFT-CORP-MSN-AS-BLOCK โ”‚ ban:1 โ”‚ 2025-08-05 19:22:47.360100531 +0000 UTC โ”‚ +โ”‚ 67 โ”‚ Ip:20.163.15.225 โ”‚ crowdsecurity/CVE-2022-41082 โ”‚ US โ”‚ 8075 MICROSOFT-CORP-MSN-AS-BLOCK โ”‚ ban:1 โ”‚ 2025-08-05 15:56:51.234726408 +0000 UTC โ”‚ +โ”‚ 56 โ”‚ Ip:23.140.40.74 โ”‚ crowdsecurity/thinkphp-cve-2018-20062 โ”‚ RU โ”‚ 61400 Start LLC โ”‚ ban:1 โ”‚ 2025-08-04 21:01:55.536820464 +0000 UTC โ”‚ +โ”‚ 55 โ”‚ Ip:23.140.40.74 โ”‚ crowdsecurity/http-probing โ”‚ RU โ”‚ 61400 Start LLC โ”‚ ban:1 โ”‚ 2025-08-04 21:01:44.946667866 +0000 UTC โ”‚ +โ”‚ 54 โ”‚ Ip:23.140.40.74 โ”‚ crowdsecurity/CVE-2017-9841 โ”‚ RU โ”‚ 61400 Start LLC โ”‚ ban:1 โ”‚ 2025-08-04 21:01:48.880039776 +0000 UTC โ”‚ +โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ +``` +๐ŸŽ‰ It works! The table generated by `cscli` shows a few IP addresses have been banned. + +โœ… Done. \ No newline at end of file