mirror of
https://github.com/hanzoai/ingress.git
synced 2026-10-26 02:47:15 +00:00
169 lines
6.3 KiB
YAML
169 lines
6.3 KiB
YAML
# Hanzo Ingress — L7 reverse proxy for lux-k8s (DaemonSet, all nodes)
|
|
# TLS: ACME DNS challenge via Cloudflare
|
|
# Routes: K8s Ingress + CRD providers (no file provider)
|
|
# hostNetwork=true: pods bind directly to node ports 80/443 (no LB needed)
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: DaemonSet
|
|
metadata:
|
|
name: hanzo-ingress
|
|
namespace: lux-system
|
|
labels:
|
|
app: hanzo-ingress
|
|
app.kubernetes.io/name: hanzo-ingress
|
|
app.kubernetes.io/part-of: lux-network
|
|
spec:
|
|
selector:
|
|
matchLabels:
|
|
app: hanzo-ingress
|
|
updateStrategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxUnavailable: 1
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: hanzo-ingress
|
|
spec:
|
|
serviceAccountName: hanzo-ingress
|
|
imagePullSecrets:
|
|
- name: ghcr-secret
|
|
hostNetwork: true
|
|
dnsPolicy: ClusterFirstWithHostNet
|
|
terminationGracePeriodSeconds: 30
|
|
containers:
|
|
- name: ingress
|
|
image: ghcr.io/hanzoai/ingress:1.7.41
|
|
imagePullPolicy: IfNotPresent
|
|
args:
|
|
# Providers
|
|
- --providers.kubernetesingress=true
|
|
- --providers.kubernetesingress.allowemptyservices=true
|
|
- --providers.kubernetesingress.allowexternalnameservices=true
|
|
- --providers.kubernetesingress.disableingressclasslookup=true
|
|
- --providers.kubernetescrd=true
|
|
- --providers.kubernetescrd.allowcrossnamespace=true
|
|
- --providers.kubernetescrd.allowexternalnameservices=true
|
|
- --providers.kubernetescrd.allowemptyservices=true
|
|
# Entrypoints
|
|
- --entrypoints.web.address=:80
|
|
- --entrypoints.web.http.redirections.entrypoint.to=:443
|
|
- --entrypoints.web.http.redirections.entrypoint.scheme=https
|
|
- --entrypoints.web.http.redirections.entrypoint.permanent=true
|
|
- --entrypoints.websecure.address=:443
|
|
- --entrypoints.websecure.http.tls=true
|
|
- --entrypoints.websecure.http.tls.certresolver=letsencrypt
|
|
# ACME / Let's Encrypt via Cloudflare DNS-01
|
|
- --[email protected]
|
|
- --certificatesresolvers.letsencrypt.acme.storage=/data/acme.json
|
|
- --certificatesresolvers.letsencrypt.acme.dnschallenge=true
|
|
- --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
|
|
- --certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
|
|
# Health + observability
|
|
- --ping=true
|
|
- --ping.entryPoint=web
|
|
- --api.dashboard=false
|
|
- --log.level=INFO
|
|
- --accesslog=true
|
|
- --accesslog.fields.defaultmode=keep
|
|
- --accesslog.fields.headers.defaultmode=drop
|
|
# The key /data/acme.json is sealed under comes from this brand's own
|
|
# KMS, under this brand's own org — never from this file and never
|
|
# from a Kubernetes Secret:
|
|
#
|
|
# ingress/acme-seal 32 bytes, AES-256-GCM, seals the ACME state
|
|
#
|
|
# Reading it needs one credential of its own, and that one IS a
|
|
# Secret, because it is what opens KMS. The reference is REQUIRED:
|
|
# this pod is configured for KMS, so a missing credential is a
|
|
# missing configuration and the kubelet naming the absent Secret is
|
|
# the clearest way to say so. Materialise it with a KMSSecret before
|
|
# applying this manifest.
|
|
#
|
|
# The Cloudflare credential the DNS-01 challenge is answered with
|
|
# stays where the cluster already holds it. Moving it to a
|
|
# zone-scoped token is its own change: it swaps the mechanism lego
|
|
# authenticates with, and lego prefers the account pair over any
|
|
# token, so the two do not coexist.
|
|
env:
|
|
- name: INGRESS_KMS_ENDPOINT
|
|
value: "https://kms.hanzo.ai"
|
|
- name: INGRESS_KMS_ORG
|
|
value: "lux"
|
|
- name: INGRESS_KMS_PATH
|
|
value: "ingress"
|
|
- name: INGRESS_KMS_CLIENT_ID
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: ingress-kms
|
|
key: clientId
|
|
- name: INGRESS_KMS_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: ingress-kms
|
|
key: clientSecret
|
|
- name: CF_API_EMAIL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cloudflare-api-credentials
|
|
key: email
|
|
- name: CF_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cloudflare-api-credentials
|
|
key: apiKey
|
|
ports:
|
|
- name: web
|
|
containerPort: 80
|
|
hostPort: 80
|
|
protocol: TCP
|
|
- name: websecure
|
|
containerPort: 443
|
|
hostPort: 443
|
|
protocol: TCP
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
limits:
|
|
cpu: "2"
|
|
memory: 512Mi
|
|
securityContext:
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
capabilities:
|
|
add:
|
|
- NET_BIND_SERVICE
|
|
drop:
|
|
- ALL
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /ping
|
|
port: 80
|
|
scheme: HTTP
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 10
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /ping
|
|
port: 80
|
|
scheme: HTTP
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 5
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /data
|
|
volumes:
|
|
# /data/acme.json holds the ACME account key and every leaf private key
|
|
# this node has been issued. It is sealed (AES-256-GCM under a key read
|
|
# from KMS at startup and held only in memory), so the file on the node
|
|
# is inert. Still node-local: ACME_SHARED_STORE_NAMESPACE moves the
|
|
# state to one Kubernetes object with one elected writer, sealed there
|
|
# too.
|
|
- name: data
|
|
hostPath:
|
|
path: /var/lib/hanzo-ingress
|
|
type: DirectoryOrCreate
|