[{"data":1,"prerenderedAt":20},["ShallowReactive",2],{"blog:post:en:using-proxies-to-validate-zero-trust-network-policies-in-kubernetes":3},{"slug":4,"lang":5,"title":6,"summary":7,"date":8,"tags":9,"tag_slugs":15,"thumbnail_url":16,"translations":17,"body":18,"asset_base":19},"using-proxies-to-validate-zero-trust-network-policies-in-kubernetes","en","Using Proxies to Validate Zero‑Trust Network Policies in Kubernetes","Learn how to route Envoy sidecar traffic through rotating proxies to test zero‑trust policies, simulate external origins, and verify policy enforcement without touching your application code.","2026-09-28",[10,11,12,13,14],"kubernetes","zero-trust","proxies","envoy","networking",[10,11,12,13,14],"https://blog-api.ro-proxy.com/api/blog/posts/using-proxies-to-validate-zero-trust-network-policies-in-kubernetes/thumbnail.svg?lang=en",[5],"## Introduction\nZero‑trust security assumes that no network segment is inherently trusted. In Kubernetes this often translates to policies in Envoy or Istio that require JWT tokens, source IP ranges, or mutual TLS before allowing a request to reach a service. Testing these policies reliably means you need to generate traffic that appears to come from outside the cluster, from various IP addresses, and sometimes with specific headers. Rotating proxies give you a convenient way to emulate such external clients while keeping your test suite deterministic and repeatable.\n\n## Why Use Proxies for Zero‑Trust Validation\n- **IP diversity** – A proxy pool can supply addresses from many countries, letting you verify geo‑based restrictions.\n- **Rotation on demand** – You can change the source IP for each request to simulate brute‑force or credential‑stuffing attempts.\n- **Isolation** – The application code stays unchanged; all traffic redirection happens at the sidecar level.\n- **Cost‑effective** – Compared with spinning up real VMs in multiple clouds, a proxy service is cheaper and easier to manage.\n\n## Architecture Overview\nYour workload runs in a pod with an Envoy sidecar. The sidecar is configured to forward outbound traffic through an HTTP proxy before reaching the target service. The proxy rotates its outgoing IP address according to the credentials you provide. The flow looks like:\n\nPod (app) → Envoy sidecar (listener) → HTTP proxy → Target service (e.g., httpbin.org/ip)\n\nBecause the proxy sits between Envoy and the destination, Envoy sees the proxy as the immediate peer, but the final destination sees the proxy’s IP. This lets you test policies that depend on the client IP observed by the service.\n\n## Prerequisites\n- A Kubernetes cluster (v1.22+)\n- kubectl configured to access the cluster\n- Istio installed **or** the ability to inject a raw Envoy sidecar via a pod annotation\n- Access to a rotating residential or datacenter proxy provider that offers HTTP/HTTPS endpoints with username/password authentication\n- curl and a simple test service (we will use the public httpbin.org/ip endpoint)\n\n## Step‑by‑Step Guide\n\n### 1. Deploy a Test Service\nWe will deploy a tiny echo service that returns the request headers and source IP as seen by the service. Save this as `test-service.yaml`:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: echo\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: echo\n  template:\n    metadata:\n      labels:\n        app: echo\n    spec:\n      containers:\n      - name: echo\n        image: hashicorp/http-echo\n        args:\n        - \"-text=hello from echo\"\n        ports:\n        - containerPort: 5678\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: echo\nspec:\n  selector:\n    app: echo\n  ports:\n  - port: 80\n    targetPort: 5678\n    protocol: TCP\n```\n\nApply it:\n\n```bash\nkubectl apply -f test-service.yaml\n```\n\nWait for the pod to be running.\n\n### 2. Inject Envoy Sidecar\nIf you already have Istio with automatic sidecar injection, label the namespace:\n\n```bash\nkubectl label namespace default istio-injection=enabled\n```\n\nOtherwise you can manually add a sidecar container. Below is a minimal Envoy configuration that forwards all outbound traffic to an HTTP proxy. Save it as `envoy-sidecar.yaml`:\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: echo-with-proxy\n  annotations:\n    sidecar.istio.io/inject: \"false\"\nspec:\n  containers:\n  - name: echo\n    image: hashicorp/http-echo\n    args:\n    - \"-text=hello from echo\"\n    ports:\n    - containerPort: 5678\n  - name: envoy\n    image: envoyproxy/envoy:v1.25-latest\n    command: [\"/usr/local/bin/envoy\"]\n    args:\n    - \"-c\"\n    - \"/etc/envoy/envoy.yaml\"\n    - \"--service-cluster\"\n    - \"echo-svc\"\n    - \"--service-node\"\n    - \"echo-node\"\n    volumeMounts:\n    - name: envoy-config\n      mountPath: /etc/envoy\n      readOnly: true\n    - name: proxy-auth\n      mountPath: /etc/proxy\n      readOnly: true\n  volumes:\n  - name: envoy-config\n    configMap:\n      name: envoy-config\n  - name: proxy-auth\n    secret:\n      secretName: proxy-cred\n```\n\nWe will create the ConfigMap and Secret in the next steps.\n\n### 3. Configure Envoy to Forward Traffic via HTTP Proxy\nCreate a ConfigMap named `envoy-config` with the following Envoy YAML (note the use of single quotes for strings inside the JSON‑like structure – Envoy accepts YAML, so we keep double quotes only where required by the YAML syntax; we avoid extra double quotes in explanatory text):\n\n```yaml\nstatic_resources:\n  listeners:\n  - name: listener_0\n    address:\n      socket_address:\n        address: 0.0.0.0\n        port_value: 15001\n    filter_chains:\n    - filters:\n    - name: envoy.filters.network.http_connection_manager\n      typed_config:\n        \"@type\": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager\n        stat_prefix: ingress_http\n        route_config:\n          name: local_route\n          virtual_hosts:\n          - name: backend\n            domains: [\"*\"]\n            routes:\n            - match:\n                prefix: \"/\"\n              route:\n                cluster: proxy_cluster\n        http_filters:\n    - name: envoy.filters.router\n  clusters:\n  - name: proxy_cluster\n    connect_timeout: 0.25s\n    type: LOGICAL_DNS\n    lb_policy: ROUND_ROBIN\n    load_assignment:\n      cluster_name: proxy_cluster\n      endpoints:\n      - lb_endpoints:\n        - endpoint:\n            address:\n              socket_address:\n                address: proxy.example.com  # replace with your proxy host\n                port_value: 8000\n    # Optional TLS upstream if your proxy requires it\n    # transport_socket:\n    #   name: envoy.transport_sockets.tls\n    #   typed_config:\n    #     \"@type\": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext\n```\n\nReplace `proxy.example.com` and port with your provider’s endpoint.\n\nCreate the ConfigMap:\n\n```bash\nkubectl create configmap envoy-config --from-file=envoy.yaml=\u003Cpath-to-above-yaml>\n```\n\n### 4. Set Up Rotating Proxy Credentials\nMost providers give you a username and password (or token) that you must include in the Proxy‑Authorization header. Create a Kubernetes Secret named `proxy-cred`:\n\n```bash\nkubectl create secret generic proxy-cred \\\n  --from-literal=username=YOUR_USERNAME \\\n  --from-literal=password=YOUR_PASSWORD\n```\n\nNow we need Envoy to add this header to every outbound request. Extend the listener configuration with a `request_headers_to_add` entry. Add the following under `http_filters` in the ConfigMap (still YAML, no extra double quotes in comments):\n\n```yaml\n        http_filters:\n        - name: envoy.filters.router\n          typed_config:\n            \"@type\": type.googleapis.com/envoy.extensions.filters.router.v3.Router\n        - name: envoy.filters.headers\n          typed_config:\n            \"@type\": type.googleapis.com/envoy.extensions.filters.headers.v3.HeaderModifier\n            request_headers_to_add:\n            - header:\n                key: \"Proxy-Authorization\"\n                value: \"Basic \"\n                append: false\n```\n\nThe actual Base64‑encoded `username:password` must be computed and placed in the value. You can do this once and store the result in the secret, then have Envoy read it via a header value plugin, but for simplicity we pre‑compute:\n\n```bash\nprintf \"YOUR_USERNAME:YOUR_PASSWORD\" | base64\n```\n\nSuppose the output is `eW91cl91c2VybmFtZTpZb3VyX3Bhc3N3b3Jk`. Update the ConfigMap so the header value is `Basic eW91cl91c2VybmFtZTpZb3VyX3Bhc3N3b3Jk`.\n\n### 5. Apply Configuration and Test\nDeploy the pod with the sidecar:\n\n```bash\nkubectl apply -f envoy-sidecar.yaml\n```\n\nWait for both containers to be Ready. Then open a shell in the echo container:\n\n```bash\nkubectl exec -it echo-with-proxy -c echo -- sh\n```\n\nFrom inside the container, call the echo service via localhost:15001 (Envoy listener) which will forward through the proxy to httpbin.org/ip:\n\n```bash\ncurl -s http://localhost:15001/https://httpbin.org/ip\n```\n\nYou should see a JSON response like:\n\n```json\n{\n  \"origin\": \"203.0.113.45\"\n}\n```\n\nRun the command a few times; the IP address should change if your proxy pool rotates per request or per connection.\n\n### 6. Validate Zero‑Trust Policy\nNow suppose you have an Envoy RBAC policy that only allows inbound traffic to the echo service from the IP range `10.0.0.0/8` (simulating an internal trusted network). The policy might look like this in an Istio `AuthorizationPolicy`:\n\n```yaml\napiVersion: security.istio.io/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n  name: echo-internal-only\nspec:\n  selector:\n    matchLabels:\n      app: echo\n  action: DENY\n  rules:\n  - when:\n    - key: source.ip\n      notInRanges: [\"10.0.0.0/8\"]\n```\n\nApply this policy. Then, from within the pod, try to reach the echo service via the proxy (which will appear as an external IP). The request should be denied with a 403 response.\n\n```bash\ncurl -s -o /dev/null -w \"%{http_code}\" http://localhost:15001/http://echo:80/hello\n```\n\nYou should see `403`. Now temporarily add a proxy IP that falls inside `10.0.0.0/8` (some providers let you select datacenter IPs in specific ranges) or configure the proxy to use a static IP for a test. If the policy is working, the request will succeed (`200`). This demonstrates that your zero‑trust rule is enforced correctly.\n\n### 7. Automate Rotation Detection\nTo continuously verify that the proxy is rotating, you can run a small Python script inside the pod:\n\n```python\nimport time\nimport requests\n\nPROXY = \"http://proxy.example.com:8000\"\nAUTH = \"Basic eW91cl91c2VybmFtZTpZb3VyX3Bhc3N3b3Jk\"\n\nfor i in range(10):\n    proxies = {\"http\": PROXY, \"https\": PROXY}\n    headers = {\"Proxy-Authorization\": AUTH}\n    try:\n        r = requests.get(\"http://httpbin.org/ip\", proxies=proxies, headers=headers, timeout=5)\n        print(f\"Iter {i}: {r.json()}\")\n    except Exception as e:\n        print(f\"Error: {e}\")\n    time.sleep(2)\n```\n\nRun it with `python3 script.py`. You should see different `origin` values appearing over time.\n\n## Real‑World Example: Financial‑Services API\nA fintech company needed to verify that their transaction API blocked requests originating from known malicious IP ranges while allowing legitimate partners. They deployed the API behind an Envoy sidecar in a test cluster, configured the sidecar to use a rotating residential proxy pool, and ran a CI job that:\n\n1. Cycled through a list of known bad IPs (provided by the proxy’s geo‑targeting feature).\n2. Asserted each request returned HTTP 403.\n3. Switched to a set of whitelisted partner IPs and asserted HTTP 200.\n\nBecause the proxy rotation was automated, the test suite could be run nightly against fresh IP addresses, giving confidence that the zero‑trust policy stayed up‑to‑date as the threat landscape changed.\n\n## Troubleshooting\n- **Proxy authentication failures** – Ensure the Base64 string in the `Proxy-Authorization` header is correct. A common mistake is forgetting the `Basic ` prefix or adding extra whitespace.\n- **TLS handshake errors** – If your proxy requires HTTPS, add a `transport_socket` of type `tls` to the `proxy_cluster` and provide the necessary root certificates via a secret mounted into Envoy.\n- **DNS leaks** – Envoy will resolve the proxy host name inside the pod. Make sure the pod’s `/etc/resolv.conf` points to a DNS server you trust; otherwise the proxy hostname could be resolved locally, exposing your intent.\n- **Empty responses** – Verify that the listener port (15001 in the example) matches the container port you are curling. Use `kubectl logs \u003Cpod> -c envoy` to see Envoy’s error logs.\n\n## Best Practices\n- Keep the proxy pool size adequate for your test concurrency; otherwise you may see HTTP 429 (Too Many Requests) from the proxy provider.\n- Monitor proxy health via the provider’s API or by periodically checking the returned IP; replace unhealthy nodes automatically.\n- If you need sticky sessions (e.g., to maintain login state), configure the proxy to retain the same IP for a configurable duration and set Envoy’s `max_requests_per_connection` accordingly.\n- Use a sidecar metric export (Envoy stats) to capture request counts, response codes, and latency; plug these into Prometheus/Grafana for observability.\n- Rotate credentials regularly and store them as Kubernetes Secrets, never hard‑code them in manifests.\n\n## Conclusion\nBy placing a rotating HTTP proxy between Envoy sidecars and your services, you gain a flexible, low‑cost way to emulate external traffic sources for zero‑trust policy validation. The approach requires no changes to your application code, works with any language or framework, and integrates naturally into CI/CD pipelines. With the steps outlined above you can confidently test IP‑based allow/deny lists, geo‑restrictions, and other network‑level controls before they reach production.\n","https://blog-api.ro-proxy.com/api/blog/posts/using-proxies-to-validate-zero-trust-network-policies-in-kubernetes/assets",1790578728209]