Skip to main content
Version: 0.1 (next)

Expose Infrared over HTTPS

A port-forward is enough to install Infrared. To reach it without one, and to let GitHub reach it later, give it a hostname and a certificate. On k3s, Traefik and cert-manager are already there: cert-manager comes from your gitops repo at sync wave 10.

Before you expose it, check that:

  • the setup wizard is finished, because until the platform admin exists the setup token is the only credential;
  • you're on chart 0.1.0-alpha.6 or later, so /mcp requires a bearer token (Connect an MCP client).

1. Open the ports and add the DNS record​

  • Allow TCP 80 and 443 to the node. Let's Encrypt's HTTP-01 challenge needs port 80 reachable from the internet.
  • Point an A record for your host at the node's public address. With a CDN or proxy in front, turn it off (DNS only) until the certificate is issued.

2. Add the issuer and the Ingress to the gitops repo​

These files are yours. The operator's hydration never deletes a file and never rewrites one it doesn't render, so they survive template upgrades.

registry/clusters/<cluster>/components/infrared-ingress.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: infrared-ingress
namespace: argocd
labels:
app.kubernetes.io/part-of: infrared-gitops
annotations:
argocd.argoproj.io/sync-wave: "45" # after cert-manager (10) and Infrared (40)
spec:
project: platform
source:
repoURL: https://github.com/<org>/gitops
targetRevision: main
path: components/infrared-ingress
destination:
server: https://kubernetes.default.svc
namespace: infrared
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [SkipDryRunOnMissingResource=true]

components/infrared-ingress/ holds a kustomization.yaml that lists the two files below.

issuer.yaml:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-http01
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-http01-account
solvers:
- http01:
ingress:
ingressClassName: traefik

ingress.yaml: HTTPS to the infrared Service, plus a redirect from HTTP. cert-manager's challenge path is longer than /, so Traefik routes challenges to it ahead of the redirect.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: infrared
namespace: infrared
annotations:
cert-manager.io/cluster-issuer: letsencrypt-http01
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts: [<host>]
secretName: infrared-tls
rules:
- host: <host>
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: infrared, port: { number: 80 } } }
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: https-redirect
namespace: infrared
spec:
redirectScheme: { scheme: https, permanent: true }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: infrared-http
namespace: infrared
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: infrared-https-redirect@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: <host>
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: infrared, port: { number: 80 } } }

Merge the PR. Argo CD syncs the Application, and cert-manager usually issues the certificate within a minute.

3. Check it​

kubectl -n infrared get certificate infrared-tls # READY True
curl -sI http://<host>/ | head -1 # 301
curl -s -o /dev/null -w '%{http_code}\n' https://<host>/api/v1/orgs # 401 without a session

The API works out its external URL from the forwarded headers, so session cookies are marked Secure over HTTPS without any chart change.