www: redirect alias hostnames (e.g. apex) to canonical origin
Adds www_redirect_hostnames; renders a www-redirect ingress that 301s to https://<www_hostname>$request_uri while leaving more specific paths on the same host (e.g. /.well-known/matrix/*) to their own ingresses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,7 @@ Defaults deliberately refuse incomplete membership deployments. Configure:
|
||||
| --- | --- |
|
||||
| `www_namespace` | Namespace, default `www` |
|
||||
| `www_hostname` | Canonical HTTPS hostname, default **www.eom.dev** |
|
||||
| `www_redirect_hostnames` | Hostnames 301-redirected to `www_hostname`, default `[]` |
|
||||
| `www_membership_enabled` | Deploy membership/proxy, default true |
|
||||
| `www_membership_image` | Required versioned application image/digest |
|
||||
| `www_membership_secret_name` | Existing Secret, default `membership` |
|
||||
@@ -239,7 +240,10 @@ app defaults to eom.dev. This role sets `MEMBERSHIP_PUBLIC_URL` to
|
||||
|
||||
To serve the apex instead, set `www_hostname: eom.dev` and align DNS,
|
||||
certificates, callbacks and redirects. Membership requires one canonical HTTPS
|
||||
origin for sessions, CSRF, email verification and OIDC.
|
||||
origin for sessions, CSRF, email verification and OIDC. To send apex visitors
|
||||
to the canonical origin without serving it there, set
|
||||
`www_redirect_hostnames: [eom.dev]`; other ingresses' more specific apex paths
|
||||
(such as `/.well-known/matrix/*`) keep working.
|
||||
|
||||
## Upgrades and operations
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ www_repo_url: https://git.example.com/organization/www.git
|
||||
www_namespace: www
|
||||
www_hostname: www.example.com
|
||||
www_ingress_class: nginx
|
||||
# Extra hostnames 301-redirected to https://{{ www_hostname }} (e.g. [eom.dev]).
|
||||
www_redirect_hostnames: []
|
||||
www_cluster_issuer: letsencrypt-prod
|
||||
# Set to your published website image, including any required metrics exporter.
|
||||
www_httpd_image: registry.example.com/organization/www:release
|
||||
|
||||
@@ -171,3 +171,39 @@ spec:
|
||||
tls:
|
||||
- hosts: [{{ www_hostname }}]
|
||||
secretName: www
|
||||
{% if www_redirect_hostnames | length > 0 %}
|
||||
---
|
||||
# Send browsers on alias hostnames (e.g. the apex, whose /.well-known/matrix/*
|
||||
# paths are owned by another ingress) to the canonical origin. Exact/longer
|
||||
# paths on the same host in other ingresses still take precedence over "/".
|
||||
# A snippet is used because the controller's annotation validation rejects
|
||||
# "$request_uri" in permanent-redirect.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: www-redirect
|
||||
namespace: {{ www_namespace }}
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: {{ www_cluster_issuer | to_json }}
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/enable-access-log: "false"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: {{ ("return 301 https://" ~ www_hostname ~ "$request_uri;") | to_json }}
|
||||
spec:
|
||||
ingressClassName: {{ www_ingress_class }}
|
||||
rules:
|
||||
{% for host in www_redirect_hostnames %}
|
||||
- host: {{ host }}
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: /
|
||||
backend:
|
||||
service:
|
||||
name: httpd
|
||||
port:
|
||||
number: 80
|
||||
{% endfor %}
|
||||
tls:
|
||||
- hosts: {{ www_redirect_hostnames | to_json }}
|
||||
secretName: www-redirect
|
||||
{% endif %}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- name: Supply nonsecret fixture settings
|
||||
ansible.builtin.set_fact:
|
||||
www_membership_image: registry.example.org/www-membership:release-test
|
||||
www_redirect_hostnames: [apex.example.com]
|
||||
www_membership_service_groups:
|
||||
"0": ["cn=free-service,ou=groups,dc=example,dc=org"]
|
||||
"2": ["cn=paid-service,ou=groups,dc=example,dc=org", "cn=second-service,ou=groups,dc=example,dc=org"]
|
||||
|
||||
@@ -92,6 +92,9 @@ ingress = find(all_objects, "Ingress", "www")
|
||||
assert ingress["metadata"]["annotations"]["nginx.ingress.kubernetes.io/enable-access-log"] == "false"
|
||||
assert ingress["metadata"]["annotations"]["nginx.ingress.kubernetes.io/proxy-body-size"] == "2m"
|
||||
assert ingress["spec"]["rules"][0]["http"]["paths"][0]["backend"]["service"]["name"] == "httpd"
|
||||
redirect = find(all_objects, "Ingress", "www-redirect")
|
||||
assert redirect["spec"]["rules"][0]["host"] == "apex.example.com"
|
||||
assert redirect["metadata"]["annotations"]["nginx.ingress.kubernetes.io/configuration-snippet"] == "return 301 " + config["MEMBERSHIP_PUBLIC_URL"] + "$request_uri;"
|
||||
for obj in docs("external-membership-workloads"):
|
||||
if obj["kind"] == "Deployment":
|
||||
pod_checks(obj["spec"]["template"]["spec"], external=True)
|
||||
|
||||
Reference in New Issue
Block a user