29 KiB
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.
- 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.
- 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.
- 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 |
| Ingress | Layer 7 | A ""Receptionist"" directing visitors based on the name on the |
| 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
- 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 if calling Traefik/Ingress, or 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]
- 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.
- 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 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"