923 lines
29 KiB
Markdown
923 lines
29 KiB
Markdown
#### Overview
|
|
|
|
|Feature|kubectl port-forward|SSH Tunnel (-L)|VPN (Wireguard/Tailscale)|
|
|
|---|---|---|----|
|
|
|Setup|Zero (if you have kubectl)|Requires SSH access|Requires Client/Driver install|
|
|
|Scope|Single Port/Service|Single Port/Service|Entire Network|
|
|
|Lifetime|Temporary (dies if cmd closes)|Temporary|Persistent/Background|
|
|
|OS Level|User-space (Application)|User-space (Application)|Kernel-space (Network Card)|
|
|
|
|
From a technical point of view, kubectl port-forward is most accurately described as a User-Space Application Proxy or a Layer 7 Tunnel.
|
|
|
|
It is not a full-blown VPN or a network-level tunnel like Wireguard. Instead, it is a "targeted bridge" created specifically for one-way traffic between your local machine and a specific service inside the cluster.
|
|
|
|
|
|
1. Closest Match: SSH Local Port Forwarding (ssh -L)
|
|
This is the "twin brother" of kubectl port-forward.
|
|
|
|
How it's similar: You use an existing encrypted management channel (SSH or the Kubernetes API) to "piggyback" traffic for a specific port. Neither creates a new network interface on your machine; they just listen on a local port and "teleport" data to the other side.
|
|
|
|
The Comparison: Just as ssh -L 5436:localhost:5432 user@server maps a remote DB to your laptop, kubectl does the same for a Pod.
|
|
|
|
2. Cloudflare Tunnel (cloudflared)
|
|
How it's similar: Both allow you to bypass firewalls and NAT without opening public ports. They both rely on an "outbound" connection from the cluster/server to a central controller to establish the path.
|
|
|
|
The Difference: Cloudflare Tunnels are designed for permanent, public-facing access; port-forward is a temporary, private developer tool.
|
|
|
|
3. What it is NOT: VPNs (OpenVPN, Wireguard, Tailscale)
|
|
Why they are different: A VPN creates a Virtual Network Interface (like tun0 or utun). When you use a VPN, your laptop actually "lives" inside the 10.x.x.x network. You could ping any Pod or Service directly by its internal IP.
|
|
|
|
With port-forward: Your laptop doesn't know the db namespace exists. It only knows that localhost:5436 is magically connected to something else.
|
|
|
|
|
|
|
|
#### Where do they live on the OSI Model?
|
|
```
|
|
1. Layer 7 (Application Layer): kubectl port-forward and SSH Tunneling
|
|
Reason: These tools do not understand "IP addresses" or "Routing" in the traditional sense.
|
|
|
|
When you run kubectl port-forward, the kubectl binary on your machine opens a local socket.
|
|
|
|
It takes the data from that socket, wraps it in an HTTP/SPDY or HTTP/2 request, and sends it to the Kubernetes API server.
|
|
|
|
The API server then unwraps that data and sends it to the Pod.
|
|
|
|
Because the "tunneling" happens inside an application protocol (HTTP/SSH), it is a Layer 7 operation.
|
|
|
|
Layer 3 (Network Layer): VPNs (Wireguard, OpenVPN, IPSec)
|
|
Reason: These create a Virtual Network Interface (like utun0).
|
|
|
|
They handle IP Packets.
|
|
|
|
If you send a ping to 10.42.0.5, the VPN intercepts that packet at the Network Layer, encrypts it, and sends it to the other side.
|
|
|
|
It doesn't care if the data inside is Postgres, HTTP, or DNS; it only cares about the IP Header.
|
|
|
|
Layer 2 (Data Link Layer): VXLAN, Tinc, or Bridge-mode VPNs
|
|
Reason: Some advanced VPNs can operate at Layer 2.
|
|
|
|
They transport Ethernet Frames (including MAC addresses).
|
|
|
|
This makes your local computer think it is plugged into the same "virtual switch" as the remote servers.
|
|
|
|
2. Other Technologies to Connect
|
|
If port-forward is too temporary, here are the professional alternatives:
|
|
|
|
A. Ingress Controller (Layer 7)
|
|
How it works: You define a "Route" (e.g., db.example.com). The Ingress (like Nginx or Traefik) receives the traffic and forwards it to your service.
|
|
|
|
Layer: 7 (Application). It looks at the Hostname and Path in the HTTP/TLS header to decide where to send the data.
|
|
|
|
B. LoadBalancer Service (Layer 4)
|
|
How it works: In a cloud environment (AWS/GCP), this gives your Service a real, reachable IP address.
|
|
|
|
Layer: 4 (Transport). It only cares about the Port and Protocol (TCP/UDP). It doesn't look at the data inside the packets.
|
|
|
|
Comparison: This is like a "Permanent Port-Forward" that anyone on the internet (or your VPC) can reach.
|
|
|
|
C. Mesh VPNs / SD-WAN (Layer 3 - Overlay)
|
|
Examples: Tailscale, ZeroTier, Nebula.
|
|
|
|
How it works: You install a small agent on your VM and your laptop. They create a "Peer-to-Peer" encrypted mesh.
|
|
|
|
Layer: 3 (Network). It provides a stable IP address for your database that stays the same even if you move from office to home.
|
|
|
|
Reason to use: This is the most "production-ready" version of what you are doing. It is more secure than a public LoadBalancer but more stable than a port-forward.
|
|
|
|
```
|
|
|
|
|Technology|OSI Layer|Comparison|
|
|
|----|---|---|
|
|
|kubectl port-forward|Layer 7|"A temporary ""straw"" to sip data from one |specific cup.|
|
|
|Ingress|Layer 7|A ""Receptionist"" directing visitors based on the name on the |envelope.|
|
|
|LoadBalancer|Layer 4|"A ""Direct Pipeline"" to a specific door (port).|
|
|
|Tailscale / VPN|Layer 3|"A ""Secret Tunnel"" that puts your whole house next to the office.|
|
|
|
|
#### Finding IP
|
|
public: curl ifconfig.me
|
|
|
|
|Target|Command|Requirement|
|
|
|----|----|-----|
|
|
|Inside Container| hostname -I /ip addr show eth0|LXC Container|
|
|
|LXC Local IP|pct exec ID ip a|Access to Proxmox Host|
|
|
|Proxmox Local IP|hostname -I|Access to Proxmox Host|
|
|
|Proxmox Public IP|curl ifconfig.me|Internet Access|
|
|
|
|
|
|
#### Ipaymu Integration need static IP
|
|
|
|
[Cloudflare Worker] ──1. Request──► [Your Nginx Server] ──2. Request──► [iPaymu API]
|
|
│ │
|
|
[Cloudflare Worker] ◄─4. Response── [Your Nginx Server] ◄─3. Response─── [iPaymu API]
|
|
Problem: we deploy using cloudflared worker which has dynamic ips
|
|
|
|
- Find IP of the server
|
|
- curl -4 ifconfig.me
|
|
- curl -4 icanhazip.com
|
|
- curl -4 ipinfo.io/ip
|
|
- ip -4 addr show
|
|
|
|
1. Option 1: Cloudflare Tunnel (cloudflared) — Easiest & Most Secure
|
|
A. cloudflared inside an LXC Container (Separate from K3s)
|
|
An LXC container on the same LAN/Proxmox host can run cloudflared and route incoming traffic directly to your K3s cluster.
|
|
|
|
[Cloudflare Worker]
|
|
│ (Public HTTPS)
|
|
▼
|
|
[Cloudflare Edge Network]
|
|
│ (Outbound Tunnel)
|
|
[LXC Container running cloudflared]
|
|
│ (Local LAN / Bridge Traffic)
|
|
[K3s Cluster Node (Static IP)] ──► [Nginx Proxy Pod] ──► [iPaymu API]
|
|
|
|
How to configure the LXC Tunnel:
|
|
When you set up the Public Hostname rule in the Cloudflare Zero Trust Dashboard (or config.yaml), point the origin URL directly to your K3s Node IP and port:
|
|
|
|
- Service Type: HTTP
|
|
|
|
- URL: <K3S_NODE_IP>:<NODE_PORT_OR_INGRESS_PORT>
|
|
(e.g., [http://192.168.1.50:80](http://192.168.1.50:80) if calling Traefik/Ingress, or [http://192.168.1.50:30080](http://192.168.1.50:30080) if using a NodePort service).
|
|
|
|
Pros & Cons:
|
|
- Pros: Keeps your K3s cluster lightweight; if K3s reboots, the tunnel daemon stays alive in the LXC container.
|
|
- Cons: Requires exposing a NodePort or LoadBalancer (Traefik) on your K3s node so the LXC container can hit it on the local network.
|
|
|
|
B. cloudflared as a Pod inside K3s
|
|
Deploying cloudflared directly inside K3s as a Deployment is the most common Kubernetes-native method.
|
|
[Cloudflare Worker]
|
|
│
|
|
▼ (HTTPS over public internet)
|
|
[https://proxy.yourdomain.com] <-- Cloudflare Edge
|
|
│
|
|
▼ (Secure outbound Tunnel)
|
|
[cloudflared Pod in K3s / Swarm]
|
|
│
|
|
▼ (Internal Cluster Traffic)
|
|
[Nginx Proxy Pod (Static-IP Node)]
|
|
│
|
|
▼ (Outbound call from Static IPv4)
|
|
[iPaymu API]
|
|
|
|
2. Option 2: Traefik Ingress Controller + NodePort / Public IP
|
|
If your static-IP cluster node already has a Public Static IPv4 Address directly bound to its network interface:
|
|
|
|
[Cloudflare Worker]
|
|
│
|
|
▼ (HTTPS to Public Static IP)
|
|
[Public IP:443] ──► [Traefik Ingress] ──► [Nginx Proxy Service] ──► [iPaymu]
|
|
|
|
How to configure the K3s Tunnel:
|
|
Inside the Cloudflare dashboard, you point the public hostname to K3s's internal DNS service name:
|
|
|
|
- Service Type: HTTP
|
|
- URL: ipaymu-proxy-service.default.svc.cluster.local:80
|
|
|
|
Pros & Cons:
|
|
- Pros: Highly secure—you do not need to open any ports or expose NodePort services on your LAN. Traffic moves entirely within K3s cluster networking.
|
|
- Cons: Runs inside the Kubernetes runtime lifecycle.
|
|
|
|
3. Option 3: Port Mapping directly on the Static Node (hostPort)
|
|
If you don't want to deal with Traefik or Ingress controllers, you can expose your Nginx proxy directly on a port (e.g., 8080 or 8443) of your static-IP node using hostPort in K3s or ports mapping in Swarm.
|
|
|
|
K3s Manifest Snippet:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:alpine
|
|
ports:
|
|
- containerPort: 80
|
|
hostPort: 8080 # Exposes port 8080 directly on the physical host
|
|
|
|
|
|
Vour Cloudflare Worker calls http://<YOUR_STATIC_NODE_IP>:8080/ipaymu/api/v2/payment.
|
|
|
|
- How the Request Flow Works End-to-End
|
|
Once exposed via Option 1, 2, or 3, here is the full round-trip execution path:
|
|
```
|
|
// 1. Cloudflare Worker executes this code:
|
|
const response = await fetch("https://proxy.yourdomain.com/ipaymu/api/v2/payment", {
|
|
method: "POST",
|
|
headers: {
|
|
"X-Proxy-Secret": "YOUR_INTERNAL_SECRET",
|
|
"va": "1176000000000000",
|
|
"signature": "CALCULATED_HMAC_SIGNATURE",
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify(paymentPayload)
|
|
});
|
|
|
|
// 2. Request reaches https://proxy.yourdomain.com (Your Cluster via Tunnel/Ingress)
|
|
// 3. Ingress routes it internally to your Nginx proxy pod
|
|
// 4. Nginx verifies "X-Proxy-Secret", then forwards the request to https://api.ipaymu.com
|
|
// 5. iPaymu receives the call, sees your Node's STATIC IP, and accepts it
|
|
// 6. iPaymu returns payment data -> Nginx -> Ingress -> Cloudflare Worker -> Client
|
|
|
|
```
|
|
|
|
|
|
#### Step by step using ONLY Traefik in K3s
|
|
```
|
|
Step 1: Create Traefik Middlewares for Security and Stripping
|
|
Instead of Nginx if ($http_x_proxy_secret) directives, you define Traefik Middleware resources:
|
|
|
|
# 1. Security Header Check Middleware
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: Middleware
|
|
metadata:
|
|
name: ipaymu-auth-check
|
|
namespace: default
|
|
spec:
|
|
headers:
|
|
customRequestHeaders:
|
|
# Ensures requests forwarded to iPaymu clean up internal proxy secrets
|
|
X-Proxy-Secret: ""
|
|
---
|
|
# 2. Path Strip Middleware (Removes /ipaymu prefix before forwarding)
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: Middleware
|
|
metadata:
|
|
name: ipaymu-strip-prefix
|
|
namespace: default
|
|
spec:
|
|
stripPrefix:
|
|
prefixes:
|
|
- /ipaymu
|
|
|
|
Step 2: Define an ExternalName Service for iPaymu
|
|
To tell Traefik to forward traffic out to iPaymu's external domain, create a Kubernetes ExternalName service:
|
|
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: ipaymu-external-api
|
|
namespace: default
|
|
spec:
|
|
type: ExternalName
|
|
externalName: api.ipaymu.com
|
|
ports:
|
|
- port: 443
|
|
targetPort: 443
|
|
protocol: TCP
|
|
|
|
Step 3: Route Traffic with Traefik IngressRoute
|
|
Now, create a Traefik IngressRoute pinned to your static IP node:
|
|
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: IngressRoute
|
|
metadata:
|
|
name: ipaymu-proxy-route
|
|
namespace: default
|
|
spec:
|
|
entryPoints:
|
|
- web
|
|
- websecure
|
|
routes:
|
|
- match: Host(`proxy.yourdomain.com`) && PathPrefix(`/ipaymu`)
|
|
kind: Rule
|
|
middlewares:
|
|
- name: ipaymu-strip-prefix
|
|
services:
|
|
- name: ipaymu-external-api
|
|
port: 443
|
|
scheme: https
|
|
|
|
When should you STILL use Nginx?
|
|
While Traefik replaces Nginx for 90% of use cases, keeping a tiny Nginx pod is still useful if:
|
|
|
|
Complex Logic: You want custom Nginx Lua scripts or complex header manipulation (e.g., computing dynamic HMAC signatures or custom retry logic on upstream failures).
|
|
|
|
Familiarity: You are already comfortable writing .conf files and don't want to learn Traefik-specific Kubernetes CRD syntax (IngressRoute / Middleware).
|
|
```
|
|
|
|
#### Step by step using traeffik and nginx in k3s
|
|
```
|
|
1. Label your target node (Crucial Step)
|
|
Label the K3s node that holds your static public IPv4 address so K3s knows where to schedule the proxy pods:
|
|
|
|
kubectl label nodes <your-static-node-name> egress-ip=static-ipaymu
|
|
|
|
2. k3s-ipaymu-proxy.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: ipaymu-proxy-config
|
|
namespace: default
|
|
data:
|
|
default.conf: |
|
|
upstream ipaymu_backend {
|
|
server api.ipaymu.com:443;
|
|
keepalive 32;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
|
|
location /ipaymu/ {
|
|
if ($http_x_proxy_secret != "YOUR_INTERNAL_SECRET_KEY") {
|
|
return 403;
|
|
}
|
|
|
|
proxy_pass https://ipaymu_backend/;
|
|
proxy_set_header Host api.ipaymu.com;
|
|
|
|
# SSL Handshake settings
|
|
proxy_ssl_server_name on;
|
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
# HTTP Keepalive
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
}
|
|
}
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: ipaymu-proxy
|
|
namespace: default
|
|
spec:
|
|
replicas: 2 # Scale up replicas as needed
|
|
selector:
|
|
matchLabels:
|
|
app: ipaymu-proxy
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: ipaymu-proxy
|
|
spec:
|
|
# Pin all replicas to the node with the static public IP
|
|
nodeSelector:
|
|
egress-ip: static-ipaymu
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:alpine
|
|
ports:
|
|
- containerPort: 80
|
|
resources:
|
|
limits:
|
|
cpu: "200m"
|
|
memory: "128Mi"
|
|
requests:
|
|
cpu: "50m"
|
|
memory: "32Mi"
|
|
volumeMounts:
|
|
- name: nginx-config
|
|
mountPath: /etc/nginx/conf.d/default.conf
|
|
subPath: default.conf
|
|
volumes:
|
|
- name: nginx-config
|
|
configMap:
|
|
name: ipaymu-proxy-config
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: ipaymu-proxy-service
|
|
namespace: default
|
|
spec:
|
|
type: ClusterIP
|
|
ports:
|
|
- port: 80
|
|
targetPort: 80
|
|
protocol: TCP
|
|
selector:
|
|
app: ipaymu-proxy
|
|
|
|
3. Deploy : kubectl apply -f k3s-ipaymu-proxy.yaml
|
|
4. How to call it inside K3s
|
|
Any other pod inside your K3s cluster can now make payment requests directly using internal Kubernetes DNS:
|
|
[http://ipaymu-proxy-service.default.svc.cluster.local/ipaymu/api/v2/payment](http://ipaymu-proxy-service.default.svc.cluster.local/ipaymu/api/v2/payment)
|
|
```
|
|
|
|
#### Step by step using traeffik and cloudflared in k3s
|
|
```
|
|
Step 2.1: Enable allowExternalNameServices in Traefik
|
|
By default, K3s disables Traefik from routing to external domains via ExternalName. Create a HelmChartConfig manifest on your K3s server node to enable it:
|
|
|
|
Create file /var/lib/rancher/k3s/server/manifests/traefik-config.yaml:
|
|
|
|
apiVersion: helm.cattle.io/v1
|
|
kind: HelmChartConfig
|
|
metadata:
|
|
name: traefik
|
|
namespace: kube-system
|
|
spec:
|
|
valuesContent: |-
|
|
providers:
|
|
kubernetesCRD:
|
|
allowExternalNameServices: true
|
|
|
|
K3s automatically applies this change in under 30 seconds.
|
|
|
|
Step 2.2: Label your Static-IP Node
|
|
Label the node with your static public IPv4 address so K3s knows where to schedule the tunnel:
|
|
|
|
kubectl label nodes <your-node-name> egress-ip=static-ipaymu
|
|
|
|
Step 2.3: Deploy the iPaymu Traefik Route
|
|
Save this to ipaymu-traefik.yaml and run kubectl apply -f ipaymu-traefik.yaml:
|
|
|
|
# 1. External Name Service pointing to iPaymu
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: ipaymu-external-service
|
|
namespace: default
|
|
spec:
|
|
type: ExternalName
|
|
externalName: api.ipaymu.com
|
|
ports:
|
|
- name: https
|
|
port: 443
|
|
targetPort: 443
|
|
protocol: TCP
|
|
---
|
|
# 2. TLS Transport for SNI support
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: ServersTransport
|
|
metadata:
|
|
name: ipaymu-transport
|
|
namespace: default
|
|
spec:
|
|
serverName: api.ipaymu.com
|
|
---
|
|
# 3. Path Stripper Middleware (/ipaymu/api/v2/payment -> /api/v2/payment)
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: Middleware
|
|
metadata:
|
|
name: ipaymu-strip-prefix
|
|
namespace: default
|
|
spec:
|
|
stripPrefix:
|
|
prefixes:
|
|
- /ipaymu
|
|
---
|
|
# 4. Traefik IngressRoute
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: IngressRoute
|
|
metadata:
|
|
name: ipaymu-proxy-route
|
|
namespace: default
|
|
spec:
|
|
entryPoints:
|
|
- web
|
|
- websecure
|
|
routes:
|
|
- match: Host(`proxy.yourdomain.com`) && PathPrefix(`/ipaymu`)
|
|
kind: Rule
|
|
middlewares:
|
|
- name: ipaymu-strip-prefix
|
|
services:
|
|
- name: ipaymu-external-service
|
|
port: 443
|
|
scheme: https
|
|
serversTransport: ipaymu-transport
|
|
|
|
Step 2.4:
|
|
- Get Your Tunnel Secret Token
|
|
1. Log into your Cloudflare Zero Trust Dashboard.
|
|
2. Navigate to Networks → Tunnels → Create a Tunnel.
|
|
3. Choose Cloudflared as the connector and name it (e.g., k3s-ipaymu-tunnel).
|
|
4. On the installation page, select Docker/Kubernetes. Look for the command and copy the long base64 token string after --token.
|
|
|
|
- Deploy Cloudflare Tunnel inside K3s
|
|
Save this to cloudflared.yaml (replace YOUR_TOKEN_HERE with your Cloudflare Zero Trust tunnel token) and run kubectl apply -f cloudflared.yaml:
|
|
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: cloudflared-token
|
|
namespace: default
|
|
type: Opaque
|
|
stringData:
|
|
TUNNEL_TOKEN: "YOUR_TOKEN_HERE"
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: cloudflared
|
|
namespace: default
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: cloudflared
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: cloudflared
|
|
spec:
|
|
nodeSelector:
|
|
egress-ip: static-ipaymu
|
|
containers:
|
|
- name: cloudflared
|
|
image: cloudflare/cloudflared:latest
|
|
args:
|
|
- tunnel
|
|
- --no-autoupdate
|
|
- run
|
|
env:
|
|
- name: TUNNEL_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cloudflared-token
|
|
key: TUNNEL_TOKEN
|
|
|
|
|
|
kubectl apply -f cloudflared.yaml
|
|
|
|
Step 2.5: Cloudflare Dashboard Routing
|
|
In Cloudflare Zero Trust Dashboard (→ Networks → Tunnels → Public Hostname):
|
|
|
|
Public Hostname: proxy.yourdomain.com
|
|
|
|
Path: /ipaymu
|
|
|
|
Type: HTTP
|
|
|
|
URL: traefik.kube-system.svc.cluster.local:80
|
|
```
|
|
#### Step by step using nginx and cloudflared in docker-compose
|
|
```
|
|
Step 3.1: Create Directory Structure
|
|
On your target server, create a project directory:
|
|
mkdir -p ipaymu-proxy
|
|
cd ipaymu-proxy
|
|
|
|
ipaymu-proxy/
|
|
├── docker-compose.yml
|
|
└── nginx.conf
|
|
|
|
Step 3.2: Create nginx.conf
|
|
events { worker_connections 1024; }
|
|
|
|
http {
|
|
upstream ipaymu_api {
|
|
server api.ipaymu.com:443;
|
|
keepalive 16;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
location /ipaymu/ {
|
|
# Check secret key header sent by your Cloudflare Worker
|
|
if ($http_x_proxy_secret != "YOUR_INTERNAL_SECRET_KEY") {
|
|
return 403;
|
|
}
|
|
|
|
proxy_pass https://ipaymu_api/;
|
|
proxy_set_header Host api.ipaymu.com;
|
|
|
|
# Ensure TLS handshake works with iPaymu
|
|
proxy_ssl_server_name on;
|
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
# HTTP Keepalive optimizations
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Step 3.3: Create docker-compose.yml
|
|
version: '3.8'
|
|
|
|
services:
|
|
ipaymu-proxy:
|
|
image: nginx:alpine
|
|
container_name: ipaymu_proxy
|
|
restart: always
|
|
ports:
|
|
- "80:80" # Or expose via reverse proxy / Cloudflare tunnel
|
|
volumes:
|
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
|
healthcheck:
|
|
test: ["CMD", "nginx", "-t"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
or with nginx:
|
|
|
|
#docker-compose.yml
|
|
version: '3.8'
|
|
services:
|
|
ipaymu-nginx:
|
|
image: nginx:alpine
|
|
container_name: ipaymu_nginx
|
|
restart: always
|
|
volumes:
|
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
|
networks:
|
|
- proxy-net
|
|
|
|
cloudflared:
|
|
image: cloudflare/cloudflared:latest
|
|
container_name: ipaymu_tunnel
|
|
restart: always
|
|
command: tunnel --no-autoupdate run
|
|
environment:
|
|
- TUNNEL_TOKEN=YOUR_CLOUDFLARE_TUNNEL_TOKEN_HERE
|
|
networks:
|
|
- proxy-net
|
|
|
|
networks:
|
|
proxy-net:
|
|
driver: bridge
|
|
|
|
Step 3.4: Deploy Docker Compose: docker compose up -d
|
|
Step 3.5: Cloudflare Dashboard RoutingIn Cloudflare Zero Trust Dashboard ($\rightarrow$ Networks $\rightarrow$ Tunnels $\rightarrow$ Public Hostname):
|
|
Public Hostname: proxy.yourdomain.com
|
|
Path: /ipaymuType: HTTP
|
|
URL: ipaymu_nginx:80 (Connects directly using Docker's internal container DNS).
|
|
|
|
```
|
|
|
|
#### Step by step using docker swarm
|
|
|
|
```
|
|
version: '3.8'
|
|
|
|
services:
|
|
ipaymu-proxy:
|
|
image: nginx:alpine
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
configs:
|
|
- source: nginx_config
|
|
target: /etc/nginx/conf.d/default.conf
|
|
deploy:
|
|
mode: replicated
|
|
replicas: 1
|
|
placement:
|
|
constraints:
|
|
# Pin execution to the specific node with the static public IP
|
|
- node.hostname == node-with-static-ip
|
|
|
|
configs:
|
|
nginx_config:
|
|
inline: |
|
|
server {
|
|
listen 80;
|
|
server_name ipaymu-proxy.yourdomain.com;
|
|
|
|
location /ipaymu/ {
|
|
if ($http_x_proxy_secret != "YOUR_INTERNAL_SECRET") {
|
|
return 403;
|
|
}
|
|
|
|
proxy_pass https://api.ipaymu.com/;
|
|
proxy_set_header Host api.ipaymu.com;
|
|
proxy_ssl_server_name on;
|
|
|
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
# Pass client details
|
|
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;
|
|
}
|
|
}
|
|
|
|
[Cloudflare Worker]
|
|
│
|
|
├─── /ipaymu/v2/payment ─────────► [Nginx] ──► https://my.ipaymu.com/api/v2/payment
|
|
│
|
|
└─── /ipaymu-sandbox/v2/payment ─► [Nginx] ──► https://sandbox.ipaymu.com/api/v2/payment
|
|
|
|
1.a Create the dedicated folder in /opt/
|
|
mkdir -p /opt/ipaymu-swarm-proxy
|
|
cd /opt/ipaymu-swarm-proxy
|
|
|
|
|
|
b create the nginx configuration file nginx.conf:
|
|
```
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
http {
|
|
# Upstream targets using persistent HTTP keepalives
|
|
upstream ipaymu_live {
|
|
server my.ipaymu.com:443;
|
|
keepalive 16;
|
|
}
|
|
|
|
upstream ipaymu_sandbox {
|
|
server sandbox.ipaymu.com:443;
|
|
keepalive 16;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
# -------------------------------------------------------------
|
|
# 1. Production / Live Endpoint
|
|
# -------------------------------------------------------------
|
|
location /ipaymu/v2/ {
|
|
# Security Header Check
|
|
if ($http_x_proxy_secret != "YOUR_INTERNAL_SECRET_KEY") {
|
|
return 403;
|
|
}
|
|
|
|
proxy_pass https://ipaymu_live/api/v2/;
|
|
proxy_set_header Host my.ipaymu.com;
|
|
|
|
# Ensure TLS/SNI Handshake matches my.ipaymu.com
|
|
proxy_ssl_server_name on;
|
|
proxy_ssl_name my.ipaymu.com;
|
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
# Connection optimizations
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
proxy_pass_request_headers on;
|
|
}
|
|
|
|
# -------------------------------------------------------------
|
|
# 2. Development / Sandbox Endpoint
|
|
# -------------------------------------------------------------
|
|
location /ipaymu-sandbox/v2/ {
|
|
# Security Header Check
|
|
if ($http_x_proxy_secret != "YOUR_INTERNAL_SECRET_KEY") {
|
|
return 403;
|
|
}
|
|
|
|
proxy_pass https://ipaymu_sandbox/api/v2/;
|
|
proxy_set_header Host sandbox.ipaymu.com;
|
|
|
|
# Ensure TLS/SNI Handshake matches sandbox.ipaymu.com
|
|
proxy_ssl_server_name on;
|
|
proxy_ssl_name sandbox.ipaymu.com;
|
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
# Connection optimizations
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
proxy_pass_request_headers on;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
2. Change ipaymu
|
|
```
|
|
# erstelle einen starker schlüssel YOUR_INTERNAL_SECRET_KEY:
|
|
openssl rand -hex 32
|
|
|
|
# Setze diesen Schlüssel in deinen Nginx-Code (nginx.conf):
|
|
if ($http_x_proxy_secret != "0fb1d9712f16bb64e0dff4f911af02e8a2280722c80097a845e248c7a49781d7") {
|
|
return 403;
|
|
}
|
|
|
|
Setze denselben Schlüssel in deinen Cloudflare Worker / App-Code:
|
|
|
|
const headers = {
|
|
"Content-Type": "application/json",
|
|
"X-Proxy-Secret": "0fb1d9712f16bb64e0dff4f911af02e8a2280722c80097a845e248c7a49781d7", // Gleicher Schlüssel!
|
|
// ...
|
|
};
|
|
|
|
|
|
CONTAINER_ID=$(docker ps -q -f name=ipaymu_nginx-proxy | head -n1)
|
|
docker exec -it $CONTAINER_ID apk add --no-cache curl
|
|
docker exec -it $CONTAINER_ID curl -4 ifconfig.me
|
|
|
|
curl -i -X POST https://proxy.yourdomain.com/ipaymu/v2/payment \
|
|
-H "X-Proxy-Secret: YOUR_INTERNAL_SECRET_KEY" \
|
|
-H "Content-Type: application/json"
|
|
```
|
|
1. Das Label auf den gewählten Node setzen
|
|
Sobald du weißt, welcher Node die statische IP hat (z. B. node-01), vergibst du das Label egress=static-ipaymu:
|
|
```
|
|
docker node update --label-add egress=static-ipaymu node-01
|
|
|
|
docker node update --label-add egress=static-ipaymu <node-name-or-id>
|
|
docker node inspect node-01 --format '{{ json .Spec.Labels }}'
|
|
```
|
|
|
|
4. Deploy And check
|
|
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiNjFhMTZkYzg3MmM4ZjlmZTQ4Mzc0ZWQ5NzVmNTcwNmIiLCJ0IjoiNTA5ZmRlZjQtZjEzYy00ZTQ1LTllZDQtYjZhMjExOTFlY2Y3IiwicyI6Ik5UWmtZVFU1T1dZdE5XVmtZUzAwTldZeExXSm1Oek10TlRRd1pqUTBNR1kxWW1RMSJ9
|
|
|
|
- Option 1 Using Docker Swarm env ( does not work )
|
|
```
|
|
version: '3.8'
|
|
|
|
services:
|
|
nginx-proxy:
|
|
image: nginx:alpine
|
|
configs:
|
|
- source: nginx_config_v1
|
|
target: /etc/nginx/nginx.conf
|
|
networks:
|
|
- payment-network
|
|
deploy:
|
|
mode: replicated
|
|
replicas: 2
|
|
placement:
|
|
constraints:
|
|
# Pin to the node holding your static IP for iPaymu whitelisting
|
|
- node.labels.egress == static-ipaymu
|
|
restart_policy:
|
|
condition: on-failure
|
|
|
|
cloudflared:
|
|
image: cloudflare/cloudflared:latest
|
|
command: tunnel --no-autoupdate run
|
|
environment:
|
|
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
|
|
networks:
|
|
- payment-network
|
|
deploy:
|
|
mode: replicated
|
|
replicas: 2
|
|
placement:
|
|
constraints:
|
|
- node.labels.egress == static-ipaymu
|
|
restart_policy:
|
|
condition: on-failure
|
|
|
|
configs:
|
|
nginx_config_v1:
|
|
file: ./nginx.conf
|
|
|
|
networks:
|
|
payment-network:
|
|
driver: overlay
|
|
attachable: true
|
|
```
|
|
create .env file in the same directory:
|
|
sudo nano /opt/ipaymu-swarm-proxy/.env
|
|
CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiNjFhMTZkYzg3MmM4ZjlmZTQ4Mzc0ZWQ5NzVmNTcwNmIiLCJ0IjoiNTA5ZmRlZjQtZjEzYy00ZTQ1LTllZDQtYjZhMjExOTFlY2Y3IiwicyI6Ik5UWmtZVFU1T1dZdE5XVmtZUzAwTldZeExXSm1Oek10TlRRd1pqUTBNR1kxWW1RMSJ9
|
|
|
|
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiNjFhMTZkYzg3MmM4ZjlmZTQ4Mzc0ZWQ5NzVmNTcwNmIiLCJ0IjoiNTA5ZmRlZjQtZjEzYy00ZTQ1LTllZDQtYjZhMjExOTFlY2Y3IiwicyI6Ik5UWmtZVFU1T1dZdE5XVmtZUzAwTldZeExXSm1Oek10TlRRd1pqUTBNR1kxWW1RMSJ9
|
|
|
|
docker stack deploy -c ipaymu-swarm.yml ipaymu
|
|
or
|
|
export $(cat /opt/ipaymu-swarm-proxy/.env | xargs) && sudo -E docker stack deploy -c ipaymu-swarm.yml ipaymu
|
|
|
|
or
|
|
|
|
set -a && source /opt/ipaymu-swarm-proxy/.env && set +a && sudo -E docker stack deploy -c ipaymu-swarm.yml ipaymu
|
|
|
|
or
|
|
|
|
CLOUDFLARE_TUNNEL_TOKEN="eyJhIjoiNjFhMTZkYzg3MmM4ZjlmZTQ4Mzc0ZWQ5NzVmNTcwNmIiLCJ0IjoiNTA5ZmRlZjQtZjEzYy00ZTQ1LTllZDQtYjZhMjExOTFlY2Y3IiwicyI6Ik5UWmtZVFU1T1dZdE5XVmtZUzAwTldZeExXSm1Oek10TlRRd1pqUTBNR1kxWW1RMSJ9" sudo -E docker stack deploy -c ipaymu-swarm.yml ipaymu
|
|
|
|
# Option 2 Docker Swarm Secret
|
|
Create a Docker Secret on your Manager node:
|
|
printf "your_actual_token_here" | docker secret create cloudflared_tunnel_token -
|
|
|
|
version: '3.8'
|
|
|
|
services:
|
|
cloudflared:
|
|
image: cloudflare/cloudflared:latest
|
|
# Tells cloudflared to read the token file created by Docker Secrets
|
|
command: tunnel --no-autoupdate run --token-file /run/secrets/cloudflared_token
|
|
secrets:
|
|
- source: cloudflared_tunnel_token
|
|
target: cloudflared_token
|
|
networks:
|
|
- ipaymu-overlay
|
|
deploy:
|
|
mode: replicated
|
|
replicas: 2
|
|
placement:
|
|
constraints:
|
|
- node.labels.egress == static-ipaymu
|
|
|
|
secrets:
|
|
cloudflared_tunnel_token:
|
|
external: true
|
|
|
|
Summary Recommendation
|
|
For simple setups: Use Method 1 (.env file).
|
|
For strict security requirements: Use Method 4 (Docker Secrets).
|
|
|
|
docker stack services ipaymu
|
|
docker service logs ipaymu_cloudflared --tail 20
|
|
|
|
Now you need to label the node like in docker-stack.yml:
|
|
docker node update --label-add egress=static-ipaymu <node-hostname-or-id>
|
|
|
|
docker node update --label-add egress=static-ipaymu invixel-debian1
|
|
docker node update --label-add egress=static-ipaymu invixel-vm5
|
|
|
|
How to verify the exit IP
|
|
After deploying to K3s or Swarm, exec into your Nginx container/pod and test what public IP iPaymu will see:
|
|
|
|
docker exec -it <container_id> curl -4 ifconfig.me
|
|
|
|
kubectl exec -it deployment/ipaymu-proxy -- curl -4 ifconfig.me
|
|
|
|
|
|
curl -i -X POST https://bali-car-dev.easy-rent.com/ipaymu/v2/payment \
|
|
-H "X-Proxy-Secret: 0fb1d9712f16bb64e0dff4f911af02e8a2280722c80097a845e248c7a49781d7" \
|
|
-H "Content-Type: application/json"
|
|
``` |