A Kubernetes Ingress Controller that exposes services via Pangolin by creating PangolinResource CRDs.
PIC enables a Kubernetes-native experience for exposing services through Pangolin. Instead of manually configuring Pangolin, you simply create a standard Kubernetes Ingress resource.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
spec:
ingressClassName: pangolin
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 8080apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
pangolin.ingress.k8s.io/sso: "true"
pangolin.ingress.k8s.io/block-access: "true"
spec:
ingressClassName: pangolin
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 8080Expose multiple domains from a single Ingress resource. PIC creates one PangolinResource per host:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-domain-app
spec:
ingressClassName: pangolin
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 8080
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin
port:
number: 8080This creates 3 separate PangolinResource objects:
pic-default-multi-domain-app-<hash1>forapp.example.compic-default-multi-domain-app-<hash2>forapi.example.compic-default-multi-domain-app-<hash3>foradmin.example.com
All resources are garbage collected when the Ingress is deleted.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
pangolin.ingress.k8s.io/sso: "false"
spec:
ingressClassName: pangolin
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /web
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80PIC will automatically create a PangolinResource with multiple targets, each with path-based routing configured.
- Kubernetes 1.28+
pangolin-operatorinstalled with CRDs- At least one
PangolinTunnelconfigured
helm install pic ./charts/pangolin-ingress-controller \
--namespace pangolin-system \
--create-namespacehelm install pic ./charts/pangolin-ingress-controller \
--namespace pangolin-system \
--create-namespace \
--set config.defaultTunnelName=my-tunnel \
--set config.logLevel=debugkubectl apply -f https://raw.githubusercontent.com/stefb69/pangolin-ingress-controller/main/deploy/install.yaml| Variable | Default | Description |
|---|---|---|
PIC_DEFAULT_TUNNEL_NAME |
default |
Tunnel for ingressClassName: pangolin |
PIC_TUNNEL_CLASS_MAPPING |
- | Multi-tunnel mapping (see below) |
PIC_BACKEND_SCHEME |
http |
Backend protocol |
PIC_RESYNC_PERIOD |
5m |
Reconciliation interval |
PIC_LOG_LEVEL |
info |
Log level |
PIC_WATCH_NAMESPACES |
- | Limit to specific namespaces |
env:
- name: PIC_TUNNEL_CLASS_MAPPING
value: |
eu=tunnel-eu
us=tunnel-usThen use ingressClassName: pangolin-eu to route through tunnel-eu.
| Annotation | Default | Description |
|---|---|---|
pangolin.ingress.k8s.io/enabled |
true |
Enable/disable PIC processing |
pangolin.ingress.k8s.io/tunnel-name |
- | Override tunnel name |
pangolin.ingress.k8s.io/domain-name |
- | Override domain |
pangolin.ingress.k8s.io/subdomain |
- | Override subdomain |
pangolin.ingress.k8s.io/sso |
false |
Enable SSO authentication |
pangolin.ingress.k8s.io/block-access |
false |
Block access until authenticated (requires sso: true) |
Pangolin supports SSO authentication to protect your services. Use the following annotations:
sso: "false"- Service is publicly accessible (default)sso: "true"- SSO is enabled, users see identity but access is allowedsso: "true"+block-access: "true"- Users must authenticate before accessing
PIC supports multiple paths per Ingress. Each path creates a separate target in Pangolin with:
- Path: The URL path to match (e.g.,
/api) - PathMatchType: Derived from Ingress
pathType(Exact→exact,Prefix→prefix) - Priority: Automatically calculated based on path length (longer paths = higher priority)
# Build
make build
# Test
make test
# Run locally (requires kubeconfig)
make run
# Build Docker image
make docker-buildIngress ──▶ PIC ──▶ PangolinResource ──▶ pangolin-operator ──▶ Pangolin API
PIC only manages PangolinResource objects. All Pangolin API interaction is handled by pangolin-operator.
| Component | Responsibility |
|---|---|
| PIC | Watches Ingress resources, creates/updates PangolinResource CRDs |
| pangolin-operator | Watches PangolinResource CRDs, calls Pangolin API to create resources/targets |
| Pangolin API | Manages the actual tunnel routing and SSO configuration |
kubectl get pangolinresources -A
kubectl describe pangolinresource <name> -n <namespace># PIC logs
kubectl logs -n pangolin-system -l app.kubernetes.io/name=pangolin-ingress-controller
# Operator logs
kubectl logs -n pangolin-operator-system -l control-plane=controller-managerkubectl annotate ingress <name> reconcile=$(date +%s) --overwriteApache 2.0