authentik: declare LDAP Synology provider + expose outpost on ldap.renner.uno

Blueprint-manages a new LDAP integration for nas-1 DSM: dedicated
LDAPProvider, Application, Synology Users/Admins groups, and PolicyBindings.
Standalone LoadBalancer Service on the BGP pool (pinned 10.2.99.88/fd99::88,
LAN hostname ldap.renner.uno via external-dns-unifi) sidesteps the operator-
owned ClusterIP; CNP scopes :3389 ingress to the mgmt LAN so the cleartext
bind stays on a trusted segment until LDAPS is wired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 20:09:04 +02:00
co-authored by Claude Opus 4.7
parent 65bbb40ad4
commit 3ededebfdd
4 changed files with 171 additions and 0 deletions
@@ -228,6 +228,84 @@ data:
# keeps a single session store for the gate's flow.
providers: []
# Synology DSM (nas-1) LDAP integration. DSM's Directory Client
# binds to the LDAP Outpost via the standalone LoadBalancer Service
# defined in ldap-outpost-lb.yaml (lb-pool: bgp → 10.2.99.x advertised
# to UDM over BGP, reachable from the mgmt LAN). Access is gated by
# PolicyBindings on the `synology` Application — only members of
# `Synology Users` or `Synology Admins` may bind. The provider shares
# the base DN (`DC=authentik,DC=renner,DC=uno`) and authorization
# flow (`ldap-authentication-flow`) with the existing LDAP Unifi /
# LDAP Jellyfin providers; the outpost dispatches based on which
# provider an Application policy admits the bind for. UID/GID start
# 2000/4000 matches the homelab convention so synthetic POSIX IDs
# stay consistent across all consumers (relevant for SMB ACL
# ownership on nas-1's shares).
- id: synology-users-group
model: authentik_core.group
identifiers:
name: Synology Users
state: present
attrs:
is_superuser: false
- id: synology-admins-group
model: authentik_core.group
identifiers:
name: Synology Admins
state: present
attrs:
is_superuser: false
- id: ldap-synology-provider
model: authentik_providers_ldap.ldapprovider
identifiers:
name: LDAP Synology
state: present
attrs:
authorization_flow: !Find [authentik_flows.flow, [slug, ldap-authentication-flow]]
invalidation_flow: !Find [authentik_flows.flow, [slug, default-invalidation-flow]]
base_dn: DC=authentik,DC=renner,DC=uno
bind_mode: direct
search_mode: direct
uid_start_number: 2000
gid_start_number: 4000
mfa_support: false
- id: synology-application
model: authentik_core.application
identifiers:
slug: synology
state: present
attrs:
name: Synology DSM (nas-1)
slug: synology
provider: !KeyOf ldap-synology-provider
meta_launch_url: https://nas-1.renner.uno
meta_description: "Directory authentication for Synology DSM on nas-1"
- model: authentik_policies.policybinding
identifiers:
target: !KeyOf synology-application
group: !KeyOf synology-users-group
order: 0
state: present
attrs:
enabled: true
negate: false
failure_result: false
- model: authentik_policies.policybinding
identifiers:
target: !KeyOf synology-application
group: !KeyOf synology-admins-group
order: 1
state: present
attrs:
enabled: true
negate: false
failure_result: false
# `Local Radius` carried `kubernetes_namespace: default` in its
# config from the Proxmox-era data, which the chart Role has no
# permissions for. Pin spawned outposts at `authentik` so the
@@ -257,6 +335,7 @@ data:
providers:
- !Find [authentik_providers_ldap.ldapprovider, [name, "LDAP Unifi"]]
- !Find [authentik_providers_ldap.ldapprovider, [name, "LDAP Jellyfin"]]
- !KeyOf ldap-synology-provider
config:
log_level: info
authentik_host: "http://authentik-server.authentik.svc.cluster.local"
@@ -7,3 +7,5 @@ resources:
- paperless-gate-ingress.yaml
- paperless-gate-egress.yaml
- outpost-servicemonitors.yaml
- ldap-outpost-lb.yaml
- ldap-outpost-cnp.yaml
@@ -0,0 +1,33 @@
---
# Allow mgmt-LAN clients (notably nas-1 at 10.2.40.50 / fd40::50) to
# bind to the LDAP outpost via the LoadBalancer Service in
# ldap-outpost-lb.yaml. The namespace's default-deny-ingress CNP only
# admits traffic from named namespaces + host/remote-node entities;
# external LAN clients land in Cilium's `world` identity which the
# default policy blocks. This policy additively allows :389/TCP from
# the mgmt-LAN CIDRs to the LDAP outpost pods only — does not widen
# anything else in the namespace.
#
# Source CIDR is the mgmt LAN (10.2.40.0/24 + fd40::/64) where nas-1
# and other LAN clients live. We deliberately do NOT open this to
# `world` — the BGP LB pool is technically reachable from any peer
# but LDAP binds carry plaintext credentials and must stay on the
# trusted segment until LDAPS is wired.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: ldap-outpost-lan-ingress
namespace: authentik
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: authentik-outpost-ldap
goauthentik.io/outpost-name: ldap-outpost
ingress:
- fromCIDR:
- 10.2.40.0/24
- fd40::/64
toPorts:
- ports:
- port: "3389"
protocol: TCP
@@ -0,0 +1,57 @@
---
# Standalone LoadBalancer Service exposing the LDAP outpost pods on the
# BGP-advertised LB pool (10.2.99.x / fd99::x). Reachable from the mgmt
# LAN (10.2.40.0/24 / fd40::/64) via UDM-learned BGP routes — the LB IPs
# live on no device VLAN.
#
# Why standalone instead of mutating the operator-owned
# `ak-outpost-ldap-outpost` Service: the Authentik operator periodically
# reconciles its own Service from the LDAPOutpost record, which would
# stomp on any in-place edits to type/labels. Same workaround as the
# RADIUS outpost (standalone headless Service for upstream metrics-port
# bug). Selector targets the operator-managed Deployment pods directly
# via the labels Authentik stamps on them.
#
# Port mapping: container listens on 3389/TCP (LDAP) and 6636/TCP
# (LDAPS) internally; we only publish 389 here because LDAPS would
# require pinning a stable cert on the LDAPProvider — currently the
# outpost self-signs at startup, which DSM's Directory Client cannot
# trust across restarts. The CNP in ldap-outpost-cnp.yaml restricts the
# source CIDR to the mgmt LAN so the cleartext bind stays on a trusted
# segment. LDAPS is a follow-up once a stable cert is wired.
apiVersion: v1
kind: Service
metadata:
name: ldap-outpost-lb
namespace: authentik
labels:
lb-pool: bgp
annotations:
# Pinned to .88 / fd99::88 (next free in the .80-.89 infra range
# alongside traefik .86, ha .87, syslog .84, syncthing .85). Pinning
# matters for LDAP because DSM's Directory Client caches DNS very
# aggressively — a Cilium IPAM re-allocation after a churn event
# would silently break bind until DSM's cache TTL expires.
lbipam.cilium.io/ips: "10.2.99.88,fd99::88"
# LAN-side DNS only. external-dns-unifi (sources: ingress, service,
# traefik-proxy) publishes this record to the UDM split-horizon
# zone. The Cloudflare external-dns instance only watches `ingress`,
# so this Service produces no public record — LDAP stays
# LAN-internal, which matches the threat model of the cleartext
# bind on 389.
external-dns.alpha.kubernetes.io/hostname: ldap.renner.uno
spec:
type: LoadBalancer
externalTrafficPolicy: Local
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv4
- IPv6
selector:
app.kubernetes.io/name: authentik-outpost-ldap
goauthentik.io/outpost-name: ldap-outpost
ports:
- name: ldap
port: 389
targetPort: 3389
protocol: TCP