Skip to content

Repository files navigation

ip-pool-operator

A Kubernetes operator that turns remote IP/CIDR allowlists (CDN edge ranges, cloud provider IP lists, partner allowlists, etc.) into NetworkPolicy resources — kept automatically in sync on a schedule.

Define one IPPoolPolicy custom resource pointing at one or more text URLs (and/or inline CIDRs), tell it which namespaces/pods to target, and the operator periodically fetches the lists, normalizes the entries, and creates/updates a NetworkPolicy in every matching namespace.

Why

Allowing traffic only from a CDN's or cloud provider's published edge IPs is a common requirement, but those IP ranges change over time. Hand-maintaining NetworkPolicy CIDR blocks (and remembering to update them) doesn't scale. ip-pool-operator automates that loop:

[ IP/CIDR list URL(s) ] --(fetch on interval)--> [ IPPoolPolicy ] --(reconcile)--> [ NetworkPolicy per namespace ]

Features

  • Multiple sources per policy — combine any number of URLs and inline CIDRs; results are merged, deduplicated, and normalized (bare IPs become /32 or /128).
  • Comment support — source lists may use # comments (full-line or inline).
  • Flexible targeting — select namespaces with a namespaceSelector and, within them, a podSelector (defaults to all pods).
  • Ingress, Egress, or both — and any set of ports/protocols.
  • Periodic refresh — configurable refreshInterval (default 1h) re-fetches the source lists and updates policies only when something actually changed.
  • Clean teardown — deleting an IPPoolPolicy removes every NetworkPolicy it owns via a finalizer.
  • Status reportingkubectl get ippp shows direction, ports, selectors, and last sync time at a glance.

How it works

  1. You create an IPPoolPolicy resource (cluster-scoped).
  2. The controller fetches every URL in spec.source.urls, merges in spec.source.inlineCIDRs, strips comments/blank lines, and normalizes each entry to a CIDR.
  3. It lists namespaces matching spec.target.namespaceSelector.
  4. For each matching namespace, it creates/updates a NetworkPolicy named <namePrefix><policy-name> (default prefix ippool-) that allows the configured ports to/from the resolved CIDRs, scoped by spec.target.podSelector.
  5. It requeues itself after spec.refreshInterval to repeat the cycle.

A content hash is stored on the NetworkPolicy so unchanged source lists don't cause needless updates.

Quickstart

Install

The latest install manifest (CRDs + RBAC + controller Deployment) is published as a release asset. Pick a tag from the Releases page and apply it:

kubectl apply -f https://github.com/SepehrImanian/ip-pool-operator/releases/latest/download/install.yaml

This creates a ip-pool-operator-system namespace and runs the controller there.

Create a policy

apiVersion: net.techonfire.ir/v1alpha1
kind: IPPoolPolicy
metadata:
  name: cdn-edge-ingress
spec:
  refreshInterval: 30m
  source:
    urls:
      - https://www.arvancloud.ir/en/ips.txt
  target:
    namespaceSelector:
      matchLabels:
        enforce-ip-allowlist: "true"
    podSelector:
      matchLabels:
        app: nginx
  policy:
    types: [Ingress]
    namePrefix: ippool-
    ports:
      - port: 443
        protocol: TCP
      - port: 80
        protocol: TCP
kubectl apply -f cdn-edge-ingress.yaml
kubectl label namespace web enforce-ip-allowlist=true

More worked examples are in config/samples/, including:

Any provider that publishes its IP ranges as a plain-text, one-CIDR-per-line URL works the same way — just point spec.source.urls at it. Providers that only publish ranges as JSON (e.g. AWS, Fastly, Akamai) aren't supported directly today; you'd need to mirror their list as plain text somewhere the operator can fetch it.

Check status

kubectl get ippoolpolicies
# NAME               DIR       PORTS        POD-SELECTOR   NS-SELECTOR
# cdn-edge-ingress    Ingress   TCP:80,TCP:443   app=nginx     enforce-ip-allowlist=true

Uninstall

kubectl delete ippoolpolicy --all
kubectl delete -f https://github.com/SepehrImanian/ip-pool-operator/releases/latest/download/install.yaml

Deleting an IPPoolPolicy first removes the NetworkPolicy objects it created in every target namespace, via a finalizer.

Note: A NetworkPolicy is only enforced if your cluster's CNI supports NetworkPolicy (e.g. Calico, Cilium). Clusters without a network-policy-aware CNI will accept these resources but won't enforce them.

Configuration reference

spec.source

Field Type Description
urls []string One or more URLs serving plain text, one IP/CIDR per line. # starts a comment (full-line or inline).
inlineCIDRs []string Additional IPs/CIDRs to merge in directly.
httpHeaders map[string]string Headers sent with each URL request (e.g. Authorization).

If both urls and inlineCIDRs are set, their results are unioned and deduplicated.

spec.target

Field Type Description
namespaceSelector LabelSelector Namespaces to apply the policy in. If omitted, no namespaces match — be explicit.
podSelector LabelSelector Pods within each matched namespace the policy applies to. Empty/omitted = all pods.

spec.policy

Field Type Description
types []string One or both of Ingress, Egress. Required.
ports []NetworkPolicyPort Ports/protocols applied to the generated rule(s).
namePrefix string Prefix for generated NetworkPolicy names. Default ippool-.

spec.refreshInterval

metav1.Duration (e.g. "30m", "1h"). How often to re-fetch sources and reconcile. Default 1h.

status

observedGeneration, lastSyncTime, effectiveCIDRs, namespaces, policies, hash, plus the human-readable dirString/portList/podSelectorString/nsSelectorString columns shown by kubectl get.

The full schema is in config/crd/bases/net.techonfire.ir_ippoolpolicies.yaml.

RBAC

The controller needs to read namespaces and manage IPPoolPolicy and NetworkPolicy resources cluster-wide; see config/rbac/role.yaml for the exact rules. All of this is included in install.yaml.

Releases

Tagged releases (vX.Y.Z) publish two artifacts:

  • A multi-arch (linux/amd64, linux/arm64) controller image to ghcr.io/<owner>/ip-pool-operator:<tag>.
  • A consolidated install.yaml (CRDs + RBAC + Deployment) attached to the GitHub Release, for one-command installs.

See the Releases page for available versions. :latest tracks the most recent build of main.

Development

Prerequisites

  • Go v1.22+
  • Docker (or another OCI tool) v17.03+
  • kubectl v1.11.3+
  • A Kubernetes v1.11.3+ cluster (e.g. kind — see kind.yaml)

Run locally against a cluster

make install   # apply CRDs
make run       # run the controller out-of-cluster, using your current kubeconfig

Run tests

make test      # unit/envtest suite
make test-e2e  # e2e suite (spins up a kind cluster)

Build and deploy a custom image

make docker-build docker-push IMG=<registry>/ip-pool-operator:tag
make deploy IMG=<registry>/ip-pool-operator:tag

Build the installer manifest locally

make build-installer IMG=<registry>/ip-pool-operator:tag
# -> dist/install.yaml

Run make help for the full list of targets. This project is scaffolded with Kubebuilder; its conventions (config/, Makefile targets, etc.) follow the standard Kubebuilder layout.

Contributing

Contributions are welcome — see CONTRIBUTING.md for how to set up your environment, run tests, and submit a pull request.

License

Licensed under the Apache License, Version 2.0.

About

Automatically turn remote IP/CIDR allowlists (Cloudflare, BunnyCDN, ...) into Kubernetes NetworkPolicies and keep them in sync

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages