Skip to content

Latest commit

 

History

History
380 lines (317 loc) · 11 KB

INGRESS.md

File metadata and controls

380 lines (317 loc) · 11 KB

Orcinus — Ingress & Traefik Middlewares

Orcinus ships Traefik as the runtime's native ingress controller. This guide covers how to expose services, do path routing / prefix stripping, and attach Traefik middlewares (rate limit, headers, auth, redirects, CORS, and more).

For the command reference see USAGE.md; for HTTPS/Let's Encrypt see PLUGINS.md.


Table of Contents


1. Exposing a service

Add ingress hints to a compose service with x-orcinus-* keys:

services:
  web:
    image: nginx:1.27
    ports: ["80"]
    x-orcinus-expose: ingress        # create an Ingress for this service
    x-orcinus-host: app.example.com  # the host to route
    x-orcinus-path: /                # path (default "/")
    x-orcinus-port: 80               # backend service port (defaults from ports:)
    x-orcinus-ingress-class: traefik # ingress class (default: the cluster's)
    x-orcinus-tls: letsencrypt       # cert-manager ClusterIssuer (HTTPS) — see PLUGINS.md
Key Purpose
x-orcinus-expose: ingress Generate an Ingress for the service
x-orcinus-host Host(s) to route — one, or several comma-separated (see §1a)
x-orcinus-path Path prefix (default /)
x-orcinus-port Backend service port
x-orcinus-ingress-class Ingress class (traefik by default in-cluster)
x-orcinus-tls / x-orcinus-tls-secret HTTPS via cert-manager / an existing cert
x-orcinus-strip-prefix Strip the path prefix (Traefik StripPrefix) — §2
x-orcinus-middleware Attach Traefik middleware(s) by name — §3

1a. Multiple domains for one service

Point several domains at the same service by listing them comma-separated in x-orcinus-host:

services:
  web:
    image: nginx:1.27
    ports: ["80"]
    x-orcinus-expose: ingress
    x-orcinus-host: "shop.com,www.shop.com,api.shop.com"
    x-orcinus-tls: letsencrypt

This generates one Ingress with a routing rule per host, and — when x-orcinus-tls / x-orcinus-tls-secret is set — a TLS block that covers all the hosts (cert-manager requests a certificate for every domain). Path, port, strip-prefix and middlewares apply to all of them.


2. Path routing & prefix stripping

Route a service under a sub-path with x-orcinus-path:

services:
  api:
    image: myapi:1.0
    ports: ["8080"]
    x-orcinus-expose: ingress
    x-orcinus-host: shop.example.com
    x-orcinus-path: /api            # https://shop.example.com/api → this service

The catch: the request still arrives at the backend as /api/.... Most apps expect to receive /.... x-orcinus-strip-prefix fixes that — it generates a Traefik StripPrefix middleware and attaches it, so the backend sees the path with the prefix removed:

    x-orcinus-path: /api
    x-orcinus-strip-prefix: true     # backend receives "/", not "/api"

x-orcinus-strip-prefix accepts:

Form Meaning
true Strip whatever x-orcinus-path is (e.g. /api)
"/api" Strip this explicit prefix
["/v1", "/v2"] Strip any of these prefixes

Orcinus generates a Middleware named <service>-stripprefix and lists it first in the middleware chain (so everything downstream sees the stripped path).

See examples/traefik-middleware/.


3. Attaching Traefik middlewares

Traefik behaviour beyond stripping — rate limiting, auth, headers, redirects — is expressed as Middleware custom resources. You define the middleware once, then attach it to a service with x-orcinus-middleware (a name or a list, in order):

services:
  api:
    image: myapi:1.0
    ports: ["8080"]
    x-orcinus-expose: ingress
    x-orcinus-host: shop.example.com
    x-orcinus-middleware: [ratelimit, secure-headers]   # applied in this order

---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: ratelimit
spec:
  rateLimit: { average: 50, burst: 100 }

Because orcinus deploy classifies each YAML document independently, you can keep the compose service and its Middleware CRDs in the same file (separated by ---) and deploy them together — no separate kubectl apply needed.

Order of the chain orcinus builds: strip-prefix first (if any), then the x-orcinus-middleware list, left to right.


4. Middleware catalog

Common Traefik middlewares (apiVersion: traefik.io/v1alpha1). Define any of these and attach with x-orcinus-middleware: [<name>].

Path: StripPrefix / AddPrefix / ReplacePathRegex

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: strip-api }
spec:
  stripPrefix:
    prefixes: ["/api"]        # remove a leading prefix (also via x-orcinus-strip-prefix)
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: add-api }
spec:
  addPrefix:
    prefix: "/api"            # prepend a prefix before hitting the backend
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: rewrite }
spec:
  replacePathRegex:
    regex: "^/old/(.*)"       # rewrite the path
    replacement: "/new/$1"

Redirects: scheme & regex

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: https-redirect }
spec:
  redirectScheme:
    scheme: https
    permanent: true           # 301 http → https
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: apex-to-www }
spec:
  redirectRegex:
    regex: "^https://example\\.com/(.*)"
    replacement: "https://www.example.com/${1}"
    permanent: true

Headers & CORS

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: secure-headers }
spec:
  headers:
    stsSeconds: 31536000
    stsIncludeSubdomains: true
    contentTypeNosniff: true
    browserXssFilter: true
    frameDeny: true
    referrerPolicy: same-origin
    customRequestHeaders:
      X-Forwarded-Proto: https
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: cors }
spec:
  headers:
    accessControlAllowMethods: ["GET", "POST", "OPTIONS"]
    accessControlAllowOriginList: ["https://app.example.com"]
    accessControlAllowHeaders: ["Content-Type", "Authorization"]
    accessControlMaxAge: 600
    addVaryHeader: true

Auth: basic / forward

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: dashboard-auth }
spec:
  basicAuth:
    secret: dashboard-users   # Secret with `users` = htpasswd lines (bcrypt)
---
# Create the secret:  htpasswd -nbB admin 's3cret'  → put the line under stringData.users
apiVersion: v1
kind: Secret
metadata: { name: dashboard-users }
stringData:
  users: |
    admin:$2y$05$....(bcrypt)....
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: forward-auth }
spec:
  forwardAuth:
    address: http://authelia.default.svc:9091/api/verify
    authResponseHeaders: ["Remote-User", "Remote-Groups"]

Rate limit & in-flight

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: ratelimit }
spec:
  rateLimit:
    average: 50               # req/s sustained
    burst: 100                # short bursts
    period: 1s
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: inflight }
spec:
  inFlightReq:
    amount: 20                # max simultaneous in-flight requests

Compress, Retry, CircuitBreaker

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: gzip }
spec:
  compress: {}
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: retry }
spec:
  retry:
    attempts: 3
    initialInterval: 100ms
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: breaker }
spec:
  circuitBreaker:
    expression: "NetworkErrorRatio() > 0.30"

IP allow-list

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: office-only }
spec:
  ipAllowList:                # Traefik v3 key (v2 used `ipWhiteList`)
    sourceRange: ["10.0.0.0/8", "203.0.113.0/24"]

Chain

Group several middlewares into one reusable unit:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata: { name: hardened }
spec:
  chain:
    middlewares:
      - name: https-redirect
      - name: secure-headers
      - name: ratelimit

Then just x-orcinus-middleware: [hardened].


5. How orcinus wires it (reference)

Under the hood orcinus works with standard Kubernetes Ingress + Traefik's Kubernetes-CRD provider:

  • x-orcinus-middleware / x-orcinus-strip-prefix set the annotation on the generated Ingress:

    metadata:
      annotations:
        traefik.ingress.kubernetes.io/router.middlewares: <ns>-<name>@kubernetescrd,...

    The reference is namespace-qualified and suffixed @kubernetescrd. Orcinus fills in the deploy namespace for you (defaulting to default).

  • x-orcinus-strip-prefix additionally generates a StripPrefix Middleware named <service>-stripprefix, carrying orcinus ownership labels so rm/prune clean it up with the rest of the project.

  • Middlewares referenced by name in x-orcinus-middleware must exist in the same namespace as the service (define them in the same file).


6. Notes & gotchas

  • Traefik must be enabled. It is by default. If you started the cluster with Traefik disabled (--server-arg --disable=traefik), install an ingress controller or don't use these keys.
  • API version. Traefik v3 (current) uses traefik.io/v1alpha1. Older Traefik v2 used traefik.containo.us/v1alpha1 and ipWhiteList instead of ipAllowList. Use the group that matches your Traefik.
  • Order matters. The chain is: auto strip-prefix (if any) → your x-orcinus-middleware list, left to right. Put redirects/auth before the app.
  • Cross-namespace. To attach a middleware from another namespace, reference it as <other-ns>-<name>@kubernetescrd in a raw Ingress; the x-orcinus-middleware sugar assumes the service's own namespace.
  • IngressRoute vs Ingress. Orcinus generates standard Ingress objects (+ the Traefik annotation), which covers the vast majority of cases. For advanced Traefik-only routing (TCP/UDP routers, weighted services) write a raw IngressRoute manifest and deploy it in the same file.