Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61674df541
|
||
|
|
78a73d80aa
|
||
|
|
b6c6287ae3
|
+37
-6
@@ -1,14 +1,45 @@
|
||||
# Hydra 与 OIDC Login/Consent PoC
|
||||
# Hydra 与独立 IAM 登录接入
|
||||
|
||||
本目录提供独立 Hydra 签发服务,以及一个薄的 **OIDC 上游适配器**。当前上游配置为
|
||||
Authelia;适配器不连接 LDAP,也不管理用户目录。Samba AD、密码和 MFA 继续由现有
|
||||
Authelia 链路负责。第一轮只接入人类和 Gitea,不实现 agent 动态授权。
|
||||
本目录提供独立 Hydra 签发服务。当前分支将实验性 Hydra 登录入口接至 Spring `iam-login`
|
||||
开发实例,由其执行 AD 密码、WebAuthn 和授权确认;不改变 issuer、Gitea 登录源或数据库。
|
||||
旧 Go OIDC 适配器继续部署以便回退,Gitea 的直接 Authelia 登录源也保留。
|
||||
该配置需经 PR 合并与 Flux 应用后才生效;下面原有 Go/Authelia 说明保留为回退路径资料。
|
||||
|
||||
```text
|
||||
Gitea → Hydra → OIDC Login/Consent → Authelia → Samba AD
|
||||
← OIDC ← 经验证的上游身份 ← OIDC callback
|
||||
Gitea → Hydra → iam-login(Tailscale 开发实例)→ Samba AD + WebAuthn
|
||||
```
|
||||
|
||||
## Spring 开发路径验收
|
||||
|
||||
源码版本:[iam-login 5f49a7c](https://git.ddupan.top/panxiao81/iam-login/commit/5f49a7c)。
|
||||
开发 UI 是 `https://laptop.tail7e769.ts.net:18082`,已有 Passkey RP ID 保持不变。
|
||||
Hydra 回调为 `/oauth2/start`、`/oauth2/consent`、`/oauth2/logout`;默认注销回到 `/signin`。
|
||||
浏览器需要 Tailscale 连通性;开发服务暂由本地 JVM 容器运行,并非生产 Native 部署。
|
||||
|
||||
开发服务通过 `kubectl -n hydra port-forward --address 127.0.0.1 service/hydra-admin 18445:4445`
|
||||
访问私有 Admin API。转发需在 Hydra 滚动更新后重连;验收运行器会循环重建该通道。
|
||||
该通道与本地容器是临时验收依赖,必须保持运行;不修改 NetworkPolicy、
|
||||
不新增 Admin HTTPRoute。临时运行配置与旧版回退备份仅保存在受限本地运行目录,不提交秘密。
|
||||
|
||||
验收前已只读核对目标 Gitea 账号的现有 Hydra 外部关联,并将旧 sub 显式绑定到 AD
|
||||
稳定主体;不按邮箱重建关联、不修改 Gitea 账号或仓库权限。客户端管理仅允许有效 MFA 且
|
||||
直接属于 AD Domain Admins 的用户;这是验收期明确指定的粗粒度管理组。
|
||||
|
||||
从 Gitea `/user/oauth2/hydra` 发起,应进入新密码/Passkey 页,确认授权后返回原账号,核对
|
||||
仓库权限。当前 Gitea client 未登记 front/back-channel 或 post-logout 回调,不能声称 Gitea 会话会
|
||||
随 IAM 注销。应用的注销协议兼容性需单独验收。旧 `authelia` 登录源始终保留。
|
||||
|
||||
开发配置关闭 Boot 默认 LDAP health indicator:它未配置目录连接,不对应同用户 bind 的
|
||||
AD 仓储。总健康检查仍验证数据库,真实 AD 认证由本次人类登录验收,不将 readiness 当作
|
||||
AD 凭据有效性的证明。
|
||||
|
||||
Hydra 的 login/consent URL 是全局配置,本次影响全部经 Hydra 的登录请求(当前接入 Gitea),
|
||||
不影响直接走 Authelia 的应用。不要在无法访问 Tailscale 开发实例时合并此切换。
|
||||
验收失败时 revert 本次回调变更,恢复旧 Go 的 `/login`、`/consent`;不删除客户端、签名密钥、
|
||||
数据库、用户关联或 Passkey。最终 `auth.ddupan.top` 同源部署另开 PR,不在此次切换范围内。
|
||||
|
||||
## 原 Go/Authelia 路径与回退资料
|
||||
|
||||
目标入口:
|
||||
|
||||
- `https://hydra.ad.ddupan.top`:Hydra 公共 OAuth2/OIDC endpoint。
|
||||
|
||||
@@ -12,8 +12,10 @@ urls:
|
||||
self:
|
||||
issuer: https://hydra.ad.ddupan.top
|
||||
public: https://hydra.ad.ddupan.top
|
||||
login: https://hydra-login.ad.ddupan.top/login
|
||||
consent: https://hydra-login.ad.ddupan.top/consent
|
||||
login: https://laptop.tail7e769.ts.net:18082/oauth2/start
|
||||
consent: https://laptop.tail7e769.ts.net:18082/oauth2/consent
|
||||
logout: https://laptop.tail7e769.ts.net:18082/oauth2/logout
|
||||
post_logout_redirect: https://laptop.tail7e769.ts.net:18082/signin
|
||||
ttl:
|
||||
access_token: 15m
|
||||
id_token: 15m
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# SecretStore 由独立控制面的部署管理,必须只读下述两个管理凭据路径。
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: homelab-postgresql-prod-admin
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: homelab-postgresql-admin
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: homelab-postgresql-prod-admin
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/prod
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/prod
|
||||
property: password
|
||||
---
|
||||
# SecretStore 由独立控制面的部署管理,必须只读下述两个管理凭据路径。
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: homelab-postgresql-dev-admin
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: homelab-postgresql-admin
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: homelab-postgresql-dev-admin
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/dev
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/dev
|
||||
property: password
|
||||
@@ -1 +0,0 @@
|
||||
common/ca.crt
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMzCCAhugAwIBAgIUMs0iV657yC9UhA2p2vomLIbFnzgwDQYJKoZIhvcNAQEL
|
||||
BQAwITEfMB0GA1UEAxMWZGR1cGFuLnRvcCBJbnRlcm5hbCBDQTAeFw0yNjA3MjQy
|
||||
MDE1MDFaFw0zNjA3MjEyMDE1MzFaMCExHzAdBgNVBAMTFmRkdXBhbi50b3AgSW50
|
||||
ZXJuYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6QWlwBe6f
|
||||
t7Ca3KCTvr4Pz+jVO60WrMBoEDYYM8Mp04btBHzhAQHf9Pp8+15aEW9iUcQhqqm+
|
||||
2vT6H0JEhIbplyCWY6Guv0mTu8f+lvFknJIl2b3JqnMLHJKjh/rBrsE12XZ3i17M
|
||||
2tCr34BWcei85IZyQl5HMW6dB8lAE6bdom+YynK4oLJdej9DD6bSyM8WcL0OsneZ
|
||||
NsjwOlNMy3zjbtaH6mH71SgbFinxLp3AAAuLVe1DIKhFxuTQeVr/WaPum5y/oOsc
|
||||
0gJp9If6nsC33lpRGcPLiZE9kfFZa4fPe8laCaN8q1K253qZ0rjRiDhbTAppW4Fy
|
||||
r5P67h+2D+TbAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
|
||||
AQH/MB0GA1UdDgQWBBSOgk1fR0qhz/Bo4wD9g2BnOAzDXzAfBgNVHSMEGDAWgBSO
|
||||
gk1fR0qhz/Bo4wD9g2BnOAzDXzANBgkqhkiG9w0BAQsFAAOCAQEANm5kKkts1Ar2
|
||||
7IlS+TxLFrZ/C9yhIdGcBk2SL5E+5E8S3skQWLEPGLRwvV4RmiB8gQ2V6UyGLrCx
|
||||
1MuuSmCDaSYL9G66sGX1MIHlQ0F0bHIOxxtsTwIYzb5Sl8h3MfsARabmOhE3xUkn
|
||||
jaAT9YUweHhjF4vi0U1Q4F8oOSvu4eJp5dMx1r7b2bLN90A1xh9sfdkEenSBX0tm
|
||||
xK82ROYXI2Ejv/EO+lPUIn3jfqbqrS2itw75Xz/ECHjIfSxvW98puP69U54a1gf6
|
||||
gWdXslr0pGkyMHqxw4dmaecpK0QK3jvqCNycNwNBfMdCypS2QRy03adcUosEAP3O
|
||||
LZU7Kd8aeg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,20 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMzCCAhugAwIBAgIUMs0iV657yC9UhA2p2vomLIbFnzgwDQYJKoZIhvcNAQEL
|
||||
BQAwITEfMB0GA1UEAxMWZGR1cGFuLnRvcCBJbnRlcm5hbCBDQTAeFw0yNjA3MjQy
|
||||
MDE1MDFaFw0zNjA3MjEyMDE1MzFaMCExHzAdBgNVBAMTFmRkdXBhbi50b3AgSW50
|
||||
ZXJuYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6QWlwBe6f
|
||||
t7Ca3KCTvr4Pz+jVO60WrMBoEDYYM8Mp04btBHzhAQHf9Pp8+15aEW9iUcQhqqm+
|
||||
2vT6H0JEhIbplyCWY6Guv0mTu8f+lvFknJIl2b3JqnMLHJKjh/rBrsE12XZ3i17M
|
||||
2tCr34BWcei85IZyQl5HMW6dB8lAE6bdom+YynK4oLJdej9DD6bSyM8WcL0OsneZ
|
||||
NsjwOlNMy3zjbtaH6mH71SgbFinxLp3AAAuLVe1DIKhFxuTQeVr/WaPum5y/oOsc
|
||||
0gJp9If6nsC33lpRGcPLiZE9kfFZa4fPe8laCaN8q1K253qZ0rjRiDhbTAppW4Fy
|
||||
r5P67h+2D+TbAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
|
||||
AQH/MB0GA1UdDgQWBBSOgk1fR0qhz/Bo4wD9g2BnOAzDXzAfBgNVHSMEGDAWgBSO
|
||||
gk1fR0qhz/Bo4wD9g2BnOAzDXzANBgkqhkiG9w0BAQsFAAOCAQEANm5kKkts1Ar2
|
||||
7IlS+TxLFrZ/C9yhIdGcBk2SL5E+5E8S3skQWLEPGLRwvV4RmiB8gQ2V6UyGLrCx
|
||||
1MuuSmCDaSYL9G66sGX1MIHlQ0F0bHIOxxtsTwIYzb5Sl8h3MfsARabmOhE3xUkn
|
||||
jaAT9YUweHhjF4vi0U1Q4F8oOSvu4eJp5dMx1r7b2bLN90A1xh9sfdkEenSBX0tm
|
||||
xK82ROYXI2Ejv/EO+lPUIn3jfqbqrS2itw75Xz/ECHjIfSxvW98puP69U54a1gf6
|
||||
gWdXslr0pGkyMHqxw4dmaecpK0QK3jvqCNycNwNBfMdCypS2QRy03adcUosEAP3O
|
||||
LZU7Kd8aeg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# 公共部分只含公开 CA;接入时必须选择 dev/ 或 prod/,不提供跨环境聚合入口。
|
||||
configMapGenerator:
|
||||
- name: homelab-postgresql-ca
|
||||
namespace: ayatori-system
|
||||
files: [ca.crt]
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: true
|
||||
@@ -1,22 +0,0 @@
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: homelab-postgresql-dev-admin
|
||||
namespace: ayatori-system
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: homelab-postgresql-dev-admin
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: homelab-postgresql-dev-admin
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/dev
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/dev
|
||||
property: password
|
||||
@@ -1,2 +0,0 @@
|
||||
# 仅交付本环境管理凭据;由控制面部署流程绑定专用 ESO 身份。
|
||||
path "kv/data/infra/postgresql/ayatori/dev" { capabilities = ["read"] }
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: database.ayatori.ddupan.top/v1alpha1
|
||||
kind: PostgreSQLInstance
|
||||
metadata:
|
||||
name: homelab-dev
|
||||
spec:
|
||||
endpoint:
|
||||
host: pg-dev.ad.ddupan.top
|
||||
hostaddr: 192.168.10.127
|
||||
port: 5433
|
||||
database: postgres
|
||||
sslMode: verify-full
|
||||
adminCredentialRef:
|
||||
name: homelab-postgresql-dev-admin
|
||||
@@ -1,6 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../common
|
||||
- instance.yaml
|
||||
- admin-credentials.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
# 供未来独立控制面管理 SecretStore 的 ESO 身份绑定;本文件尚未应用。
|
||||
path "kv/data/infra/postgresql/ayatori/prod" { capabilities = ["read"] }
|
||||
path "kv/data/infra/postgresql/ayatori/dev" { capabilities = ["read"] }
|
||||
@@ -0,0 +1,28 @@
|
||||
# 尚未 apply:须先准备管理 Secret、CA bundle 和角色感知的生产稳定入口。
|
||||
apiVersion: database.ayatori.ddupan.top/v1alpha1
|
||||
kind: PostgreSQLInstance
|
||||
metadata:
|
||||
name: homelab-prod
|
||||
spec:
|
||||
endpoint:
|
||||
host: pg-prod.ad.ddupan.top
|
||||
hostaddr: 192.168.10.2
|
||||
port: 5432
|
||||
database: postgres
|
||||
sslMode: verify-full
|
||||
adminCredentialRef:
|
||||
name: homelab-postgresql-prod-admin
|
||||
---
|
||||
apiVersion: database.ayatori.ddupan.top/v1alpha1
|
||||
kind: PostgreSQLInstance
|
||||
metadata:
|
||||
name: homelab-dev
|
||||
spec:
|
||||
endpoint:
|
||||
host: pg-dev.ad.ddupan.top
|
||||
hostaddr: 192.168.10.127
|
||||
port: 5433
|
||||
database: postgres
|
||||
sslMode: verify-full
|
||||
adminCredentialRef:
|
||||
name: homelab-postgresql-dev-admin
|
||||
@@ -1,5 +1,12 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# 公共入口不注册实例或同步凭据;必须显式选择 dev/ 或 prod/。
|
||||
# 仅供未来独立 Ayatori 控制面;此 namespace 必须与 manager 的 Secret namespace 一致。
|
||||
namespace: ayatori-system
|
||||
resources:
|
||||
- common
|
||||
- instances.yaml
|
||||
- admin-credentials.yaml
|
||||
configMapGenerator:
|
||||
- name: homelab-postgresql-ca
|
||||
files: [ca.crt]
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: true
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: homelab-postgresql-prod-admin
|
||||
namespace: ayatori-system
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: homelab-postgresql-prod-admin
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: homelab-postgresql-prod-admin
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/prod
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: infra/postgresql/ayatori/prod
|
||||
property: password
|
||||
@@ -1,2 +0,0 @@
|
||||
# 仅交付本环境管理凭据;由控制面部署流程绑定专用 ESO 身份。
|
||||
path "kv/data/infra/postgresql/ayatori/prod" { capabilities = ["read"] }
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: database.ayatori.ddupan.top/v1alpha1
|
||||
kind: PostgreSQLInstance
|
||||
metadata:
|
||||
name: homelab-prod
|
||||
spec:
|
||||
endpoint:
|
||||
host: pg-prod.ad.ddupan.top
|
||||
hostaddr: 192.168.10.2
|
||||
port: 5432
|
||||
database: postgres
|
||||
sslMode: verify-full
|
||||
adminCredentialRef:
|
||||
name: homelab-postgresql-prod-admin
|
||||
@@ -1,6 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../common
|
||||
- instance.yaml
|
||||
- admin-credentials.yaml
|
||||
@@ -1,51 +0,0 @@
|
||||
"""校验渲染后的环境隔离与引用关系,无网络、集群写入或凭据访问。"""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1] / 'ayatori'
|
||||
|
||||
|
||||
def render(environment):
|
||||
return list(yaml.safe_load_all(subprocess.check_output(
|
||||
['kubectl', 'kustomize', str(ROOT / environment)], text=True)))
|
||||
|
||||
|
||||
class AyatoriManifestTests(unittest.TestCase):
|
||||
def test_common_entry_cannot_register_instances_or_sync_credentials(self):
|
||||
objects = render('.')
|
||||
self.assertEqual([x['kind'] for x in objects], ['ConfigMap'])
|
||||
self.assertEqual(objects[0]['metadata']['namespace'], 'ayatori-system')
|
||||
|
||||
def test_environment_isolation_scope_and_references(self):
|
||||
for env, address, port in [('dev', '192.168.10.127', 5433), ('prod', '192.168.10.2', 5432)]:
|
||||
with self.subTest(environment=env):
|
||||
objects = render(env)
|
||||
self.assertEqual(sorted(x['kind'] for x in objects),
|
||||
['ConfigMap', 'ExternalSecret', 'PostgreSQLInstance'])
|
||||
instance = next(x for x in objects if x['kind'] == 'PostgreSQLInstance')
|
||||
secret = next(x for x in objects if x['kind'] == 'ExternalSecret')
|
||||
self.assertNotIn('namespace', instance['metadata'])
|
||||
self.assertEqual(instance['metadata']['name'], 'homelab-' + env)
|
||||
self.assertEqual(instance['spec']['endpoint'], {
|
||||
'host': 'pg-' + env + '.ad.ddupan.top', 'hostaddr': address,
|
||||
'port': port, 'database': 'postgres', 'sslMode': 'verify-full'})
|
||||
self.assertEqual(secret['metadata']['namespace'], 'ayatori-system')
|
||||
self.assertEqual(instance['spec']['adminCredentialRef']['name'], secret['spec']['target']['name'])
|
||||
self.assertEqual(secret['spec']['secretStoreRef'],
|
||||
{'name': 'homelab-postgresql-' + env + '-admin', 'kind': 'SecretStore'})
|
||||
self.assertEqual({x['remoteRef']['key'] for x in secret['spec']['data']},
|
||||
{'infra/postgresql/ayatori/' + env})
|
||||
self.assertEqual({x['secretKey'] for x in secret['spec']['data']}, {'username', 'password'})
|
||||
self.assertEqual({x['remoteRef']['property'] for x in secret['spec']['data']}, {'username', 'password'})
|
||||
policy = (ROOT / env / 'eso-policy.hcl').read_text()
|
||||
self.assertEqual(re.findall(r'path\s+"([^"]+)"', policy),
|
||||
['kv/data/infra/postgresql/ayatori/' + env])
|
||||
self.assertEqual(re.findall(r'capabilities\s*=\s*\[([^]]+)\]', policy), ['"read"'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user