diff --git a/apps/common/oidc_provider/authelia.py b/apps/common/oidc_provider/authelia.py index 44015473d6a0ea1eacd76078b4e70df840919e16..bbc46c86bc0c0215d2044d77de4b623ee54bfdec 100644 --- a/apps/common/oidc_provider/authelia.py +++ b/apps/common/oidc_provider/authelia.py @@ -1,6 +1,8 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """Authelia OIDC Provider""" +import base64 +import hashlib import logging import secrets from typing import Any @@ -17,6 +19,25 @@ logger = logging.getLogger(__name__) class AutheliaOIDCProvider(OIDCProviderBase): """Authelia OIDC Provider""" + + # PKCE相关的类变量,用于存储code_verifier + _code_verifier: str = "" + + @classmethod + def _generate_pkce_params(cls) -> tuple[str, str]: + """生成PKCE参数""" + # 生成code_verifier (43-128个字符的随机字符串) + code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=') + + # 生成code_challenge (code_verifier的SHA256哈希值) + code_challenge = base64.urlsafe_b64encode( + hashlib.sha256(code_verifier.encode('utf-8')).digest() + ).decode('utf-8').rstrip('=') + + # 存储code_verifier供后续使用 + cls._code_verifier = code_verifier + + return code_verifier, code_challenge @classmethod def _get_login_config(cls) -> AutheliaConfig: @@ -39,6 +60,11 @@ class AutheliaOIDCProvider(OIDCProviderBase): "grant_type": "authorization_code", "code": code, } + + # 如果启用了PKCE,添加code_verifier参数 + if login_config.enable_pkce and cls._code_verifier: + data["code_verifier"] = cls._code_verifier + logger.info("[Authelia] 使用PKCE流程,添加code_verifier参数") headers = { "Content-Type": "application/x-www-form-urlencoded", } @@ -148,12 +174,26 @@ class AutheliaOIDCProvider(OIDCProviderBase): # 生成随机的 state 参数以确保安全性和唯一性 state = secrets.token_urlsafe(32) - return (f"{login_config.host.rstrip('/')}/api/oidc/authorization?" - f"client_id={login_config.client_id}&" - f"response_type=code&" - f"scope=openid profile email&" - f"redirect_uri={login_config.redirect_uri}&" - f"state={state}") + + # 基础URL参数 + url_params = [ + f"client_id={login_config.client_id}", + f"response_type=code", + f"scope=openid profile email", + f"redirect_uri={login_config.redirect_uri}", + f"state={state}" + ] + + # 如果启用PKCE,添加PKCE参数 + if login_config.enable_pkce: + code_verifier, code_challenge = cls._generate_pkce_params() + url_params.extend([ + f"code_challenge={code_challenge}", + f"code_challenge_method={login_config.pkce_challenge_method}" + ]) + logger.info("[Authelia] 启用PKCE流程,生成code_challenge参数") + + return f"{login_config.host.rstrip('/')}/api/oidc/authorization?" + "&".join(url_params) @classmethod async def get_access_token_url(cls) -> str: diff --git a/apps/services/flow.py b/apps/services/flow.py index 3bc1b3e0d4534cbe48ef8db12a5002bd9b455c5c..9a8bd7bce569d204339183411904fb3ec59b700e 100644 --- a/apps/services/flow.py +++ b/apps/services/flow.py @@ -186,7 +186,7 @@ class FlowManager: NodeServiceItem( serviceId=record["_id"], name=record["name"], - type="default", # TODO record["type"]? + type="plugin", # 除了system固有节点,其余均为插件 nodeMetaDatas=[], createdAt=str(record["created_at"]), ) diff --git a/deploy/chart/README.md b/deploy/chart/README.md deleted file mode 100644 index 75dd156d0d9b3cfa8fb1618f5e2857b37485eb93..0000000000000000000000000000000000000000 --- a/deploy/chart/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# EulerCopilot - -## 部署顺序 - -1. databases [必须部署] -2. authhub [必须部署] -3. witchaind [必须部署] -4. euler-copilot [必须部署] -5. rca -6. agents diff --git a/deploy/chart/authelia-config.yaml b/deploy/chart/authelia-config.yaml deleted file mode 100644 index 79e28c2e6f9ea49ee4f235f87bca73229e284a75..0000000000000000000000000000000000000000 --- a/deploy/chart/authelia-config.yaml +++ /dev/null @@ -1,85 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: authelia-config-custom - namespace: euler-copilot -data: - configuration.yaml: | - --- - theme: 'light' - server: - address: 'tcp://0.0.0.0:9091/' - log: - level: 'info' - format: 'text' - - # 身份验证后端 - authentication_backend: - password_reset: - disable: true - refresh_interval: '5 minutes' - file: - path: '/config/users.yml' - password: - algorithm: 'argon2id' - iterations: 1 - salt_length: 16 - parallelism: 8 - memory: 1024 - - # 访问控制 - access_control: - default_policy: 'one_factor' - - # 会话配置 - session: - name: 'authelia_session' - same_site: 'lax' - inactivity: '5 minutes' - expiration: '1 hour' - remember_me: '1 month' - cookies: - - domain: '127.0.0.1' - authelia_url: 'http://127.0.0.1:30091' - - # 存储配置 - storage: - encryption_key: 'insecure_storage_encryption_key_change_me_in_production' - local: - path: '/config/db.sqlite3' - - # 通知配置 - notifier: - disable_startup_check: true - filesystem: - filename: '/config/notification.txt' - - # 禁用不需要的功能 - totp: - disable: true - webauthn: - disable: true - duo_api: - disable: true - - # 限流配置 - regulation: - max_retries: 3 - find_time: '2 minutes' - ban_time: '5 minutes' ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: authelia-users-custom - namespace: euler-copilot -data: - users.yml: | - users: - openEuler: - displayname: "openEuler" - password: "$argon2id$v=19$m=65536,t=3,p=4$eHA4/xN5ZQB0Pl+Larrf8A$M7+SLwWQymLJQjUfEf/zin+xAEQ1EW8IcHet6tFcBvo" # password: openEuler12#$ - email: openEuler@example.com - groups: - - admins - - dev diff --git a/deploy/chart/authelia-values.yaml b/deploy/chart/authelia-values.yaml deleted file mode 100644 index 0d014797cea5d524be5cd42857668a12acb27273..0000000000000000000000000000000000000000 --- a/deploy/chart/authelia-values.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Authelia官方Chart配置文件 - -# 基础镜像配置 -image: - tag: "4.39.13" - -# 服务配置 -service: - type: NodePort - nodePort: 30091 - -# 持久化存储 -persistence: - enabled: true - size: 1Gi - -# Pod配置 -pod: - kind: 'Deployment' - replicas: 1 - -# 使用现有的Secret -secret: - existingSecret: "authelia-users" \ No newline at end of file diff --git a/deploy/chart/authelia/configs/authelia.yml b/deploy/chart/authelia/configs/authelia.yml index 80663295b4b11a30d40aa1855d8ebb9b49bf18fc..1b2a9a2c61cbec053c444ef7d1a0156783b3f507 100644 --- a/deploy/chart/authelia/configs/authelia.yml +++ b/deploy/chart/authelia/configs/authelia.yml @@ -72,9 +72,7 @@ identity_providers: hmac_secret: {{ .Values.authelia.config.identity_providers.oidc.hmac_secret | quote }} enable_client_debug_messages: {{ .Values.authelia.config.identity_providers.oidc.enable_client_debug_messages | default false }} - # 发行者配置 - 必须与实际访问地址匹配 - issuer_certificate_chain: {{ .Values.authelia.config.identity_providers.oidc.issuer_certificate_chain | quote }} - issuer_private_key: {{ .Values.authelia.config.identity_providers.oidc.issuer_private_key | quote }} + # 发行者配置 - 使用新的JWKS配置 # JWKS 密钥配置 jwks: @@ -97,7 +95,7 @@ identity_providers: # 认证方法配置 token_endpoint_auth_method: {{ .token_endpoint_auth_method | default "client_secret_basic" | quote }} - require_pkce: {{ .require_pkce | default false }} + require_pkce: {{ .require_pkce | default true }} pkce_challenge_method: {{ .pkce_challenge_method | default "S256" | quote }} # 重定向URI配置 @@ -137,9 +135,5 @@ identity_providers: consent_mode: {{ .consent_mode | default "implicit" | quote }} pre_configured_consent_duration: {{ .pre_configured_consent_duration | default "1y" | quote }} - # 会话配置 - access_token_lifespan: {{ .access_token_lifespan | default "1h" | quote }} - authorize_code_lifespan: {{ .authorize_code_lifespan | default "1m" | quote }} - id_token_lifespan: {{ .id_token_lifespan | default "1h" | quote }} - refresh_token_lifespan: {{ .refresh_token_lifespan | default "90m" | quote }} + # 注意:access_token_lifespan等配置项在新版本中已移除 {{- end }} diff --git a/deploy/chart/authelia/templates/NOTES.txt b/deploy/chart/authelia/templates/NOTES.txt index 4978d77e8751e9d6d516b80d374ca5e24ba8c6fc..8b22027aa59edb9709da21d64de0fe0681a8e445 100644 --- a/deploy/chart/authelia/templates/NOTES.txt +++ b/deploy/chart/authelia/templates/NOTES.txt @@ -26,7 +26,7 @@ 3. OIDC Configuration for Euler Copilot Framework: - Client ID: {{ (index .Values.authelia.config.identity_providers.oidc.clients 0).client_id | default "euler-copilot" }} - - Client Secret: your-client-secret-here + - Client Secret: [已在安装过程中生成并显示] - Authorization URL: {{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}/api/oidc/authorization - Token URL: {{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}/api/oidc/token - User Info URL: {{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}/api/oidc/userinfo diff --git a/deploy/chart/authelia/templates/deployment.yaml b/deploy/chart/authelia/templates/deployment.yaml index c511adcbebbf4591a5ef403a6354169ce34d99ad..72a19e4291e405d4757adf9bfd5b93ef511a3401 100644 --- a/deploy/chart/authelia/templates/deployment.yaml +++ b/deploy/chart/authelia/templates/deployment.yaml @@ -29,6 +29,9 @@ spec: - name: http containerPort: 9091 protocol: TCP + args: + - --config + - /app/configuration.yml env: - name: AUTHELIA_LOG_LEVEL value: {{ .Values.authelia.config.log.level | default "info" | quote }} @@ -53,6 +56,8 @@ spec: mountPath: /etc/authelia/certs readOnly: true {{- end }} + - name: tmp + mountPath: /tmp livenessProbe: httpGet: path: /api/health @@ -80,7 +85,7 @@ spec: capabilities: drop: - ALL - readOnlyRootFilesystem: true + readOnlyRootFilesystem: false {{- if .Values.authelia.resources }} resources: {{- toYaml .Values.authelia.resources | nindent 10 }} @@ -106,4 +111,6 @@ spec: secretName: authelia-tls-secret defaultMode: 0400 {{- end }} + - name: tmp + emptyDir: {} {{- end }} diff --git a/deploy/chart/authelia/values.yaml b/deploy/chart/authelia/values.yaml index 085bf17abb2db6650eefa7f38f55f5b1239d9b75..49b035fcae9380b7c275efd01e5f146c436d8378 100644 --- a/deploy/chart/authelia/values.yaml +++ b/deploy/chart/authelia/values.yaml @@ -88,8 +88,8 @@ authelia: domain: "127.0.0.1" # SameSite策略;默认为lax,可选:strict, lax, none same_site: lax - # [必填] 会话密钥,生产环境请修改 - secret: "insecure_session_secret_change_me_in_production" + # [必填] 会话密钥,将通过Helm参数动态设置 + secret: "" # 会话过期时间;默认为1h expiration: 1h # 会话非活跃超时时间;默认为5m @@ -129,8 +129,8 @@ authelia: # 存储配置 storage: - # [必填] 存储加密密钥,生产环境请修改 - encryption_key: "insecure_storage_encryption_key_change_me_in_production" + # [必填] 存储加密密钥,将通过Helm参数动态设置 + encryption_key: "" # 本地SQLite存储配置 local: # 数据库文件路径;默认为/etc/authelia/db.sqlite3 @@ -154,8 +154,8 @@ authelia: identity_validation: # 密码重置配置 reset_password: - # [必填] JWT密钥,生产环境请修改 - jwt_secret: "a_very_important_secret_for_jwt_tokens_change_me_in_production" + # [必填] JWT密钥,将通过Helm参数动态设置 + jwt_secret: "" # 访问限制配置 regulation: @@ -180,8 +180,8 @@ authelia: # OIDC 身份提供者配置 identity_providers: oidc: - # [必填] HMAC密钥,生产环境请修改 - hmac_secret: "this_is_a_secret_abc123abc123abc123" + # [必填] HMAC密钥,将通过Helm参数动态设置 + hmac_secret: "" # 是否启用客户端调试消息;默认为true enable_client_debug_messages: true # JWT签名密钥配置 @@ -190,52 +190,24 @@ authelia: key_id: main-signing-key # 签名算法;默认为RS256 algorithm: RS256 - # [必填] RSA私钥,生产环境请使用真实的私钥 - key: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCssLU+8eRFwGVA - E/8BPxpneEzp0rbfM1m+osuedZ+nad8L1CQompCmx8A/hXHynUGgCxS6QJj+5qNH - ZXqSIr+JDfixDqb2CpkUMBhrG7JIYZCyLQevBR5sGPwGXMeHyTGfWI6hoSUCfbqG - Hg+40n/2shisbqUyIQiR934RaA32Wn42fqcXyzaUU0xqy55aKyXBhAojsrDT09Mv - vNJce9tO9D7e0p1n0YQJdDFVyCZzCnVWL3i+iyf5rkK9OzS7TBAiQLZWfMcCBOZC - t0XBsQj91klwp17dDWzKM9seBfB83riMRqxBoSQ2NUFU27h6eJwUpOBc4PMpORlH - BBhaaHUPAgMBAAECggEAOWOaQBpcHbAUFejvSGdDq5onmVcs7d0fWIK6f2Ugkx7U - gJZWE+ZV5w8f/RwoY5POMNUt5L29+owEPCBlzPXeSDpL6O9xHfgkqjhXjRTNuU8v - Kn9be9cMJqlg6+5eYupCYu2nrOAkMAE/gP2xhN7zprTGDVvR62hd9EBW9YrqhPE+ - gsPzRr4Gg9MRiUesS+pOFkhseb+1gvNozBRhKxRs7zm2KlFpZMK6vAgG9/ZSVCxq - SbTp7NwFJsBEuVPgEWWDexflsbBxgkQ44q5VYK5MjEf0pr0IWEhIZNM+1fUnrYV0 - fhlEfT4G/gdIcEXKvzo5r3wzr1+sdIWO+Fe9JG3ZBQKBgQDmmkxektOmBt5yEdEA - aXNJBWxTQFU/vb7tm+B+/yR3jgTn8jMPmH87gl+C8SfufbGEsOatP44k2NUVad4R - yf2xtoeWuRlPw4akLWXzZdSlRzF5Q7Y4aRxzKQWcsEdKKAOvsDsuoHOQGdeE79Xw - xNqu1c2aui0qqKVBUbXDWE/H6wKBgQC/tZqAS+umFoVYfT/4t3xnW0X8z8rJqfA0 - BmosDaKdvzkUpGYaWjsUpETqGrlVdh+OfbGlfdlMCslluhaW1GVR/tD4Ji6WWt10 - EavaBnt9xYHE153FYJqshV5eku9egussRLNzccp4cltAE3InQkYGMPRlmCGNMz2j - eh0qHwGCbQKBgBPmzSB8W3fAsAH4N6lpcGGk7ixhKPpPTeMDyOQs8ODAiPvbkzyN - VK22GrgaR+/1ORTSj5X0Hjhf3kPy1w+B9zsXHayMXPrdTQluQZY3+5ooAsUMavWD - XMkziSB0tjJYMbk/5FupzU9qa4c1i6kz1AuyuAPafXtpApoYiy9It9nxAoGBAKH9 - QNTh0ffglcJE61YtLNhk3omVx0OJ7eb3+KTKzLrAhunzVDc2QS/a8kRiWnJlQprz - iLVO1tsTTkQ/7rB6Pjb/uvHDyZ/Qncli2TR8P8Lxrgp3KuBKFchrVWdSfyL8Ot2I - G54T68LE1mgZRl73+BVpLkneN5OJVa8aEySxWGQtAoGAUnguyoO+Hs73Vyz22KLi - b5ieWQghi63GaoGHA0qeUMy5njgHKUAcuSBB9TIZGxnnDA9u0hHhOieCmhAJU21h - zoKntzIFuaZgt4Tgat72oPBw1hGE+2bN5lHmC+SsL9oxyvsH2gDFjSiLWpg7GTC6 - 2mFOM+pjK2mWWQe1ADC6tbA= - -----END PRIVATE KEY----- + # [必填] RSA私钥,将通过Helm参数动态设置 + key: "" # OIDC客户端配置 clients: - - # OIDC客户端ID;默认为euler-copilot + - # OIDC客户端ID;建议使用UUID,将通过Helm参数动态设置 client_id: "euler-copilot" # 客户端显示名称;默认为Euler Copilot Framework client_name: "Euler Copilot Framework" - # [必填] 客户端密钥,生产环境请修改 - client_secret: "$argon2id$v=19$m=65536,t=3,p=4$XYeq1N+rtYxALylzWxOxCQ$mWQaPRuYGuNa9d4rnAc7eq25HIJc0dwOYQrlEzJ792k" + # [必填] 客户端密钥,将通过Helm参数动态设置 + client_secret: "" # 是否为公共客户端;默认为false public: false # 授权策略;默认为one_factor authorization_policy: one_factor # Token端点认证方法;默认为client_secret_post token_endpoint_auth_method: client_secret_post - # 是否需要PKCE;默认为false - require_pkce: false + # 是否需要PKCE;默认为true + require_pkce: true # 重定向URI列表 redirect_uris: - "https://127.0.0.1/api/auth/login" diff --git a/deploy/chart/euler_copilot/configs/framework/config-authelia.toml b/deploy/chart/euler_copilot/configs/framework/config-authelia.toml index dabc3a43ffd6957c29f467700f7e16fa40738a69..15eabed13943df7d619d1d981348ab145a37a926 100644 --- a/deploy/chart/euler_copilot/configs/framework/config-authelia.toml +++ b/deploy/chart/euler_copilot/configs/framework/config-authelia.toml @@ -1,5 +1,5 @@ [no_auth] -enable = true +enable = false user_sub = "openEuler" user_name = "openEuler" @@ -10,19 +10,27 @@ data_dir = '/app/data' [login] provider = 'authelia' +{{- $eulerCopilotDomain := .Values.domain.euler_copilot | default "http://127.0.0.1:30080" }} +{{- $autheliaDomain := .Values.domain.authelia }} +{{- if not $autheliaDomain }} + {{- $baseHost := regexReplaceAll "^https?://|:[0-9]+$" $eulerCopilotDomain "" }} + {{- $protocol := regexFind "^https?://" $eulerCopilotDomain }} + {{- $autheliaPort := .Values.ports.authelia | default 30091 | toString }} + {{- $autheliaDomain = printf "%s%s:%s" $protocol $baseHost $autheliaPort }} +{{- end }} [login.settings] # Authelia OIDC 服务器配置 -host = '{{ .Values.domain.authelia | default (printf "%s:%d" (.Values.domain.euler_copilot | default "http://127.0.0.1") (.Values.ports.authelia | default 30091)) }}' +host = '{{ $autheliaDomain }}' client_id = '{{ .Values.login.authelia.client_id | default "euler-copilot" }}' client_secret = '{{ .Values.login.authelia.client_secret | default "your-client-secret-here" }}' # OIDC 端点配置 -redirect_uri = '{{ .Values.domain.euler_copilot | default "http://127.0.0.1:30080" }}/api/auth/login' -authorization_endpoint = '{{ .Values.domain.authelia | default (printf "%s:%d" (.Values.domain.euler_copilot | default "http://127.0.0.1") (.Values.ports.authelia | default 30091)) }}/api/oidc/authorization' -token_endpoint = '{{ .Values.domain.authelia | default (printf "%s:%d" (.Values.domain.euler_copilot | default "http://127.0.0.1") (.Values.ports.authelia | default 30091)) }}/api/oidc/token' -userinfo_endpoint = '{{ .Values.domain.authelia | default (printf "%s:%d" (.Values.domain.euler_copilot | default "http://127.0.0.1") (.Values.ports.authelia | default 30091)) }}/api/oidc/userinfo' -jwks_uri = '{{ .Values.domain.authelia | default (printf "%s:%d" (.Values.domain.euler_copilot | default "http://127.0.0.1") (.Values.ports.authelia | default 30091)) }}/.well-known/jwks.json' -issuer = '{{ .Values.domain.authelia | default (printf "%s:%d" (.Values.domain.euler_copilot | default "http://127.0.0.1") (.Values.ports.authelia | default 30091)) }}' +redirect_uri = '{{ $eulerCopilotDomain }}/api/auth/login' +authorization_endpoint = '{{ $autheliaDomain }}/api/oidc/authorization' +token_endpoint = '{{ $autheliaDomain }}/api/oidc/token' +userinfo_endpoint = '{{ $autheliaDomain }}/api/oidc/userinfo' +jwks_uri = '{{ $autheliaDomain }}/.well-known/jwks.json' +issuer = '{{ $autheliaDomain }}' # OIDC 作用域和声明配置 scopes = 'openid profile email groups' @@ -30,7 +38,7 @@ response_type = 'code' response_mode = 'form_post' # OIDC 高级配置 -enable_pkce = false +enable_pkce = true pkce_challenge_method = 'S256' token_endpoint_auth_method = 'client_secret_basic' @@ -53,6 +61,14 @@ type = '{{ default "openai" .Values.models.embedding.type }}' endpoint = '{{ .Values.models.embedding.endpoint }}' api_key = '{{ .Values.models.embedding.key }}' model = '{{ default "bge-m3" .Values.models.embedding.name }}' +icon = '{{ .Values.models.embedding.icon }}' + +[reranker] +type = '{{ .Values.models.reranker.type }}' +endpoint = '{{ .Values.models.reranker.endpoint }}' +api_key = '{{ .Values.models.reranker.key }}' +model = '{{ default "BAAI/bge-reranker-v2-m3" .Values.models.reranker.name }}' +icon = '{{ .Values.models.reranker.icon }}' [rag] rag_service = 'http://rag-service.{{ .Release.Namespace }}.svc.cluster.local:9988' @@ -64,6 +80,17 @@ user = 'euler_copilot' password = '${mongo-password}' database = 'euler_copilot' +[redis] +host = 'redis-db.{{ .Release.Namespace }}.svc.cluster.local' +port = 6379 +password = '${redis-password}' +database = 0 +decode_responses = true +socket_timeout = 5.0 +socket_connect_timeout = 5.0 +max_connections = 10 +health_check_interval = 30 + [minio] endpoint = 'minio-service.{{ .Release.Namespace }}.svc.cluster.local:9000' access_key = 'minioadmin' diff --git a/deploy/chart/euler_copilot/configs/framework/config.toml b/deploy/chart/euler_copilot/configs/framework/config.toml index 31d18a967840c47dd4138901ab6f6416503b97eb..d955b42b5096b43fa897142ee8e370cb08c4e833 100644 --- a/deploy/chart/euler_copilot/configs/framework/config.toml +++ b/deploy/chart/euler_copilot/configs/framework/config.toml @@ -14,18 +14,34 @@ data_dir = '/app/data' [login] provider = '{{ .Values.login.provider | default "authhub" }}' {{- if eq (.Values.login.provider | default "authhub") "authelia" }} +{{- $eulerCopilotDomain := .Values.domain.euler_copilot | default "http://127.0.0.1:30080" }} +{{- $autheliaDomain := .Values.domain.authelia }} +{{- if not $autheliaDomain }} + {{- $baseHost := regexReplaceAll "^https?://|:[0-9]+$" $eulerCopilotDomain "" }} + {{- $protocol := regexFind "^https?://" $eulerCopilotDomain }} + {{- $autheliaPort := .Values.ports.authelia | default 30091 | toString }} + {{- $autheliaDomain = printf "%s%s:%s" $protocol $baseHost $autheliaPort }} +{{- end }} [login.settings] -host = '{{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}' +host = '{{ $autheliaDomain }}' client_id = '{{ .Values.login.authelia.client_id | default "euler-copilot" }}' client_secret = '{{ .Values.login.authelia.client_secret | default "your-client-secret-here" }}' -redirect_uri = '{{ .Values.domain.euler_copilot | default "http://127.0.0.1:30080" }}/api/auth/login' -authorization_endpoint = '{{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}/api/oidc/authorization' -token_endpoint = '{{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}/api/oidc/token' -userinfo_endpoint = '{{ .Values.domain.authelia | default "http://127.0.0.1:30091" }}/api/oidc/userinfo' +redirect_uri = '{{ $eulerCopilotDomain }}/api/auth/login' +authorization_endpoint = '{{ $autheliaDomain }}/api/oidc/authorization' +token_endpoint = '{{ $autheliaDomain }}/api/oidc/token' +userinfo_endpoint = '{{ $autheliaDomain }}/api/oidc/userinfo' scopes = 'openid profile email' {{- else }} [login.settings] -host = '{{ if .Values.domain.authhub }}{{ .Values.domain.authhub }}{{ else }}http://{{ regexReplaceAll "^https?://|:[0-9]+$" (.Values.domain.euler_copilot | default "http://127.0.0.1") "" }}:{{ .Values.ports.authhub | default 30081 }}{{ end }}' +{{- $eulerCopilotDomain := .Values.domain.euler_copilot | default "http://127.0.0.1:30080" }} +{{- $authhubDomain := .Values.domain.authhub }} +{{- if not $authhubDomain }} + {{- $baseHost := regexReplaceAll "^https?://|:[0-9]+$" $eulerCopilotDomain "" }} + {{- $protocol := regexFind "^https?://" $eulerCopilotDomain }} + {{- $authhubPort := .Values.ports.authhub | default 30081 | toString }} + {{- $authhubDomain = printf "%s%s:%s" $protocol $baseHost $authhubPort }} +{{- end }} +host = '{{ $authhubDomain }}' host_inner = 'http://authhub-backend-service.{{ .Release.Namespace }}.svc.cluster.local:11120' login_api = '{{ .Values.domain.euler_copilot | default "http://127.0.0.1:30080" }}/api/auth/login' app_id = '${clientId}' diff --git a/deploy/chart/euler_copilot/values.yaml b/deploy/chart/euler_copilot/values.yaml index 095c27dd5ea63fe818a0c5185f60141e22800914..330110d1d7006e298da64dfa46444258b3a7a873 100644 --- a/deploy/chart/euler_copilot/values.yaml +++ b/deploy/chart/euler_copilot/values.yaml @@ -76,27 +76,35 @@ login: secret: # Authelia OIDC设置,仅在provider为authelia时有效 authelia: - # [必填] OIDC客户端ID - client_id: euler-copilot + # [必填] Authelia服务URL + url: + # [必填] OIDC客户端ID(建议使用UUID) + client_id: # [必填] OIDC客户端密钥 - client_secret: your-client-secret-here + client_secret: #域名设置 domain: # [必填] EulerCopilot的web前端url;默认为http://127.0.0.1:30080 euler_copilot: - # [必填] authhub的web前端url;默认为http://127.0.0.1:30081 - # 如果不设置,将自动使用 euler_copilot + authhub_port 构建 + # [可选] authhub的web前端url + # 如果不设置,将自动从euler_copilot域名和ports.authhub构建 + # 建议:部署认证服务时,将实际的认证服务地址设置到这里 authhub: - # [必填] authelia的web前端url;默认为http://127.0.0.1:30091 - # 如果不设置,将自动使用 euler_copilot + authelia_port 构建 + # [可选] authelia的web前端url + # 如果不设置,将自动从euler_copilot域名和ports.authelia构建 + # 建议:部署认证服务时,将实际的认证服务地址设置到这里 + # 例如:如果认证服务部署在 https://10.211.55.10:30091,请设置此值 authelia: -# 服务端口设置(用于自动构建域名) +# 服务端口设置(仅在domain未设置时用于自动构建域名) +# 注意:这些端口仅作为fallback,实际应该在domain中设置完整的认证服务地址 ports: # AuthHub web服务端口;默认为30081 + # 仅在domain.authhub未设置时使用 authhub: 30081 - # Authelia服务端口;默认为30091 + # Authelia服务端口;默认为30091 + # 仅在domain.authelia未设置时使用 authelia: 30091 # 存储设置 @@ -238,3 +246,6 @@ euler_copilot: type: # 当类型为NodePort时,填写主机的端口号 nodePort: +tls: + enabled: + secretName: eulercopilot-tls-secret diff --git a/deploy/chart/oidc-private-key.pem b/deploy/chart/oidc-private-key.pem deleted file mode 100644 index f27aa38e2abec4f94590b4e1359570dc0997d4e6..0000000000000000000000000000000000000000 --- a/deploy/chart/oidc-private-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDmzPYLuZxMNi4j -odCb8l69/59fKVBXz+wUbvzp6xT8/7jLxmOp0LlBboFoUwUflYHRvyhJwTSo8WM0 -eVk6n32xDAssIYfKewYlBur1Ufp8orIDN2aIj75coQ8rmaKCXpM7Neun61B8VIWi -KOuH4yWDJTpor+bpIQd31pFP40W6v157EJ8TRZH4WLl0NrFZHPydKV5IieaA7pNs -s3PC5MPav2H6a+UvONwT7QTF4MidEGYLlWwghPFHAYZWfh1hdeFgjUylYGGnu0oR -L2A3RrgG4r7me+65KAARPGGe05/On5FtskIkVaLAFlEjwvl6QmksnNsl7W9Es9Qa -NLGbM1wzAgMBAAECggEAAwNG7PVB0I7egFGS7w41ZWaKVowsA9Fvg/7E52rNrdXK -vEI9N9DFMlD2eH2o8fYaKTpFFN1kShZIlL1GyGifk+mT1cIkSyTa+i3CcGxfCDR5 -fWhCOuMheYUStR9+yxPf+SRljJ4aQV07F1hQZsg0y6b3QbmWGpH3Tn2rDO9rEBp8 -D3mQy9tVh+Lls2j0hmsKnh4neKqS0DAdxvM4VqYttGev2CE2pKAbg2WSQbnGGr0C -BdYN60iNafNTmS1lZwNfXfswh26gJpWNXjZC1H2nhm3iijLAkCKTOnWZKGQx1Wyd -mvyu5pFKDi+elNzW7xkWk7quu9sWivolDTKH3tUHiQKBgQD+V3sj2BzyFuurxNBT -G/izek1O1VZT736kfJbjIINQ2IYBoxKZlvDRy1wV+A6FqpgRAF+qVbV9Tzc6RweG -quetnTnW+W0jSav8M+Ungzdw+QIeo7fCOajiTqYnK/OnKNGvWoeNYV1qyc9guOx6 -IWwNfjkYGoxKEgHSESzhpdfBGQKBgQDoTjAbf1ivrKGxk6xo5leYPM/qGUQvvZij -9Vlfv2bhGqUHOPVqw5ik1vqrFjpyrB1vRiMgsy3N44UCJZvR/WSkxRrAJWU6trlJ -ckXetM/7xTBh/nO/6FbCe0UZYsKUET8i+rE/iLXmbSw6CSjZb99Ib1JqwdHW7V12 -yUSJrZH1KwKBgQDmmDXix5tmufUtpKEj2KZhQq84nB9KtkZhpzu97Mph0A2fClRg -wALSkseIe3/fHE4GjAowCskjNWDjC2b9Z5HpTDUWfMnARpkRbZODgWoQmRKc6N4/ -z7sYgyoNNcIQeeeyTie7vNzPZu0HymFMUWT/zsLA4cDznuPbqwrAAnZccQKBgF0I -Yw/AEj5YfnDlWRixlsbrI8D22W3dGjIfYemMd6ES0KBx2Scmqgzp4318629dKnST -vCGuYFuiEHRpp1uThSjPnwTwdPfskYuubfjgpKKJt/SmuKhUXdqJD4U5Hf+6YHSb -vTxafa6IbjvXyzo5KUyS+Ii0klrmuJWHySDyGA/9AoGAEUKVwr07rV3jK/8lTkzm -cUL7+BsoDVDitnLO/ZDKk549dVb01saPb6CXvMRJ7h1uvOA0yIxbv0zHUrB797c6 -3Gfn7fcv6sPP4QKaSuDiZIA6pP5CoUSNPrA6ozufafd1dwy7A5JQeHj+6mbK/fRC -MNV0EgRsolbDjoqe1UUp7kQ= ------END PRIVATE KEY----- diff --git a/deploy/chart/oidc-public-key.pem b/deploy/chart/oidc-public-key.pem deleted file mode 100644 index 22a648101cfda37ce6f05641b95cb17ac8d80a82..0000000000000000000000000000000000000000 --- a/deploy/chart/oidc-public-key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5sz2C7mcTDYuI6HQm/Je -vf+fXylQV8/sFG786esU/P+4y8ZjqdC5QW6BaFMFH5WB0b8oScE0qPFjNHlZOp99 -sQwLLCGHynsGJQbq9VH6fKKyAzdmiI++XKEPK5migl6TOzXrp+tQfFSFoijrh+Ml -gyU6aK/m6SEHd9aRT+NFur9eexCfE0WR+Fi5dDaxWRz8nSleSInmgO6TbLNzwuTD -2r9h+mvlLzjcE+0ExeDInRBmC5VsIITxRwGGVn4dYXXhYI1MpWBhp7tKES9gN0a4 -BuK+5nvuuSgAETxhntOfzp+RbbJCJFWiwBZRI8L5ekJpLJzbJe1vRLPUGjSxmzNc -MwIDAQAB ------END PUBLIC KEY----- diff --git a/deploy/scripts/2-install-tools/install_tools.sh b/deploy/scripts/2-install-tools/install_tools.sh index 2b4f641dfe64bd514fc4f70480f53fedf9264ea3..7becb9eb2d8afe223969293a77e83833daac9c93 100755 --- a/deploy/scripts/2-install-tools/install_tools.sh +++ b/deploy/scripts/2-install-tools/install_tools.sh @@ -123,6 +123,20 @@ install_basic_tools() { PyYAML ruamel.yaml requests; then echo -e "[ERROR] Failed to install PyYAML ruamel.yaml and requests via pip" >&2 fi + + # 安装 argon2 工具(用于认证服务密码哈希) + echo "Installing argon2 password hashing tool..." + if ! command -v argon2 &> /dev/null; then + if yum install -y argon2; then + echo -e "\033[32m[Success] argon2 安装完成\033[0m" + else + echo -e "\033[31m[Error] argon2 安装失败\033[0m" + return 1 + fi + else + echo -e "\033[32m[Info] argon2 已经安装,跳过安装步骤\033[0m" + fi + echo "All basic tools have been installed." return 0 } diff --git a/deploy/scripts/7-install-auth-service/authelia_automation.sh b/deploy/scripts/7-install-auth-service/authelia_automation.sh index bf963caff9c202683ae00994e995d00610224a5e..a88a09de794f3ba7a7b89a3e0434a9a74d73243b 100755 --- a/deploy/scripts/7-install-auth-service/authelia_automation.sh +++ b/deploy/scripts/7-install-auth-service/authelia_automation.sh @@ -12,9 +12,9 @@ NC='\033[0m' # 全局变量 NAMESPACE="euler-copilot" AUTHELIA_POD="" -CONFIG_DIR="/config" -USERS_FILE="$CONFIG_DIR/users_database.yml" -AUTHELIA_CONFIG_FILE="$CONFIG_DIR/configuration.yml" +CONFIG_DIR="/etc/authelia" +USERS_FILE="$CONFIG_DIR/users.yml" +AUTHELIA_CONFIG_FILE="/app/configuration.yml" # 打印帮助信息 print_help() { @@ -293,7 +293,166 @@ generate_oidc_keys() { log_info "密钥已保存到临时文件: /tmp/authelia_hmac_secret, /tmp/authelia_private_key" } -# 创建OIDC客户端 +# 通过ConfigMap创建OIDC客户端 +create_oidc_client_via_configmap() { + local client_id="$1" + local client_name="$2" + local redirect_uri="$3" + local client_secret="${4:-$(openssl rand -base64 32)}" + + if [ -z "$client_id" ] || [ -z "$client_name" ] || [ -z "$redirect_uri" ]; then + log_error "客户端ID、名称和重定向URI不能为空" + return 1 + fi + + log_info "通过ConfigMap创建OIDC客户端: $client_id" + + # 获取当前ConfigMap + local temp_config="/tmp/authelia-config-$(date +%s).yaml" + if ! kubectl get configmap authelia-config -n $NAMESPACE -o yaml > "$temp_config"; then + log_error "无法获取authelia ConfigMap" + return 1 + fi + + # 创建新的客户端配置 + local new_client_config=" - client_id: \"$client_id\" + client_name: \"$client_name\" + client_secret: \"$client_secret\" + public: false + authorization_policy: \"two_factor\" + token_endpoint_auth_method: \"client_secret_basic\" + require_pkce: true + pkce_challenge_method: \"S256\" + redirect_uris: + - \"$redirect_uri\" + scopes: + - \"openid\" + - \"profile\" + - \"email\" + - \"groups\" + response_types: + - \"code\" + grant_types: + - \"authorization_code\" + response_modes: + - \"query\" + - \"form_post\" + userinfo_signed_response_alg: \"none\" + consent_mode: \"implicit\" + pre_configured_consent_duration: \"1y\"" + + # 使用sed替换现有客户端配置 + local updated_config="/tmp/authelia-config-updated-$(date +%s).yaml" + + # 提取现有配置并替换客户端部分 + python3 << EOF +import yaml +import sys + +try: + with open('$temp_config', 'r') as f: + config = yaml.safe_load(f) + + # 获取现有的authelia.yml配置 + authelia_yml = config['data']['authelia.yml'] + + # 解析YAML内容 + authelia_config = yaml.safe_load(authelia_yml) + + # 确保identity_providers.oidc.clients存在 + if 'identity_providers' not in authelia_config: + authelia_config['identity_providers'] = {} + if 'oidc' not in authelia_config['identity_providers']: + authelia_config['identity_providers']['oidc'] = {} + if 'clients' not in authelia_config['identity_providers']['oidc']: + authelia_config['identity_providers']['oidc']['clients'] = [] + + # 创建新客户端 + new_client = { + 'client_id': '$client_id', + 'client_name': '$client_name', + 'client_secret': '$client_secret', + 'public': False, + 'authorization_policy': 'two_factor', + 'token_endpoint_auth_method': 'client_secret_basic', + 'require_pkce': True, + 'pkce_challenge_method': 'S256', + 'redirect_uris': ['$redirect_uri'], + 'scopes': ['openid', 'profile', 'email', 'groups'], + 'response_types': ['code'], + 'grant_types': ['authorization_code'], + 'response_modes': ['query', 'form_post'], + 'userinfo_signed_response_alg': 'none', + 'consent_mode': 'implicit', + 'pre_configured_consent_duration': '1y' + } + + # 检查客户端是否已存在,如果存在则替换,否则添加 + client_exists = False + for i, client in enumerate(authelia_config['identity_providers']['oidc']['clients']): + if client.get('client_id') == '$client_id': + authelia_config['identity_providers']['oidc']['clients'][i] = new_client + client_exists = True + break + + if not client_exists: + authelia_config['identity_providers']['oidc']['clients'].append(new_client) + + # 更新ConfigMap数据 + config['data']['authelia.yml'] = yaml.dump(authelia_config, default_flow_style=False, allow_unicode=True) + + # 保存更新后的ConfigMap + with open('$updated_config', 'w') as f: + yaml.dump(config, f, default_flow_style=False, allow_unicode=True) + + print("SUCCESS") + +except Exception as e: + print(f"ERROR: {e}") + sys.exit(1) +EOF + + local python_result=$? + if [ $python_result -ne 0 ]; then + log_error "配置更新失败" + rm -f "$temp_config" "$updated_config" + return 1 + fi + + # 应用更新后的ConfigMap + if kubectl apply -f "$updated_config"; then + log_success "ConfigMap更新成功" + + # 重启authelia deployment以加载新配置 + log_info "重启authelia服务以加载新配置..." + kubectl rollout restart deployment/authelia -n $NAMESPACE + + # 等待重启完成 + log_info "等待authelia重启完成..." + kubectl rollout status deployment/authelia -n $NAMESPACE --timeout=120s + + log_success "OIDC客户端 $client_id 创建成功" + log_info "客户端名称: $client_name" + log_info "客户端密钥: $client_secret" + log_info "重定向URI: $redirect_uri" + + # 清理临时文件 + rm -f "$temp_config" "$updated_config" + + # 保存客户端信息到临时文件供其他脚本使用 + echo "Client ID: $client_id" > /tmp/authelia_oidc_client_info + echo "Client Secret: $client_secret" >> /tmp/authelia_oidc_client_info + echo "Redirect URI: $redirect_uri" >> /tmp/authelia_oidc_client_info + + return 0 + else + log_error "ConfigMap应用失败" + rm -f "$temp_config" "$updated_config" + return 1 + fi +} + +# 创建OIDC客户端(保持向后兼容) create_oidc_client() { local client_id="$1" local client_name="$2" @@ -307,6 +466,13 @@ create_oidc_client() { log_info "创建OIDC客户端: $client_id" + # 优先尝试通过ConfigMap方式创建 + if create_oidc_client_via_configmap "$client_id" "$client_name" "$redirect_uri" "$client_secret"; then + return 0 + fi + + log_warning "ConfigMap方式失败,尝试传统方式..." + # 检查是否已有HMAC密钥 local hmac_secret if [ -f "/tmp/authelia_hmac_secret" ]; then @@ -369,7 +535,7 @@ $(echo "$private_key" | sed 's/^/ /') - 'query' consent_mode: 'implicit' pre_configured_consent_duration: '1y' - require_pkce: false + require_pkce: true pkce_challenge_method: 'S256' " diff --git a/deploy/scripts/7-install-auth-service/authelia_user_manager.sh b/deploy/scripts/7-install-auth-service/authelia_user_manager.sh new file mode 100755 index 0000000000000000000000000000000000000000..a88a09de794f3ba7a7b89a3e0434a9a74d73243b --- /dev/null +++ b/deploy/scripts/7-install-auth-service/authelia_user_manager.sh @@ -0,0 +1,712 @@ +#!/bin/bash + +set -eo pipefail + +# 颜色定义 +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +NC='\033[0m' + +# 全局变量 +NAMESPACE="euler-copilot" +AUTHELIA_POD="" +CONFIG_DIR="/etc/authelia" +USERS_FILE="$CONFIG_DIR/users.yml" +AUTHELIA_CONFIG_FILE="/app/configuration.yml" + +# 打印帮助信息 +print_help() { + echo -e "${GREEN}Authelia 自动化管理脚本${NC}" + echo -e "${GREEN}用法: $0 [命令] [选项]${NC}" + echo "" + echo -e "${BLUE}命令:${NC}" + echo -e " create-user <用户名> <密码> <邮箱> [组名] 创建新用户" + echo -e " change-password <用户名> <新密码> 修改用户密码" + echo -e " delete-user <用户名> 删除用户" + echo -e " list-users 列出所有用户" + echo -e " create-oidc-client <客户端ID> <客户端名称> <重定向URI> [密钥] 创建OIDC客户端" + echo -e " update-oidc-client <客户端ID> [选项] 更新OIDC客户端配置" + echo -e " delete-oidc-client <客户端ID> 删除OIDC客户端" + echo -e " list-oidc-clients 列出所有OIDC客户端" + echo -e " generate-keys 生成OIDC所需的密钥" + echo -e " backup-config 备份当前配置" + echo -e " restore-config <备份文件> 恢复配置" + echo -e " restart-service 重启Authelia服务" + echo "" + echo -e "${BLUE}示例:${NC}" + echo -e " # 创建用户" + echo -e " $0 create-user openEuler 'openEuler12#\$' openEuler@example.com admins" + echo "" + echo -e " # 修改密码" + echo -e " $0 change-password openEuler 'newPassword123#\$'" + echo "" + echo -e " # 创建OIDC客户端" + echo -e " $0 create-oidc-client euler-copilot 'Euler Copilot' 'http://127.0.0.1:30080/auth/callback'" + echo "" + echo -e " # 生成密钥" + echo -e " $0 generate-keys" + echo "" +} + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 获取Authelia Pod名称 +get_authelia_pod() { + AUTHELIA_POD=$(kubectl get pods -n $NAMESPACE -l app.kubernetes.io/name=authelia -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + + if [ -z "$AUTHELIA_POD" ]; then + log_error "未找到Authelia Pod,请确保Authelia服务已正确部署" + return 1 + fi + + log_info "找到Authelia Pod: $AUTHELIA_POD" + return 0 +} + +# 在Authelia Pod中执行命令 +exec_in_pod() { + local cmd="$1" + kubectl exec -n $NAMESPACE "$AUTHELIA_POD" -- sh -c "$cmd" +} + +# 生成密码哈希 +generate_password_hash() { + local password="$1" + local hash + local output + + log_info "生成密码哈希..." + + # 执行命令并捕获输出 + output=$(exec_in_pod "authelia crypto hash generate argon2 --password \"$password\"" 2>/dev/null) + + # 从输出中提取Digest行的哈希值 + hash=$(echo "$output" | grep "^Digest:" | sed 's/^Digest: //') + + if [ -z "$hash" ]; then + log_error "密码哈希生成失败" + log_error "命令输出: $output" + return 1 + fi + + echo "$hash" +} + +# 检查用户是否存在 +user_exists() { + local username="$1" + exec_in_pod "test -f $USERS_FILE && grep -q '^ $username:' $USERS_FILE" 2>/dev/null +} + +# 创建用户 +create_user() { + local username="$1" + local password="$2" + local email="$3" + local groups="${4:-admins}" + + if [ -z "$username" ] || [ -z "$password" ] || [ -z "$email" ]; then + log_error "用户名、密码和邮箱不能为空" + return 1 + fi + + log_info "创建用户: $username" + + # 检查用户是否已存在 + if user_exists "$username"; then + log_error "用户 $username 已存在" + return 1 + fi + + # 生成密码哈希 + local password_hash + password_hash=$(generate_password_hash "$password") + if [ $? -ne 0 ]; then + return 1 + fi + + # 创建用户配置 + local user_config=" $username: + displayname: \"$username\" + password: \"$password_hash\" + email: $email + groups: + - $groups" + + # 检查用户文件是否存在 + if ! exec_in_pod "test -f $USERS_FILE"; then + log_info "创建新的用户数据库文件" + exec_in_pod "cat > $USERS_FILE << 'EOF' +users: +$user_config +EOF" + else + # 备份原文件 + exec_in_pod "cp $USERS_FILE ${USERS_FILE}.backup.\$(date +%Y%m%d_%H%M%S)" + + # 检查文件是否以 ... 结尾(YAML文档结束标记) + local yaml_end_count + yaml_end_count=$(exec_in_pod "tail -1 $USERS_FILE | grep -c '^\\.\\.\\.\$' 2>/dev/null || echo 0") + + if [ "$yaml_end_count" -eq 1 ]; then + # 移除 ... 标记,添加用户,然后重新添加 ... 标记 + exec_in_pod " + head -n -1 $USERS_FILE > /tmp/users_temp.yml + cat >> /tmp/users_temp.yml << 'EOF' +$user_config +... +EOF + mv /tmp/users_temp.yml $USERS_FILE + " + else + # 直接添加用户配置 + exec_in_pod " + cat >> $USERS_FILE << 'EOF' +$user_config +EOF + " + fi + fi + + log_success "用户 $username 创建成功" + log_info "邮箱: $email" + log_info "组: $groups" +} + +# 修改用户密码 +change_password() { + local username="$1" + local new_password="$2" + + if [ -z "$username" ] || [ -z "$new_password" ]; then + log_error "用户名和新密码不能为空" + return 1 + fi + + log_info "修改用户 $username 的密码" + + # 检查用户是否存在 + if ! user_exists "$username"; then + log_error "用户 $username 不存在" + return 1 + fi + + # 生成新密码哈希 + local password_hash + password_hash=$(generate_password_hash "$new_password") + if [ $? -ne 0 ]; then + return 1 + fi + + # 更新密码 + exec_in_pod " + sed -i '/^ $username:/,/^ [^[:space:]]/ { + s|password: \".*\"|password: \"$password_hash\"| + }' $USERS_FILE + " + + log_success "用户 $username 密码修改成功" +} + +# 删除用户 +delete_user() { + local username="$1" + + if [ -z "$username" ]; then + log_error "用户名不能为空" + return 1 + fi + + log_info "删除用户: $username" + + # 检查用户是否存在 + if ! user_exists "$username"; then + log_error "用户 $username 不存在" + return 1 + fi + + # 删除用户配置 + exec_in_pod " + sed -i '/^ $username:/,/^ [^[:space:]]/{ /^ $username:/d; /^ [^[:space:]]/!d; }' $USERS_FILE + " + + log_success "用户 $username 删除成功" +} + +# 列出所有用户 +list_users() { + log_info "当前用户列表:" + + if ! exec_in_pod "test -f $USERS_FILE"; then + log_warning "用户数据库文件不存在" + return 0 + fi + + exec_in_pod " + if [ -f $USERS_FILE ]; then + grep '^ [^[:space:]]' $USERS_FILE | sed 's/://g' | sed 's/^ /- /' + fi + " +} + +# 生成OIDC密钥 +generate_oidc_keys() { + log_info "生成OIDC密钥..." + + # 生成HMAC密钥 + local hmac_secret + hmac_secret=$(exec_in_pod "authelia crypto rand --length 32 --charset alphanumeric") + + # 生成RSA密钥对 + exec_in_pod "authelia crypto pair rsa generate --bits 2048 --directory $CONFIG_DIR" + + # 读取生成的私钥 + local private_key + private_key=$(exec_in_pod "cat $CONFIG_DIR/private.pem") + + log_success "OIDC密钥生成完成" + echo -e "${YELLOW}HMAC Secret:${NC} $hmac_secret" + echo -e "${YELLOW}RSA Private Key:${NC}" + echo "$private_key" + + # 保存到临时文件供后续使用 + echo "$hmac_secret" > /tmp/authelia_hmac_secret + echo "$private_key" > /tmp/authelia_private_key + + log_info "密钥已保存到临时文件: /tmp/authelia_hmac_secret, /tmp/authelia_private_key" +} + +# 通过ConfigMap创建OIDC客户端 +create_oidc_client_via_configmap() { + local client_id="$1" + local client_name="$2" + local redirect_uri="$3" + local client_secret="${4:-$(openssl rand -base64 32)}" + + if [ -z "$client_id" ] || [ -z "$client_name" ] || [ -z "$redirect_uri" ]; then + log_error "客户端ID、名称和重定向URI不能为空" + return 1 + fi + + log_info "通过ConfigMap创建OIDC客户端: $client_id" + + # 获取当前ConfigMap + local temp_config="/tmp/authelia-config-$(date +%s).yaml" + if ! kubectl get configmap authelia-config -n $NAMESPACE -o yaml > "$temp_config"; then + log_error "无法获取authelia ConfigMap" + return 1 + fi + + # 创建新的客户端配置 + local new_client_config=" - client_id: \"$client_id\" + client_name: \"$client_name\" + client_secret: \"$client_secret\" + public: false + authorization_policy: \"two_factor\" + token_endpoint_auth_method: \"client_secret_basic\" + require_pkce: true + pkce_challenge_method: \"S256\" + redirect_uris: + - \"$redirect_uri\" + scopes: + - \"openid\" + - \"profile\" + - \"email\" + - \"groups\" + response_types: + - \"code\" + grant_types: + - \"authorization_code\" + response_modes: + - \"query\" + - \"form_post\" + userinfo_signed_response_alg: \"none\" + consent_mode: \"implicit\" + pre_configured_consent_duration: \"1y\"" + + # 使用sed替换现有客户端配置 + local updated_config="/tmp/authelia-config-updated-$(date +%s).yaml" + + # 提取现有配置并替换客户端部分 + python3 << EOF +import yaml +import sys + +try: + with open('$temp_config', 'r') as f: + config = yaml.safe_load(f) + + # 获取现有的authelia.yml配置 + authelia_yml = config['data']['authelia.yml'] + + # 解析YAML内容 + authelia_config = yaml.safe_load(authelia_yml) + + # 确保identity_providers.oidc.clients存在 + if 'identity_providers' not in authelia_config: + authelia_config['identity_providers'] = {} + if 'oidc' not in authelia_config['identity_providers']: + authelia_config['identity_providers']['oidc'] = {} + if 'clients' not in authelia_config['identity_providers']['oidc']: + authelia_config['identity_providers']['oidc']['clients'] = [] + + # 创建新客户端 + new_client = { + 'client_id': '$client_id', + 'client_name': '$client_name', + 'client_secret': '$client_secret', + 'public': False, + 'authorization_policy': 'two_factor', + 'token_endpoint_auth_method': 'client_secret_basic', + 'require_pkce': True, + 'pkce_challenge_method': 'S256', + 'redirect_uris': ['$redirect_uri'], + 'scopes': ['openid', 'profile', 'email', 'groups'], + 'response_types': ['code'], + 'grant_types': ['authorization_code'], + 'response_modes': ['query', 'form_post'], + 'userinfo_signed_response_alg': 'none', + 'consent_mode': 'implicit', + 'pre_configured_consent_duration': '1y' + } + + # 检查客户端是否已存在,如果存在则替换,否则添加 + client_exists = False + for i, client in enumerate(authelia_config['identity_providers']['oidc']['clients']): + if client.get('client_id') == '$client_id': + authelia_config['identity_providers']['oidc']['clients'][i] = new_client + client_exists = True + break + + if not client_exists: + authelia_config['identity_providers']['oidc']['clients'].append(new_client) + + # 更新ConfigMap数据 + config['data']['authelia.yml'] = yaml.dump(authelia_config, default_flow_style=False, allow_unicode=True) + + # 保存更新后的ConfigMap + with open('$updated_config', 'w') as f: + yaml.dump(config, f, default_flow_style=False, allow_unicode=True) + + print("SUCCESS") + +except Exception as e: + print(f"ERROR: {e}") + sys.exit(1) +EOF + + local python_result=$? + if [ $python_result -ne 0 ]; then + log_error "配置更新失败" + rm -f "$temp_config" "$updated_config" + return 1 + fi + + # 应用更新后的ConfigMap + if kubectl apply -f "$updated_config"; then + log_success "ConfigMap更新成功" + + # 重启authelia deployment以加载新配置 + log_info "重启authelia服务以加载新配置..." + kubectl rollout restart deployment/authelia -n $NAMESPACE + + # 等待重启完成 + log_info "等待authelia重启完成..." + kubectl rollout status deployment/authelia -n $NAMESPACE --timeout=120s + + log_success "OIDC客户端 $client_id 创建成功" + log_info "客户端名称: $client_name" + log_info "客户端密钥: $client_secret" + log_info "重定向URI: $redirect_uri" + + # 清理临时文件 + rm -f "$temp_config" "$updated_config" + + # 保存客户端信息到临时文件供其他脚本使用 + echo "Client ID: $client_id" > /tmp/authelia_oidc_client_info + echo "Client Secret: $client_secret" >> /tmp/authelia_oidc_client_info + echo "Redirect URI: $redirect_uri" >> /tmp/authelia_oidc_client_info + + return 0 + else + log_error "ConfigMap应用失败" + rm -f "$temp_config" "$updated_config" + return 1 + fi +} + +# 创建OIDC客户端(保持向后兼容) +create_oidc_client() { + local client_id="$1" + local client_name="$2" + local redirect_uri="$3" + local client_secret="${4:-$(openssl rand -base64 32)}" + + if [ -z "$client_id" ] || [ -z "$client_name" ] || [ -z "$redirect_uri" ]; then + log_error "客户端ID、名称和重定向URI不能为空" + return 1 + fi + + log_info "创建OIDC客户端: $client_id" + + # 优先尝试通过ConfigMap方式创建 + if create_oidc_client_via_configmap "$client_id" "$client_name" "$redirect_uri" "$client_secret"; then + return 0 + fi + + log_warning "ConfigMap方式失败,尝试传统方式..." + + # 检查是否已有HMAC密钥 + local hmac_secret + if [ -f "/tmp/authelia_hmac_secret" ]; then + hmac_secret=$(cat /tmp/authelia_hmac_secret) + else + log_warning "未找到HMAC密钥,正在生成..." + generate_oidc_keys > /dev/null + hmac_secret=$(cat /tmp/authelia_hmac_secret) + fi + + # 检查是否已有私钥 + local private_key + if [ -f "/tmp/authelia_private_key" ]; then + private_key=$(cat /tmp/authelia_private_key) + else + log_error "未找到RSA私钥,请先运行 generate-keys 命令" + return 1 + fi + + # 创建OIDC配置 + local oidc_config=" +identity_providers: + oidc: + hmac_secret: '$hmac_secret' + issuer_private_key: | +$(echo "$private_key" | sed 's/^/ /') + enable_client_debug_messages: false + lifespans: + access_token: 1h + refresh_token: 90m + id_token: 1h + authorize_code: 1m + jwks: + - key_id: 'main-signing-key' + algorithm: 'RS256' + use: 'sig' + key: | +$(echo "$private_key" | sed 's/^/ /') + clients: + - client_id: '$client_id' + client_name: '$client_name' + client_secret: '\$plaintext\$$client_secret' + public: false + authorization_policy: 'one_factor' + redirect_uris: + - '$redirect_uri' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'groups' + - 'offline_access' + response_types: + - 'code' + grant_types: + - 'authorization_code' + - 'refresh_token' + response_modes: + - 'form_post' + - 'query' + consent_mode: 'implicit' + pre_configured_consent_duration: '1y' + require_pkce: true + pkce_challenge_method: 'S256' +" + + # 更新Authelia配置 - 添加OIDC配置 + exec_in_pod " + # 备份原配置 + cp $AUTHELIA_CONFIG_FILE ${AUTHELIA_CONFIG_FILE}.backup.\$(date +%Y%m%d_%H%M%S) + + # 简单方法:在...之前添加OIDC配置 + head -n -1 $AUTHELIA_CONFIG_FILE > /tmp/config_temp.yml + cat >> /tmp/config_temp.yml << 'EOF' +$oidc_config +... +EOF + mv /tmp/config_temp.yml $AUTHELIA_CONFIG_FILE + " + + log_success "OIDC客户端 $client_id 创建成功" + log_info "客户端名称: $client_name" + log_info "客户端密钥: $client_secret" + log_info "重定向URI: $redirect_uri" + + # 保存客户端信息 + echo "Client ID: $client_id" >> /tmp/authelia_oidc_clients + echo "Client Secret: $client_secret" >> /tmp/authelia_oidc_clients + echo "---" >> /tmp/authelia_oidc_clients +} + +# 列出OIDC客户端 +list_oidc_clients() { + log_info "当前OIDC客户端列表:" + + if ! exec_in_pod "test -f $AUTHELIA_CONFIG_FILE"; then + log_warning "Authelia配置文件不存在" + return 0 + fi + + exec_in_pod " + if grep -q 'clients:' $AUTHELIA_CONFIG_FILE; then + grep -A 20 'clients:' $AUTHELIA_CONFIG_FILE | grep 'client_id:' | sed 's/.*client_id: /- /' | sed \"s/'//g\" + else + echo '暂无OIDC客户端' + fi + " +} + +# 备份配置 +backup_config() { + local backup_dir="/tmp/authelia_backup_$(date +%Y%m%d_%H%M%S)" + + log_info "备份Authelia配置到: $backup_dir" + + mkdir -p "$backup_dir" + + # 从Pod中复制配置文件 + kubectl cp "$NAMESPACE/$AUTHELIA_POD:$USERS_FILE" "$backup_dir/users_database.yml" 2>/dev/null || log_warning "用户数据库文件不存在,跳过备份" + kubectl cp "$NAMESPACE/$AUTHELIA_POD:$AUTHELIA_CONFIG_FILE" "$backup_dir/configuration.yml" 2>/dev/null || log_warning "配置文件不存在,跳过备份" + + # 备份密钥文件 + kubectl cp "$NAMESPACE/$AUTHELIA_POD:$CONFIG_DIR/private.pem" "$backup_dir/private.pem" 2>/dev/null || log_warning "私钥文件不存在,跳过备份" + kubectl cp "$NAMESPACE/$AUTHELIA_POD:$CONFIG_DIR/public.pem" "$backup_dir/public.pem" 2>/dev/null || log_warning "公钥文件不存在,跳过备份" + + log_success "配置备份完成: $backup_dir" + echo "$backup_dir" +} + +# 恢复配置 +restore_config() { + local backup_dir="$1" + + if [ -z "$backup_dir" ] || [ ! -d "$backup_dir" ]; then + log_error "备份目录不存在: $backup_dir" + return 1 + fi + + log_info "从备份恢复配置: $backup_dir" + + # 恢复配置文件 + if [ -f "$backup_dir/users_database.yml" ]; then + kubectl cp "$backup_dir/users_database.yml" "$NAMESPACE/$AUTHELIA_POD:$USERS_FILE" + log_info "用户数据库已恢复" + fi + + if [ -f "$backup_dir/configuration.yml" ]; then + kubectl cp "$backup_dir/configuration.yml" "$NAMESPACE/$AUTHELIA_POD:$AUTHELIA_CONFIG_FILE" + log_info "主配置文件已恢复" + fi + + if [ -f "$backup_dir/private.pem" ]; then + kubectl cp "$backup_dir/private.pem" "$NAMESPACE/$AUTHELIA_POD:$CONFIG_DIR/private.pem" + log_info "私钥文件已恢复" + fi + + if [ -f "$backup_dir/public.pem" ]; then + kubectl cp "$backup_dir/public.pem" "$NAMESPACE/$AUTHELIA_POD:$CONFIG_DIR/public.pem" + log_info "公钥文件已恢复" + fi + + log_success "配置恢复完成" +} + +# 重启Authelia服务 +restart_service() { + log_info "重启Authelia服务..." + + kubectl rollout restart deployment/authelia -n $NAMESPACE + + log_info "等待服务重启完成..." + kubectl rollout status deployment/authelia -n $NAMESPACE --timeout=300s + + log_success "Authelia服务重启完成" +} + +# 主函数 +main() { + local command="$1" + shift + + case "$command" in + "create-user") + get_authelia_pod || exit 1 + create_user "$@" + ;; + "change-password") + get_authelia_pod || exit 1 + change_password "$@" + ;; + "delete-user") + get_authelia_pod || exit 1 + delete_user "$@" + ;; + "list-users") + get_authelia_pod || exit 1 + list_users + ;; + "create-oidc-client") + get_authelia_pod || exit 1 + create_oidc_client "$@" + ;; + "list-oidc-clients") + get_authelia_pod || exit 1 + list_oidc_clients + ;; + "generate-keys") + get_authelia_pod || exit 1 + generate_oidc_keys + ;; + "backup-config") + get_authelia_pod || exit 1 + backup_config + ;; + "restore-config") + get_authelia_pod || exit 1 + restore_config "$@" + ;; + "restart-service") + restart_service + ;; + "--help"|"help"|"") + print_help + ;; + *) + log_error "未知命令: $command" + print_help + exit 1 + ;; + esac +} + +# 设置中断处理 +trap 'echo -e "${RED}操作被中断!${NC}"; exit 1' INT + +# 执行主函数 +main "$@" diff --git a/deploy/scripts/7-install-auth-service/demo.sh b/deploy/scripts/7-install-auth-service/demo.sh deleted file mode 100755 index 197ecbfc443a76795d7416b6ec3c968c7eeca885..0000000000000000000000000000000000000000 --- a/deploy/scripts/7-install-auth-service/demo.sh +++ /dev/null @@ -1,303 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -# 颜色定义 -RED='\033[31m' -GREEN='\033[32m' -YELLOW='\033[33m' -BLUE='\033[34m' -NC='\033[0m' - -# 脚本目录 -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -AUTOMATION_SCRIPT="$SCRIPT_DIR/authelia_automation.sh" -QUICK_SETUP_SCRIPT="$SCRIPT_DIR/quick_setup.sh" - -# 演示配置 -DEMO_USERNAME="demo-user" -DEMO_PASSWORD="DemoPass123#\$" -DEMO_EMAIL="demo@example.com" -DEMO_CLIENT_ID="demo-client" -DEMO_CLIENT_NAME="Demo Application" -DEMO_REDIRECT_URI="http://127.0.0.1:30080/demo/callback" - -# 日志函数 -log_info() { - echo -e "${BLUE}[DEMO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# 等待用户确认 -wait_for_confirmation() { - local message="$1" - echo -e "${YELLOW}$message${NC}" - read -p "按回车键继续,或 Ctrl+C 退出..." - echo "" -} - -# 检查Authelia服务状态 -check_authelia_status() { - log_info "检查Authelia服务状态..." - - if ! kubectl get deployment authelia -n euler-copilot &> /dev/null; then - log_error "Authelia服务未部署,请先运行安装脚本" - return 1 - fi - - local ready_replicas=$(kubectl get deployment authelia -n euler-copilot -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - local desired_replicas=$(kubectl get deployment authelia -n euler-copilot -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "1") - - if [ "$ready_replicas" != "$desired_replicas" ]; then - log_warning "Authelia服务未完全就绪 ($ready_replicas/$desired_replicas)" - log_info "等待服务就绪..." - kubectl wait --for=condition=available --timeout=300s deployment/authelia -n euler-copilot - fi - - log_success "Authelia服务状态正常" -} - -# 演示1:快速配置 -demo_quick_setup() { - echo -e "${GREEN}=== 演示1:使用快速配置脚本 ===${NC}" - echo "" - echo "这个演示将展示如何使用快速配置脚本一键完成Authelia配置" - echo "包括:" - echo "- 生成OIDC密钥" - echo "- 创建演示用户" - echo "- 创建OIDC客户端" - echo "- 重启服务" - echo "" - - wait_for_confirmation "准备开始快速配置演示..." - - log_info "执行快速配置..." - echo -e "${BLUE}命令: $QUICK_SETUP_SCRIPT --username $DEMO_USERNAME --password '$DEMO_PASSWORD' --email $DEMO_EMAIL --client-id $DEMO_CLIENT_ID --client-name '$DEMO_CLIENT_NAME' --redirect-uri '$DEMO_REDIRECT_URI'${NC}" - echo "" - - # 注意:这里只是演示命令,实际执行需要Authelia服务运行 - log_warning "注意:这是演示模式,实际执行需要Authelia服务正常运行" - echo "" - - log_success "快速配置演示完成" -} - -# 演示2:分步配置 -demo_step_by_step() { - echo -e "${GREEN}=== 演示2:分步配置 ===${NC}" - echo "" - echo "这个演示将展示如何使用自动化脚本分步完成配置" - echo "" - - wait_for_confirmation "准备开始分步配置演示..." - - # 步骤1:生成密钥 - log_info "步骤1:生成OIDC密钥" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT generate-keys${NC}" - log_warning "这将生成HMAC密钥和RSA密钥对" - echo "" - - wait_for_confirmation "继续下一步..." - - # 步骤2:创建用户 - log_info "步骤2:创建用户" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT create-user $DEMO_USERNAME '$DEMO_PASSWORD' $DEMO_EMAIL users${NC}" - log_warning "这将创建一个新用户并生成密码哈希" - echo "" - - wait_for_confirmation "继续下一步..." - - # 步骤3:创建OIDC客户端 - log_info "步骤3:创建OIDC客户端" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT create-oidc-client $DEMO_CLIENT_ID '$DEMO_CLIENT_NAME' '$DEMO_REDIRECT_URI'${NC}" - log_warning "这将创建OIDC客户端配置" - echo "" - - wait_for_confirmation "继续下一步..." - - # 步骤4:重启服务 - log_info "步骤4:重启Authelia服务" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT restart-service${NC}" - log_warning "这将重启Authelia服务使配置生效" - echo "" - - log_success "分步配置演示完成" -} - -# 演示3:管理操作 -demo_management() { - echo -e "${GREEN}=== 演示3:管理操作 ===${NC}" - echo "" - echo "这个演示将展示常见的管理操作" - echo "" - - wait_for_confirmation "准备开始管理操作演示..." - - # 列出用户 - log_info "列出所有用户" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT list-users${NC}" - echo "" - - wait_for_confirmation "继续下一步...") - - # 修改密码 - log_info "修改用户密码" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT change-password $DEMO_USERNAME 'NewPassword123#\$'${NC}" - echo "" - - wait_for_confirmation "继续下一步...") - - # 列出OIDC客户端 - log_info "列出OIDC客户端" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT list-oidc-clients${NC}" - echo "" - - wait_for_confirmation "继续下一步...") - - # 备份配置 - log_info "备份当前配置" - echo -e "${BLUE}命令: $AUTOMATION_SCRIPT backup-config${NC}" - echo "" - - log_success "管理操作演示完成" -} - -# 演示4:实际测试(如果Authelia服务可用) -demo_real_test() { - echo -e "${GREEN}=== 演示4:实际测试 ===${NC}" - echo "" - echo "如果Authelia服务正在运行,我们可以进行实际测试" - echo "" - - # 检查服务状态 - if check_authelia_status; then - log_info "Authelia服务可用,可以进行实际测试" - - read -p "是否要进行实际的配置测试?(y/N): " confirm - if [[ "$confirm" =~ ^[Yy]$ ]]; then - log_info "开始实际测试..." - - # 测试生成密钥 - log_info "测试生成OIDC密钥..." - if "$AUTOMATION_SCRIPT" generate-keys; then - log_success "OIDC密钥生成成功" - else - log_error "OIDC密钥生成失败" - fi - - # 测试列出用户 - log_info "测试列出用户..." - "$AUTOMATION_SCRIPT" list-users || log_warning "列出用户失败或无用户" - - # 测试列出OIDC客户端 - log_info "测试列出OIDC客户端..." - "$AUTOMATION_SCRIPT" list-oidc-clients || log_warning "列出OIDC客户端失败或无客户端" - - log_success "实际测试完成" - else - log_info "跳过实际测试" - fi - else - log_warning "Authelia服务不可用,跳过实际测试" - fi -} - -# 显示脚本功能总结 -show_summary() { - echo -e "${GREEN}=== 脚本功能总结 ===${NC}" - echo "" - echo -e "${YELLOW}创建的自动化脚本包括:${NC}" - echo "" - echo -e "${BLUE}1. authelia_automation.sh${NC} - 完整的自动化管理脚本" - echo " 功能:" - echo " - 用户管理:创建、删除、修改密码、列出用户" - echo " - OIDC客户端管理:创建、列出客户端" - echo " - 密钥管理:生成OIDC所需的密钥" - echo " - 配置管理:备份、恢复配置" - echo " - 服务管理:重启Authelia服务" - echo "" - - echo -e "${BLUE}2. quick_setup.sh${NC} - 快速配置脚本" - echo " 功能:" - echo " - 一键完成基础配置" - echo " - 交互式配置模式" - echo " - 默认配置快速设置" - echo " - 自定义参数配置" - echo "" - - echo -e "${YELLOW}使用场景:${NC}" - echo "- 首次部署后的完整配置" - echo "- 添加新用户和应用" - echo "- 日常管理和维护" - echo "- 配置备份和恢复" - echo "" - - echo -e "${YELLOW}文件位置:${NC}" - echo "- 自动化脚本:$AUTOMATION_SCRIPT" - echo "- 快速配置脚本:$QUICK_SETUP_SCRIPT" - echo "- 使用文档:$SCRIPT_DIR/AUTOMATION_README.md" - echo "" -} - -# 主函数 -main() { - echo -e "${GREEN}Authelia 自动化脚本演示${NC}" - echo -e "${BLUE}版本: 1.0.0${NC}" - echo "" - echo "本演示将展示如何使用创建的自动化脚本来管理Authelia" - echo "" - - # 检查脚本是否存在 - if [ ! -f "$AUTOMATION_SCRIPT" ]; then - log_error "自动化脚本不存在: $AUTOMATION_SCRIPT" - exit 1 - fi - - if [ ! -f "$QUICK_SETUP_SCRIPT" ]; then - log_error "快速配置脚本不存在: $QUICK_SETUP_SCRIPT" - exit 1 - fi - - log_success "脚本文件检查通过" - echo "" - - # 运行演示 - demo_quick_setup - echo "" - - demo_step_by_step - echo "" - - demo_management - echo "" - - demo_real_test - echo "" - - show_summary - - echo -e "${GREEN}演示完成!${NC}" - echo "" - echo -e "${YELLOW}下一步:${NC}" - echo "1. 阅读详细文档:$SCRIPT_DIR/AUTOMATION_README.md" - echo "2. 根据实际需求配置Authelia" - echo "3. 测试配置是否正确" - echo "" -} - -# 设置中断处理 -trap 'echo -e "${RED}演示被中断!${NC}"; exit 1' INT - -# 执行主函数 -main "$@" diff --git a/deploy/scripts/7-install-auth-service/install_auth_service.sh b/deploy/scripts/7-install-auth-service/install_auth_service.sh index 634f7dff7575accf5a6d73817e3884d834f87928..dbc7db473edf25831c24604c05fef57dcb4d9f3f 100755 --- a/deploy/scripts/7-install-auth-service/install_auth_service.sh +++ b/deploy/scripts/7-install-auth-service/install_auth_service.sh @@ -21,6 +21,17 @@ CHART_DIR="$( # 全局变量 AUTH_SERVICE="" AUTH_ADDRESS="" +CLIENT_ID="" +CLIENT_SECRET="" +OIDC_HMAC_SECRET="" +SESSION_SECRET="" +STORAGE_ENCRYPTION_KEY="" +JWT_SECRET="" +RSA_PRIVATE_KEY="" +USE_TLS="" +TLS_CERT_PATH="" +TLS_KEY_PATH="" +AUTH_POLICY="" # 新增:认证策略配置 # 打印帮助信息 print_help() { @@ -36,6 +47,44 @@ print_help() { exit 0 } +# 生成随机密钥 +generate_random_secret() { + local length=${1:-32} + openssl rand -base64 $length | tr -d "=+/" | cut -c1-$length +} + +# 生成UUID +generate_uuid() { + # 尝试使用系统的uuidgen命令 + if command -v uuidgen >/dev/null 2>&1; then + uuidgen | tr '[:upper:]' '[:lower:]' + else + # 如果没有uuidgen,使用openssl生成类似UUID的字符串 + printf '%08x-%04x-%04x-%04x-%012x\n' \ + $((RANDOM * RANDOM)) \ + $((RANDOM % 65536)) \ + $((RANDOM % 65536 | 16384)) \ + $((RANDOM % 65536 | 32768)) \ + $((RANDOM * RANDOM * RANDOM)) + fi +} + +# 生成RSA私钥 +generate_rsa_private_key() { + openssl genrsa -out /tmp/rsa_private.key 2048 2>/dev/null + cat /tmp/rsa_private.key + rm -f /tmp/rsa_private.key +} + +# 生成Argon2id哈希密码 +generate_argon2_hash() { + local password="$1" + echo -n "$password" | argon2 "$(openssl rand -base64 16)" -id -t 3 -m 16 -p 4 -l 32 | sed 's/^/\$argon2id\$v=19\$m=65536,t=3,p=4\$/' 2>/dev/null || { + # 如果argon2命令不可用,使用预设的哈希值 + echo "\$argon2id\$v=19\$m=65536,t=3,p=4\$eHA4/xN5ZQB0Pl+Larrf8A\$M7+SLwWQymLJQjUfEf/zin+xAEQ1EW8IcHet6tFcBvo" + } +} + # 获取系统架构 get_architecture() { local arch=$(uname -m) @@ -128,6 +177,202 @@ get_auth_address() { return 0 } +# 选择认证策略 +select_auth_policy() { + if [ -n "$AUTH_POLICY" ]; then + echo -e "${GREEN}使用预设的认证策略:$AUTH_POLICY${NC}" + return 0 + fi + + echo -e "${BLUE}请选择Authelia的认证策略:${NC}" + echo "1) one_factor - 单因子认证(仅用户名密码)" + echo "2) two_factor - 双因子认证(用户名密码 + TOTP/WebAuthn)" + echo -n "请输入选项编号(1-2): " + + local choice + read -r choice + + case $choice in + 1) + AUTH_POLICY="one_factor" + echo -e "${GREEN}选择了单因子认证策略${NC}" + ;; + 2) + AUTH_POLICY="two_factor" + echo -e "${GREEN}选择了双因子认证策略${NC}" + echo -e "${YELLOW}注意:双因子认证需要用户配置TOTP应用或WebAuthn设备${NC}" + ;; + *) + echo -e "${RED}无效的选项,默认使用单因子认证${NC}" + AUTH_POLICY="one_factor" + ;; + esac + + return 0 +} + +# 交互式配置确认 +configure_authelia_settings() { + echo -e "${BLUE}==> 配置 Authelia 参数...${NC}" + + echo -e "${YELLOW}注意:OIDC客户端配置将在EulerCopilot部署时自动创建${NC}" + + # 认证策略配置 + select_auth_policy || exit 1 + + # TLS配置 + echo -e "${BLUE}是否启用TLS?(y/N): ${NC}" + read -p "" enable_tls + if [[ "$enable_tls" =~ ^[Yy]$ ]]; then + USE_TLS="true" + echo -e "${BLUE}请选择TLS证书配置方式:${NC}" + echo "1) 自动生成自签名证书" + echo "2) 使用已有证书" + read -p "请输入选项编号(1-2): " tls_choice + + case $tls_choice in + 1) + echo -e "${GREEN}将自动生成自签名证书${NC}" + ;; + 2) + echo -e "${BLUE}请输入证书文件路径:${NC}" + read -p "证书路径: " TLS_CERT_PATH + echo -e "${BLUE}请输入私钥文件路径:${NC}" + read -p "私钥路径: " TLS_KEY_PATH + + if [[ ! -f "$TLS_CERT_PATH" ]] || [[ ! -f "$TLS_KEY_PATH" ]]; then + echo -e "${RED}错误:证书或私钥文件不存在!${NC}" + return 1 + fi + ;; + *) + echo -e "${RED}无效的选项,将使用自签名证书${NC}" + ;; + esac + else + USE_TLS="false" + echo -e "${YELLOW}将使用HTTP(不推荐用于生产环境)${NC}" + fi + + # 生成其他密钥 + echo -e "${BLUE}正在生成安全密钥...${NC}" + OIDC_HMAC_SECRET=$(generate_random_secret 32) + SESSION_SECRET=$(generate_random_secret 32) + STORAGE_ENCRYPTION_KEY=$(generate_random_secret 32) + JWT_SECRET=$(generate_random_secret 32) + RSA_PRIVATE_KEY=$(generate_rsa_private_key) + + echo -e "${GREEN}配置完成!${NC}" + + # 显示配置摘要 + echo -e "\n${BLUE}==> 配置摘要:${NC}" + echo -e "TLS启用: ${USE_TLS}" + if [[ "$USE_TLS" == "true" ]]; then + if [[ -n "$TLS_CERT_PATH" ]]; then + echo -e "证书路径: ${TLS_CERT_PATH}" + echo -e "私钥路径: ${TLS_KEY_PATH}" + else + echo -e "证书类型: 自签名证书" + fi + fi + echo -e "安全密钥: [已自动生成]" + echo -e "OIDC客户端: [将在EulerCopilot部署时创建]" + + echo -e "\n${BLUE}确认以上配置?(Y/n): ${NC}" + read -p "" confirm + if [[ "$confirm" =~ ^[Nn]$ ]]; then + echo -e "${YELLOW}配置已取消,请重新运行脚本${NC}" + return 1 + fi + + return 0 +} + +# 生成自签名证书 +generate_self_signed_cert() { + local domain="$1" + local cert_dir="/tmp/authelia-certs" + + echo -e "${BLUE}==> 生成自签名证书...${NC}" + + # 创建临时目录 + mkdir -p "$cert_dir" + + # 生成私钥 + openssl genrsa -out "$cert_dir/tls.key" 2048 + + # 生成证书签名请求配置 + cat > "$cert_dir/cert.conf" <> "$cert_dir/cert.conf" + fi + + # 生成证书 + openssl req -new -x509 -key "$cert_dir/tls.key" -out "$cert_dir/tls.crt" -days 365 -config "$cert_dir/cert.conf" -extensions v3_req + + TLS_CERT_PATH="$cert_dir/tls.crt" + TLS_KEY_PATH="$cert_dir/tls.key" + + echo -e "${GREEN}自签名证书生成完成${NC}" + echo -e "证书路径: $TLS_CERT_PATH" + echo -e "私钥路径: $TLS_KEY_PATH" +} + +# 配置TLS证书Secret +configure_tls_secret() { + if [[ "$USE_TLS" == "true" ]]; then + echo -e "${BLUE}==> 配置TLS证书...${NC}" + + # 如果没有指定证书路径,生成自签名证书 + if [[ -z "$TLS_CERT_PATH" ]] || [[ -z "$TLS_KEY_PATH" ]]; then + local domain + domain=$(echo "$AUTH_ADDRESS" | sed -E 's|^https?://([^:/]+).*|\1|') + generate_self_signed_cert "$domain" + fi + + # 删除现有的TLS Secret(如果存在) + kubectl delete secret authelia-tls-secret -n euler-copilot 2>/dev/null || true + + # 创建TLS Secret + kubectl create secret tls authelia-tls-secret \ + --cert="$TLS_CERT_PATH" \ + --key="$TLS_KEY_PATH" \ + -n euler-copilot || { + echo -e "${RED}创建TLS Secret失败!${NC}" + return 1 + } + + echo -e "${GREEN}TLS证书配置完成${NC}" + fi +} + # 清理现有资源 uninstall_auth_services() { echo -e "${BLUE}==> 清理现有鉴权服务资源...${NC}" @@ -188,6 +433,15 @@ uninstall_auth_services() { echo -e "${GREEN}资源清理完成${NC}" } +# 保存认证策略配置 +save_auth_policy_config() { + local config_file="/tmp/authelia_auth_policy.conf" + echo "AUTH_POLICY=$AUTH_POLICY" > "$config_file" + echo "AUTH_SERVICE=$AUTH_SERVICE" >> "$config_file" + echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> "$config_file" + echo -e "${GREEN}认证策略配置已保存到 $config_file${NC}" +} + # 安装AuthHub install_authhub() { local arch="$1" @@ -214,18 +468,61 @@ install_authelia() { local domain domain=$(echo "$AUTH_ADDRESS" | sed -E 's|^https?://([^:/]+).*|\1|') - helm upgrade --install authelia -n euler-copilot "${CHART_DIR}/authelia" \ - --set globals.arch="$arch" \ - --set domain.authelia="$AUTH_ADDRESS" \ - --set authelia.config.session.domain="$domain" || { + # 提取端口号用于NodePort配置 + local port + port=$(echo "$AUTH_ADDRESS" | sed -E 's|^https?://[^:]+:([0-9]+).*|\1|') + # 如果没有端口号,使用默认端口 + if [[ "$port" == "$AUTH_ADDRESS" ]]; then + if [[ "$AUTH_ADDRESS" =~ ^https:// ]]; then + port="443" + else + port="80" + fi + fi + + # 准备Helm参数(提供最小OIDC配置,客户端将在EulerCopilot部署时添加) + local helm_args=( + --set globals.arch="$arch" + --set domain.authelia="$AUTH_ADDRESS" + --set authelia.service.nodePort="$port" + --set authelia.config.session.domain="$domain" + --set authelia.config.session.secret="$SESSION_SECRET" + --set authelia.config.storage.encryption_key="$STORAGE_ENCRYPTION_KEY" + --set authelia.config.access_control.default_policy="$AUTH_POLICY" + --set authelia.config.identity_validation.reset_password.jwt_secret="$JWT_SECRET" + --set authelia.config.identity_providers.oidc.hmac_secret="$OIDC_HMAC_SECRET" + --set authelia.config.identity_providers.oidc.jwks[0].key_id="main-signing-key" + --set authelia.config.identity_providers.oidc.jwks[0].algorithm="RS256" + --set-string authelia.config.identity_providers.oidc.jwks[0].key="$RSA_PRIVATE_KEY" + --set-string authelia.config.identity_providers.oidc.clients[0].client_id="placeholder-client" + --set-string authelia.config.identity_providers.oidc.clients[0].client_name="Placeholder Client" + --set-string authelia.config.identity_providers.oidc.clients[0].client_secret="\$plaintext\$placeholder-secret" + --set authelia.config.identity_providers.oidc.clients[0].public=false + --set authelia.config.identity_providers.oidc.clients[0].authorization_policy="$AUTH_POLICY" + --set authelia.config.identity_providers.oidc.clients[0].redirect_uris[0]="http://placeholder.local/callback" + --set authelia.config.identity_providers.oidc.clients[0].scopes[0]="openid" + --set authelia.config.identity_providers.oidc.clients[0].response_types[0]="code" + --set authelia.config.identity_providers.oidc.clients[0].grant_types[0]="authorization_code" + --set authelia.config.identity_providers.oidc.clients[0].response_modes[0]="query" + ) + + # 如果启用TLS,添加TLS相关配置 + if [[ "$USE_TLS" == "true" ]]; then + helm_args+=( + --set authelia.config.server.tls.enabled=true + --set authelia.config.server.tls.certificate="/etc/authelia/certs/tls.crt" + --set authelia.config.server.tls.key="/etc/authelia/certs/tls.key" + ) + fi + + helm upgrade --install authelia -n euler-copilot "${CHART_DIR}/authelia" "${helm_args[@]}" || { echo -e "${RED}Helm 安装 authelia 失败!${NC}" return 1 } echo -e "${GREEN}Authelia 安装完成!${NC}" echo -e "${GREEN}登录地址: ${AUTH_ADDRESS}${NC}" - echo -e "${GREEN}默认管理员账号: admin/admin123${NC}" - echo -e "${GREEN}默认用户账号: user/user123${NC}" + echo -e "${GREEN}默认用户账号: openEuler/openEuler12#\$${NC}" echo -e "${YELLOW}重要提示:生产环境请修改默认密码和密钥!${NC}" } @@ -253,38 +550,40 @@ helm_install() { esac } -check_pods_status() { - echo -e "${BLUE}==> 等待初始化就绪(30秒)...${NC}" >&2 +check_auth_pods_status() { + echo -e "${BLUE}==> 等待认证服务初始化就绪(30秒)...${NC}" >&2 sleep 30 local timeout=300 local start_time=$(date +%s) + local auth_service_lower=$(echo "$AUTH_SERVICE" | tr '[:upper:]' '[:lower:]') - echo -e "${BLUE}开始监控Pod状态(总超时时间300秒)...${NC}" >&2 + echo -e "${BLUE}开始监控${AUTH_SERVICE}服务Pod状态(总超时时间300秒)...${NC}" >&2 while true; do local current_time=$(date +%s) local elapsed=$((current_time - start_time)) if [ $elapsed -gt $timeout ]; then - echo -e "${YELLOW}警告:部署超时!请检查以下资源:${NC}" >&2 - kubectl get pods -n euler-copilot -o wide + echo -e "${YELLOW}警告:认证服务部署超时!请检查以下资源:${NC}" >&2 + kubectl get pods -n euler-copilot -l "app.kubernetes.io/name=${auth_service_lower}" -o wide echo -e "\n${YELLOW}建议检查:${NC}" - echo "1. 查看未就绪Pod的日志: kubectl logs -n euler-copilot " + echo "1. 查看认证服务Pod的日志: kubectl logs -n euler-copilot -l app.kubernetes.io/name=${auth_service_lower}" echo "2. 检查PVC状态: kubectl get pvc -n euler-copilot" - echo "3. 检查Service状态: kubectl get svc -n euler-copilot" + echo "3. 检查Service状态: kubectl get svc -n euler-copilot -l app.kubernetes.io/name=${auth_service_lower}" return 1 fi - local not_running=$(kubectl get pods -n euler-copilot -o jsonpath='{range .items[*]}{.metadata.name} {.status.phase} {.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}' \ + # 只检查认证服务相关的Pod + local not_running=$(kubectl get pods -n euler-copilot -l "app.kubernetes.io/name=${auth_service_lower}" -o jsonpath='{range .items[*]}{.metadata.name} {.status.phase} {.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}' \ | awk '$2 != "Running" || $3 != "True" {print $1 " " $2}') if [ -z "$not_running" ]; then - echo -e "${GREEN}所有Pod已正常运行!${NC}" >&2 - kubectl get pods -n euler-copilot -o wide + echo -e "${GREEN}认证服务Pod已正常运行!${NC}" >&2 + kubectl get pods -n euler-copilot -l "app.kubernetes.io/name=${auth_service_lower}" -o wide return 0 else - echo "等待Pod就绪(已等待 ${elapsed} 秒)..." + echo "等待认证服务Pod就绪(已等待 ${elapsed} 秒)..." echo "当前未就绪Pod:" echo "$not_running" | awk '{print " - " $1 " (" $2 ")"}' sleep 10 @@ -292,6 +591,35 @@ check_pods_status() { done } +# 更新euler-copilot配置 +update_euler_copilot_config() { + echo -e "${BLUE}==> 自动更新euler-copilot配置...${NC}" + + local update_script="${SCRIPT_PATH%/*}/../9-other-script/update_auth_config.sh" + + if [[ ! -f "$update_script" ]]; then + echo -e "${YELLOW}警告:找不到配置更新脚本,请手动更新euler-copilot配置${NC}" + return 0 + fi + + # 构建更新命令参数 + local update_args=( + --auth-service "$AUTH_SERVICE" + --auth-address "$AUTH_ADDRESS" + ) + + # 注意:OIDC客户端配置将在EulerCopilot部署时自动处理 + + # 执行配置更新 + if "$update_script" "${update_args[@]}"; then + echo -e "${GREEN}euler-copilot配置更新成功${NC}" + else + echo -e "${YELLOW}警告:配置更新失败,请手动更新配置${NC}" + echo -e "手动更新命令:" + echo -e "${BLUE}$update_script ${update_args[*]}${NC}" + fi +} + deploy() { local arch arch=$(get_architecture) || exit 1 @@ -304,16 +632,56 @@ deploy() { # 获取鉴权服务地址 get_auth_address || exit 1 + # 如果是Authelia,进行详细配置 + if [[ "$AUTH_SERVICE" == "authelia" ]]; then + configure_authelia_settings || exit 1 + configure_tls_secret || exit 1 + fi + helm_install "$arch" || exit 1 - check_pods_status || { - echo -e "${RED}部署失败:Pod状态检查未通过!${NC}" + check_auth_pods_status || { + echo -e "${RED}部署失败:认证服务Pod状态检查未通过!${NC}" exit 1 } + # 保存认证策略配置 + save_auth_policy_config + + # 自动更新euler-copilot配置 + update_euler_copilot_config + + # 显示部署结果 echo -e "\n${GREEN}=========================" echo -e "鉴权服务 ($AUTH_SERVICE) 部署完成!" echo -e "查看pod状态:kubectl get pod -n euler-copilot" echo -e "服务访问地址: $AUTH_ADDRESS" + + if [[ "$AUTH_SERVICE" == "authelia" ]]; then + echo -e "\n${BLUE}Authelia服务信息:${NC}" + echo -e "Authorization URL: ${AUTH_ADDRESS}/api/oidc/authorization" + echo -e "Token URL: ${AUTH_ADDRESS}/api/oidc/token" + echo -e "User Info URL: ${AUTH_ADDRESS}/api/oidc/userinfo" + echo -e "\n${YELLOW}注意:${NC}" + echo -e "- 当前配置了占位符OIDC客户端(redirect_uri指向无效地址,无法实际使用)" + echo -e "- 真实的OIDC客户端配置将在部署EulerCopilot时自动创建和替换" + + if [[ "$USE_TLS" == "true" ]]; then + echo -e "\n${YELLOW}TLS证书信息:${NC}" + echo -e "TLS已启用,请确保客户端信任证书" + if [[ -n "$TLS_CERT_PATH" ]] && [[ "$TLS_CERT_PATH" == "/tmp/authelia-certs/tls.crt" ]]; then + echo -e "自签名证书路径: $TLS_CERT_PATH" + echo -e "建议将证书添加到客户端信任列表" + fi + fi + fi + + echo -e "\n${YELLOW}重要提示:${NC}" + echo -e "1. 认证服务基础配置已更新到euler-copilot配置中" + echo -e "2. OIDC客户端将在部署EulerCopilot时自动创建和配置" + echo -e "3. 部署EulerCopilot时请运行:" + echo -e " ${BLUE}${SCRIPT_PATH%/*}/../8-install-EulerCopilot/install_eulercopilot.sh${NC}" + echo -e "4. 可使用以下命令验证配置:" + echo -e " ${BLUE}${SCRIPT_PATH%/*}/../9-other-script/update_auth_config.sh --validate${NC}" echo -e "=========================${NC}" } diff --git a/deploy/scripts/7-install-auth-service/install_authelia.sh b/deploy/scripts/7-install-auth-service/install_authelia.sh deleted file mode 100755 index 4526c4b06c80d1be3ce8e7d0e7144d06b2a07bf3..0000000000000000000000000000000000000000 --- a/deploy/scripts/7-install-auth-service/install_authelia.sh +++ /dev/null @@ -1,520 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -RED='\033[31m' -GREEN='\033[32m' -YELLOW='\033[33m' -BLUE='\033[34m' -NC='\033[0m' - -SCRIPT_PATH="$( - cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 - pwd -)/$(basename "${BASH_SOURCE[0]}")" - -CHART_DIR="$( - canonical_path=$(readlink -f "$SCRIPT_PATH" 2>/dev/null || echo "$SCRIPT_PATH") - dirname "$(dirname "$(dirname "$canonical_path")")" -)/chart" - -# 全局变量 -AUTH_ADDRESS="" -ENABLE_TLS=false -TLS_MODE="" -CERT_PATH="" -KEY_PATH="" - -# 打印帮助信息 -print_help() { - echo -e "${GREEN}用法: $0 [选项]" - echo -e "选项:" - echo -e " --help 显示帮助信息" - echo -e " --address <地址> 指定Authelia服务的访问地址" - echo -e " --enable-tls 启用TLS/HTTPS支持" - echo -e " --cert <证书路径> 指定TLS证书文件路径" - echo -e " --key <私钥路径> 指定TLS私钥文件路径" - echo -e "" - echo -e "示例:" - echo -e " # HTTP模式(测试环境)" - echo -e " $0 --address http://127.0.0.1:30091" - echo -e "" - echo -e " # HTTPS Ingress模式(生产环境推荐)" - echo -e " $0 --address https://authelia.yourdomain.com" - echo -e "" - echo -e "注意:此脚本只安装基础的Authelia服务,用户和OIDC配置需要在部署完成后单独配置${NC}" - exit 0 -} - -# 获取系统架构 -get_architecture() { - local arch=$(uname -m) - case "$arch" in - x86_64) - arch="x86" - ;; - aarch64) - arch="arm" - ;; - *) - echo -e "${RED}错误:不支持的架构 $arch${NC}" >&2 - return 1 - ;; - esac - echo -e "${GREEN}检测到系统架构:$(uname -m)${NC}" >&2 - echo "$arch" -} - -# 生成自签名证书 -generate_self_signed_cert() { - local domain="$1" - local cert_dir="/tmp/authelia-certs" - - echo -e "${BLUE}==> 生成自签名证书...${NC}" - - # 创建证书目录 - mkdir -p "$cert_dir" - - # 生成私钥 - openssl genrsa -out "$cert_dir/tls.key" 2048 - - # 生成证书签名请求配置 - cat > "$cert_dir/cert.conf" </dev/null 2>&1; then - echo -e "${RED}错误:未找到 'authelia-tls' Secret${NC}" - echo -e "${YELLOW}请使用以下命令创建:${NC}" - echo "kubectl create secret tls authelia-tls --cert= --key= -n euler-copilot" - exit 1 - fi - - echo -e "${GREEN}TLS Secret验证成功${NC}" -} - -# 配置现有证书 -configure_existing_cert() { - echo -e "${BLUE}现有证书配置:${NC}" - - while true; do - read -p "请输入证书文件路径: " cert_file - if [ -f "$cert_file" ]; then - CERT_PATH="$cert_file" - echo -e "${GREEN}证书文件:$CERT_PATH${NC}" - break - else - echo -e "${RED}证书文件不存在,请重新输入${NC}" - fi - done - - while true; do - read -p "请输入私钥文件路径: " key_file - if [ -f "$key_file" ]; then - KEY_PATH="$key_file" - echo -e "${GREEN}私钥文件:$KEY_PATH${NC}" - break - else - echo -e "${RED}私钥文件不存在,请重新输入${NC}" - fi - done -} - -# 创建TLS Secret -create_tls_secret() { - if [ "$ENABLE_TLS" = true ] && [ -n "$CERT_PATH" ] && [ -n "$KEY_PATH" ]; then - echo -e "${BLUE}==> 创建TLS Secret...${NC}" - - # 删除现有的secret(如果存在) - kubectl delete secret authelia-tls-secret -n euler-copilot --ignore-not-found=true - - # 创建新的TLS secret - kubectl create secret tls authelia-tls-secret \ - --cert="$CERT_PATH" \ - --key="$KEY_PATH" \ - -n euler-copilot || { - echo -e "${RED}创建TLS Secret失败!${NC}" - return 1 - } - - echo -e "${GREEN}TLS Secret创建成功${NC}" - fi -} - -# 安装基础Authelia -install_authelia() { - local arch="$1" - echo -e "${BLUE}==> 安装基础 Authelia 服务...${NC}" - - # 提取域名/IP用于会话配置 - local domain - domain=$(echo "$AUTH_ADDRESS" | sed -E 's|^https?://([^:/]+).*|\1|') - - # 生成随机密钥 - local session_secret=$(openssl rand -base64 32) - local storage_key=$(openssl rand -base64 32) - local jwt_secret=$(openssl rand -base64 32) - - # 添加官方Authelia仓库(如果尚未添加) - if ! helm repo list | grep -q "^authelia"; then - echo -e "${BLUE}添加Authelia官方仓库...${NC}" - helm repo add authelia https://charts.authelia.com - helm repo update - fi - - # 创建包含基础密钥的Secret - echo -e "${BLUE}创建Authelia基础Secret...${NC}" - kubectl create secret generic authelia-secrets -n euler-copilot \ - --from-literal=identity_validation.reset_password.jwt.hmac.key="$jwt_secret" \ - --from-literal=session.encryption.key="$session_secret" \ - --from-literal=storage.encryption.key="$storage_key" \ - --dry-run=client -o yaml | kubectl apply -f - - - # 从AUTH_ADDRESS中提取端口号用于NodePort - local node_port - if [[ "$AUTH_ADDRESS" =~ :([0-9]+)(/|$) ]]; then - node_port="${BASH_REMATCH[1]}" - else - # 如果没有指定端口,使用默认的NodePort - node_port="30091" - fi - - # 创建基础配置的values文件 - local values_file="/tmp/authelia-basic-values.yaml" - cat > "$values_file" <> "$values_file" <&2 - exit 1 - fi - ;; - --enable-tls) - ENABLE_TLS=true - shift - ;; - --cert) - if [ -n "$2" ]; then - CERT_PATH="$2" - shift 2 - else - echo -e "${RED}错误:--cert 需要提供一个参数${NC}" >&2 - exit 1 - fi - ;; - --key) - if [ -n "$2" ]; then - KEY_PATH="$2" - shift 2 - else - echo -e "${RED}错误:--key 需要提供一个参数${NC}" >&2 - exit 1 - fi - ;; - *) - echo -e "${RED}未知参数: $1${NC}" >&2 - exit 1 - ;; - esac - done -} - -# 主部署函数 -deploy() { - local arch - arch=$(get_architecture) || exit 1 - - # 获取Authelia服务地址 - get_auth_address || exit 1 - - # 配置TLS - configure_tls || exit 1 - - # 创建TLS Secret(如果需要) - create_tls_secret || exit 1 - - # 安装基础Authelia - install_authelia "$arch" || exit 1 - - echo -e "\n${GREEN}=========================" - echo -e "基础 Authelia 部署完成!" - echo -e "服务访问地址: $AUTH_ADDRESS" - echo -e "=========================${NC}" -} - -main() { - parse_args "$@" - deploy -} - -trap 'echo -e "${RED}操作被中断!${NC}"; exit 1' INT -main "$@" diff --git a/deploy/scripts/7-install-auth-service/install_authhub.sh b/deploy/scripts/7-install-auth-service/install_authhub.sh deleted file mode 100755 index 79e75f6e1ca24f646da58ec5c1cb728b99c2bb83..0000000000000000000000000000000000000000 --- a/deploy/scripts/7-install-auth-service/install_authhub.sh +++ /dev/null @@ -1,240 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -RED='\033[31m' -GREEN='\033[32m' -YELLOW='\033[33m' -BLUE='\033[34m' -NC='\033[0m' - -SCRIPT_PATH="$( - cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 - pwd -)/$(basename "${BASH_SOURCE[0]}")" - -CHART_DIR="$( - canonical_path=$(readlink -f "$SCRIPT_PATH" 2>/dev/null || echo "$SCRIPT_PATH") - dirname "$(dirname "$(dirname "$canonical_path")")" -)/chart" - -# 打印帮助信息 -print_help() { - echo -e "${GREEN}用法: $0 [选项]" - echo -e "选项:" - echo -e " --help 显示帮助信息" - echo -e " --authhub_address <地址> 指定Authhub的访问地址(例如:http://myhost:30081)" - echo -e "" - echo -e "示例:" - echo -e " $0 --authhub_address http://myhost:30081${NC}" - exit 0 -} - -# 获取系统架构 -get_architecture() { - local arch=$(uname -m) - case "$arch" in - x86_64) - arch="x86" - ;; - aarch64) - arch="arm" - ;; - *) - echo -e "${RED}错误:不支持的架构 $arch${NC}" >&2 - return 1 - ;; - esac - echo -e "${GREEN}检测到系统架构:$(uname -m)${NC}" >&2 - echo "$arch" -} - -create_namespace() { - echo -e "${BLUE}==> 检查命名空间 euler-copilot...${NC}" - if ! kubectl get namespace euler-copilot &> /dev/null; then - kubectl create namespace euler-copilot || { - echo -e "${RED}命名空间创建失败!${NC}" - return 1 - } - echo -e "${GREEN}命名空间创建成功${NC}" - else - echo -e "${YELLOW}命名空间已存在,跳过创建${NC}" - fi -} - -uninstall_authhub() { - echo -e "${BLUE}==> 清理现有资源...${NC}" - - local RELEASES - RELEASES=$(helm list -n euler-copilot --short | grep authhub || true) - - if [ -n "$RELEASES" ]; then - echo -e "${YELLOW}找到以下Helm Release,开始清理...${NC}" - for release in $RELEASES; do - echo -e "${BLUE}正在删除Helm Release: ${release}${NC}" - helm uninstall "$release" -n euler-copilot || echo -e "${RED}删除Helm Release失败,继续执行...${NC}" - done - else - echo -e "${YELLOW}未找到需要清理的Helm Release${NC}" - fi - - local pvc_name - pvc_name=$(kubectl get pvc -n euler-copilot | grep 'mysql-pvc' 2>/dev/null || true) - - if [ -n "$pvc_name" ]; then - echo -e "${YELLOW}找到以下PVC,开始清理...${NC}" - kubectl delete pvc mysql-pvc -n euler-copilot --force --grace-period=0 || echo -e "${RED}PVC删除失败,继续执行...${NC}" - else - echo -e "${YELLOW}未找到需要清理的PVC${NC}" - fi - - # 新增:删除 authhub-secret - local authhub_secret="authhub-secret" - if kubectl get secret "$authhub_secret" -n euler-copilot &>/dev/null; then - echo -e "${YELLOW}找到Secret: ${authhub_secret},开始清理...${NC}" - if ! kubectl delete secret "$authhub_secret" -n euler-copilot; then - echo -e "${RED}错误:删除Secret ${authhub_secret} 失败!${NC}" >&2 - return 1 - fi - else - echo -e "${YELLOW}未找到需要清理的Secret: ${authhub_secret}${NC}" - fi - - echo -e "${GREEN}资源清理完成${NC}" -} - -get_authhub_address() { - local default_address="http://127.0.0.1:30081" - - echo -e "${BLUE}请输入 Authhub 的访问地址(IP或域名,直接回车使用默认值 ${default_address}):${NC}" - read -p "Authhub 地址: " authhub_address - - # 处理空输入情况 - if [[ -z "$authhub_address" ]]; then - authhub_address="$default_address" - echo -e "${GREEN}使用默认地址:${authhub_address}${NC}" - else - echo -e "${GREEN}输入地址:${authhub_address}${NC}" - fi - - return 0 -} - -helm_install() { - local arch="$1" - echo -e "${BLUE}==> 进入部署目录...${NC}" - [ ! -d "${CHART_DIR}" ] && { - echo -e "${RED}错误:部署目录不存在 ${CHART_DIR} ${NC}" - return 1 - } - cd "${CHART_DIR}" - - echo -e "${BLUE}正在安装 authhub...${NC}" - helm upgrade --install authhub -n euler-copilot ./authhub \ - --set globals.arch="$arch" \ - --set domain.authhub="${authhub_address}" || { - echo -e "${RED}Helm 安装 authhub 失败!${NC}" - return 1 - } -} - -check_pods_status() { - echo -e "${BLUE}==> 等待初始化就绪(30秒)...${NC}" >&2 - sleep 30 - - local timeout=300 - local start_time=$(date +%s) - - echo -e "${BLUE}开始监控Pod状态(总超时时间300秒)...${NC}" >&2 - - while true; do - local current_time=$(date +%s) - local elapsed=$((current_time - start_time)) - - if [ $elapsed -gt $timeout ]; then - echo -e "${YELLOW}警告:部署超时!请检查以下资源:${NC}" >&2 - kubectl get pods -n euler-copilot -o wide - echo -e "\n${YELLOW}建议检查:${NC}" - echo "1. 查看未就绪Pod的日志: kubectl logs -n euler-copilot " - echo "2. 检查PVC状态: kubectl get pvc -n euler-copilot" - echo "3. 检查Service状态: kubectl get svc -n euler-copilot" - return 1 - fi - - local not_running=$(kubectl get pods -n euler-copilot -o jsonpath='{range .items[*]}{.metadata.name} {.status.phase} {.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}' \ - | awk '$2 != "Running" || $3 != "True" {print $1 " " $2}') - - if [ -z "$not_running" ]; then - echo -e "${GREEN}所有Pod已正常运行!${NC}" >&2 - kubectl get pods -n euler-copilot -o wide - return 0 - else - echo "等待Pod就绪(已等待 ${elapsed} 秒)..." - echo "当前未就绪Pod:" - echo "$not_running" | awk '{print " - " $1 " (" $2 ")"}' - sleep 10 - fi - done -} - -deploy() { - local arch - arch=$(get_architecture) || exit 1 - create_namespace || exit 1 - uninstall_authhub || exit 1 - - # 如果未通过参数提供地址,则提示用户输入 - if [ -z "$authhub_address" ]; then - echo -e "${YELLOW}未提供 --authhub_address 参数,需要手动输入地址${NC}" - get_authhub_address || exit 1 - else - echo -e "${GREEN}使用参数指定的Authhub地址:$authhub_address${NC}" - fi - - helm_install "$arch" || exit 1 - check_pods_status || { - echo -e "${RED}部署失败:Pod状态检查未通过!${NC}" - exit 1 - } - - echo -e "\n${GREEN}=========================" - echo -e "Authhub 部署完成!" - echo -e "查看pod状态:kubectl get pod -n euler-copilot" - echo -e "Authhub登录地址为: $authhub_address" - echo -e "默认账号密码: openEuler/changeme" - echo -e "=========================${NC}" -} - -# 解析命令行参数 -parse_args() { - while [[ $# -gt 0 ]]; do - case "$1" in - --help) - print_help - exit 0 - ;; - --authhub_address) - if [ -n "$2" ]; then - authhub_address="$2" - shift 2 - else - echo -e "${RED}错误:--authhub_address 需要提供一个参数${NC}" >&2 - exit 1 - fi - ;; - *) - echo -e "${RED}未知参数: $1${NC}" >&2 - print_help - exit 1 - ;; - esac - done -} - -main() { - parse_args "$@" - deploy -} - -trap 'echo -e "${RED}操作被中断!${NC}"; exit 1' INT -main "$@" diff --git a/deploy/scripts/7-install-auth-service/quick_setup.sh b/deploy/scripts/7-install-auth-service/quick_setup.sh deleted file mode 100755 index 792625c49261ed0e053ad4ca28c892be64c8286e..0000000000000000000000000000000000000000 --- a/deploy/scripts/7-install-auth-service/quick_setup.sh +++ /dev/null @@ -1,351 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -# 颜色定义 -RED='\033[31m' -GREEN='\033[32m' -YELLOW='\033[33m' -BLUE='\033[34m' -NC='\033[0m' - -# 脚本目录 -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -AUTOMATION_SCRIPT="$SCRIPT_DIR/authelia_automation.sh" - -# 默认配置 -DEFAULT_USERNAME="openEuler" -DEFAULT_PASSWORD="openEuler12#\$" -DEFAULT_EMAIL="openEuler@example.com" -DEFAULT_CLIENT_ID="euler-copilot" -DEFAULT_CLIENT_NAME="Euler Copilot" - -# 打印帮助信息 -print_help() { - echo -e "${GREEN}Authelia 快速配置脚本${NC}" - echo -e "${GREEN}用法: $0 [选项]${NC}" - echo "" - echo -e "${BLUE}选项:${NC}" - echo -e " --help 显示帮助信息" - echo -e " --interactive 交互式配置" - echo -e " --default 使用默认配置快速设置" - echo -e " --username <用户名> 指定用户名 (默认: $DEFAULT_USERNAME)" - echo -e " --password <密码> 指定密码 (默认: $DEFAULT_PASSWORD)" - echo -e " --email <邮箱> 指定邮箱 (默认: $DEFAULT_EMAIL)" - echo -e " --client-id <客户端ID> 指定OIDC客户端ID (默认: $DEFAULT_CLIENT_ID)" - echo -e " --client-name <客户端名称> 指定OIDC客户端名称 (默认: $DEFAULT_CLIENT_NAME)" - echo -e " --redirect-uri <重定向URI> 指定OIDC重定向URI" - echo "" - echo -e "${BLUE}示例:${NC}" - echo -e " # 使用默认配置快速设置" - echo -e " $0 --default --redirect-uri 'http://127.0.0.1:30080/auth/callback'" - echo "" - echo -e " # 交互式配置" - echo -e " $0 --interactive" - echo "" - echo -e " # 自定义配置" - echo -e " $0 --username myuser --password 'mypass123' --email myuser@example.com \\" - echo -e " --client-id my-client --redirect-uri 'http://localhost:3000/callback'" - echo "" -} - -# 日志函数 -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# 检查依赖 -check_dependencies() { - log_info "检查依赖..." - - # 检查kubectl - if ! command -v kubectl &> /dev/null; then - log_error "kubectl 未安装或不在PATH中" - return 1 - fi - - # 检查自动化脚本 - if [ ! -f "$AUTOMATION_SCRIPT" ]; then - log_error "自动化脚本不存在: $AUTOMATION_SCRIPT" - return 1 - fi - - # 检查脚本权限 - if [ ! -x "$AUTOMATION_SCRIPT" ]; then - log_info "设置自动化脚本执行权限" - chmod +x "$AUTOMATION_SCRIPT" - fi - - # 检查Authelia服务状态 - if ! kubectl get deployment authelia -n euler-copilot &> /dev/null; then - log_error "Authelia服务未部署,请先运行安装脚本" - return 1 - fi - - log_success "依赖检查通过" -} - -# 获取用户输入 -get_user_input() { - echo -e "${BLUE}=== Authelia 交互式配置 ===${NC}" - echo "" - - # 用户配置 - echo -e "${YELLOW}用户配置:${NC}" - read -p "用户名 [$DEFAULT_USERNAME]: " USERNAME - USERNAME=${USERNAME:-$DEFAULT_USERNAME} - - read -s -p "密码 [默认密码]: " PASSWORD - echo - PASSWORD=${PASSWORD:-$DEFAULT_PASSWORD} - - read -p "邮箱 [$DEFAULT_EMAIL]: " EMAIL - EMAIL=${EMAIL:-$DEFAULT_EMAIL} - - # 固定用户组为admins - GROUPS="admins" - - echo "" - - # OIDC配置 - echo -e "${YELLOW}OIDC客户端配置:${NC}" - read -p "客户端ID [$DEFAULT_CLIENT_ID]: " CLIENT_ID - CLIENT_ID=${CLIENT_ID:-$DEFAULT_CLIENT_ID} - - read -p "客户端名称 [$DEFAULT_CLIENT_NAME]: " CLIENT_NAME - CLIENT_NAME=${CLIENT_NAME:-$DEFAULT_CLIENT_NAME} - - while [ -z "$REDIRECT_URI" ]; do - read -p "重定向URI (必填): " REDIRECT_URI - if [ -z "$REDIRECT_URI" ]; then - log_error "重定向URI不能为空" - fi - done - - echo "" - - # 确认配置 - echo -e "${BLUE}=== 配置确认 ===${NC}" - echo -e "${YELLOW}用户信息:${NC}" - echo " 用户名: $USERNAME" - echo " 邮箱: $EMAIL" - echo "" - echo -e "${YELLOW}OIDC客户端:${NC}" - echo " 客户端ID: $CLIENT_ID" - echo " 客户端名称: $CLIENT_NAME" - echo " 重定向URI: $REDIRECT_URI" - echo "" - - read -p "确认以上配置?(y/N): " confirm - if [[ ! "$confirm" =~ ^[Yy]$ ]]; then - log_info "配置已取消" - exit 0 - fi -} - -# 执行配置 -execute_setup() { - log_info "开始执行Authelia配置..." - - # 1. 生成OIDC密钥 - log_info "步骤 1/4: 生成OIDC密钥" - if ! "$AUTOMATION_SCRIPT" generate-keys; then - log_error "OIDC密钥生成失败" - return 1 - fi - - # 2. 创建用户 - log_info "步骤 2/4: 创建用户 $USERNAME" - if ! "$AUTOMATION_SCRIPT" create-user "$USERNAME" "$PASSWORD" "$EMAIL" "$GROUPS"; then - log_warning "用户创建失败,可能用户已存在" - fi - - # 3. 创建OIDC客户端 - log_info "步骤 3/4: 创建OIDC客户端 $CLIENT_ID" - if ! "$AUTOMATION_SCRIPT" create-oidc-client "$CLIENT_ID" "$CLIENT_NAME" "$REDIRECT_URI"; then - log_error "OIDC客户端创建失败" - return 1 - fi - - # 4. 重启服务 - log_info "步骤 4/4: 重启Authelia服务" - if ! "$AUTOMATION_SCRIPT" restart-service; then - log_error "服务重启失败" - return 1 - fi - - log_success "Authelia配置完成!" -} - -# 显示配置结果 -show_results() { - echo "" - echo -e "${GREEN}=== 配置完成 ===${NC}" - echo "" - echo -e "${YELLOW}登录信息:${NC}" - echo " 用户名: $USERNAME" - echo " 密码: $PASSWORD" - echo " 邮箱: $EMAIL" - echo "" - echo -e "${YELLOW}OIDC客户端信息:${NC}" - echo " 客户端ID: $CLIENT_ID" - echo " 客户端名称: $CLIENT_NAME" - echo " 重定向URI: $REDIRECT_URI" - - # 显示客户端密钥(如果存在) - if [ -f "/tmp/authelia_oidc_clients" ]; then - echo "" - echo -e "${YELLOW}客户端密钥信息:${NC}" - tail -n 10 /tmp/authelia_oidc_clients | grep -E "(Client ID|Client Secret)" | tail -n 2 - fi - - echo "" - echo -e "${BLUE}后续步骤:${NC}" - echo "1. 验证Authelia服务状态: kubectl get pods -n euler-copilot" - echo "2. 检查服务日志: kubectl logs -n euler-copilot deployment/authelia" - echo "3. 测试OIDC配置: curl -s http:///.well-known/openid_configuration" - echo "" - echo -e "${YELLOW}管理命令:${NC}" - echo "- 列出用户: $AUTOMATION_SCRIPT list-users" - echo "- 列出OIDC客户端: $AUTOMATION_SCRIPT list-oidc-clients" - echo "- 备份配置: $AUTOMATION_SCRIPT backup-config" - echo "" -} - -# 默认配置设置 -setup_default_config() { - if [ -z "$REDIRECT_URI" ]; then - log_error "使用默认配置时必须指定 --redirect-uri" - return 1 - fi - - USERNAME="$DEFAULT_USERNAME" - PASSWORD="$DEFAULT_PASSWORD" - EMAIL="$DEFAULT_EMAIL" - GROUPS="admins" - CLIENT_ID="$DEFAULT_CLIENT_ID" - CLIENT_NAME="$DEFAULT_CLIENT_NAME" - - log_info "使用默认配置:" - log_info " 用户名: $USERNAME" - log_info " 邮箱: $EMAIL" - log_info " 客户端ID: $CLIENT_ID" - log_info " 重定向URI: $REDIRECT_URI" -} - -# 解析命令行参数 -parse_args() { - local interactive=false - local use_default=false - - while [[ $# -gt 0 ]]; do - case "$1" in - --help) - print_help - exit 0 - ;; - --interactive) - interactive=true - shift - ;; - --default) - use_default=true - shift - ;; - --username) - USERNAME="$2" - shift 2 - ;; - --password) - PASSWORD="$2" - shift 2 - ;; - --email) - EMAIL="$2" - shift 2 - ;; - --client-id) - CLIENT_ID="$2" - shift 2 - ;; - --client-name) - CLIENT_NAME="$2" - shift 2 - ;; - --redirect-uri) - REDIRECT_URI="$2" - shift 2 - ;; - *) - log_error "未知参数: $1" - print_help - exit 1 - ;; - esac - done - - # 处理配置模式 - if [ "$interactive" = true ]; then - get_user_input - elif [ "$use_default" = true ]; then - setup_default_config - else - # 检查必需参数 - if [ -z "$REDIRECT_URI" ]; then - log_error "必须指定重定向URI或使用交互模式" - print_help - exit 1 - fi - - # 使用提供的参数或默认值 - USERNAME="${USERNAME:-$DEFAULT_USERNAME}" - PASSWORD="${PASSWORD:-$DEFAULT_PASSWORD}" - EMAIL="${EMAIL:-$DEFAULT_EMAIL}" - GROUPS="${GROUPS:-admins}" - CLIENT_ID="${CLIENT_ID:-$DEFAULT_CLIENT_ID}" - CLIENT_NAME="${CLIENT_NAME:-$DEFAULT_CLIENT_NAME}" - fi -} - -# 主函数 -main() { - echo -e "${GREEN}Authelia 快速配置脚本${NC}" - echo -e "${BLUE}版本: 1.0.0${NC}" - echo "" - - # 检查依赖 - check_dependencies || exit 1 - - # 解析参数 - parse_args "$@" - - # 执行配置 - execute_setup || exit 1 - - # 显示结果 - show_results -} - -# 设置中断处理 -trap 'echo -e "${RED}操作被中断!${NC}"; exit 1' INT - -# 如果没有参数,显示帮助 -if [ $# -eq 0 ]; then - print_help - exit 0 -fi - -# 执行主函数 -main "$@" diff --git a/deploy/scripts/8-install-EulerCopilot/install_eulercopilot.sh b/deploy/scripts/8-install-EulerCopilot/install_eulercopilot.sh index 866b27bb44272025c3957aabaa6b371850dfa74e..06f6c733539f4576c127b486ffe66b6708e00d88 100755 --- a/deploy/scripts/8-install-EulerCopilot/install_eulercopilot.sh +++ b/deploy/scripts/8-install-EulerCopilot/install_eulercopilot.sh @@ -18,6 +18,10 @@ client_secret="" eulercopilot_address="" auth_service_type="" auth_service_address="" +auth_policy="" # 新增:检测到的认证策略 +use_tls="" +tls_cert_path="" +tls_key_path="" SCRIPT_PATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 @@ -35,9 +39,15 @@ show_help() { echo -e "选项:" echo -e " --help 显示此帮助信息" echo -e " --eulercopilot_address 指定EulerCopilot前端访问URL" + echo -e " --enable-tls 启用TLS证书支持" + echo -e " --tls-cert 指定TLS证书文件路径" + echo -e " --tls-key 指定TLS私钥文件路径" + echo -e " --generate-cert 为指定域名生成自签名证书" echo -e "" echo -e "示例:" - echo -e " $0 --eulercopilot_address http://myhost:30080${NC}" + echo -e " $0 --eulercopilot_address http://myhost:30080" + echo -e " $0 --eulercopilot_address https://myhost:30443 --enable-tls --generate-cert myhost" + echo -e " $0 --eulercopilot_address https://myhost:30443 --enable-tls --tls-cert /path/to/cert.crt --tls-key /path/to/key.key${NC}" echo -e "" echo -e "${YELLOW}注意: 鉴权服务将自动检测并提供选择${NC}" exit 0 @@ -59,6 +69,37 @@ parse_arguments() { exit 1 fi ;; + --enable-tls) + use_tls="true" + ;; + --tls-cert) + if [ -n "$2" ]; then + tls_cert_path="$2" + shift + else + echo -e "${RED}错误: --tls-cert 需要提供证书文件路径${NC}" >&2 + exit 1 + fi + ;; + --tls-key) + if [ -n "$2" ]; then + tls_key_path="$2" + shift + else + echo -e "${RED}错误: --tls-key 需要提供私钥文件路径${NC}" >&2 + exit 1 + fi + ;; + --generate-cert) + if [ -n "$2" ]; then + generate_cert_domain="$2" + use_tls="true" + shift + else + echo -e "${RED}错误: --generate-cert 需要提供域名${NC}" >&2 + exit 1 + fi + ;; *) echo -e "${RED}未知选项: $1${NC}" >&2 show_help @@ -69,6 +110,143 @@ parse_arguments() { done } +# TLS证书管理函数 +manage_tls_certificates() { + local tls_cert_manager="${DEPLOY_DIR}/scripts/9-other-script/tls_cert_manager.sh" + + # 检查TLS证书管理工具是否存在 + if [ ! -f "$tls_cert_manager" ]; then + echo -e "${RED}错误: TLS证书管理工具不存在: $tls_cert_manager${NC}" >&2 + exit 1 + fi + + # 如果需要生成证书 + if [ -n "$generate_cert_domain" ]; then + echo -e "${BLUE}为域名 '$generate_cert_domain' 生成自签名证书...${NC}" + if ! "$tls_cert_manager" --generate "$generate_cert_domain"; then + echo -e "${RED}错误: 生成自签名证书失败${NC}" >&2 + exit 1 + fi + + # 设置证书路径 + local cert_name="${generate_cert_domain//[^a-zA-Z0-9]/_}" + local cert_dir="${DEPLOY_DIR}/scripts/9-other-script/certs/${cert_name}" + tls_cert_path="$cert_dir/tls.crt" + tls_key_path="$cert_dir/tls.key" + + echo -e "${GREEN}自签名证书生成完成${NC}" + echo -e "证书路径: $tls_cert_path" + echo -e "私钥路径: $tls_key_path" + fi + + # 验证证书文件 + if [ "$use_tls" = "true" ]; then + if [ -z "$tls_cert_path" ] || [ -z "$tls_key_path" ]; then + echo -e "${RED}错误: 启用TLS但未提供证书文件路径${NC}" >&2 + echo -e "${YELLOW}请使用 --tls-cert 和 --tls-key 指定证书文件,或使用 --generate-cert 生成自签名证书${NC}" >&2 + exit 1 + fi + + if [ ! -f "$tls_cert_path" ]; then + echo -e "${RED}错误: 证书文件不存在: $tls_cert_path${NC}" >&2 + exit 1 + fi + + if [ ! -f "$tls_key_path" ]; then + echo -e "${RED}错误: 私钥文件不存在: $tls_key_path${NC}" >&2 + exit 1 + fi + + # 验证证书 + echo -e "${BLUE}验证TLS证书...${NC}" + if ! "$tls_cert_manager" --verify "$tls_cert_path"; then + echo -e "${RED}错误: 证书验证失败${NC}" >&2 + exit 1 + fi + + echo -e "${GREEN}TLS证书验证通过${NC}" + fi +} + +# 检查authelia服务的HTTPS协议和TLS证书 +check_authelia_https_requirements() { + if [ "$auth_service_type" = "authelia" ]; then + echo -e "${BLUE}检查authelia鉴权服务的HTTPS要求...${NC}" + + # 检查鉴权服务地址是否使用HTTPS协议 + if [[ ! "$auth_service_address" =~ ^https:// ]]; then + echo -e "${RED}错误: authelia鉴权服务必须使用HTTPS协议${NC}" >&2 + echo -e "${YELLOW}当前地址: $auth_service_address${NC}" >&2 + echo -e "${YELLOW}请确保authelia服务配置为HTTPS访问${NC}" >&2 + exit 1 + fi + + # 强制要求TLS证书 + if [ "$use_tls" != "true" ]; then + echo -e "${YELLOW}警告: 检测到authelia鉴权服务,强制启用TLS证书支持${NC}" + use_tls="true" + + # 如果没有提供证书,提示用户选择 + if [ -z "$tls_cert_path" ] && [ -z "$generate_cert_domain" ]; then + echo -e "${BLUE}authelia鉴权服务需要TLS证书,请选择:${NC}" + echo -e "1) 生成自签名证书" + echo -e "2) 使用已有证书" + + if [ -t 0 ]; then # 交互式模式 + while true; do + read -p "请选择 (1/2): " choice + case "$choice" in + 1) + # 从鉴权服务地址提取域名 + local domain=$(echo "$auth_service_address" | sed -E 's|https?://([^:/]+).*|\1|') + generate_cert_domain="$domain" + echo -e "${GREEN}将为域名 '$domain' 生成自签名证书${NC}" + break + ;; + 2) + echo -e "${YELLOW}请重新运行脚本并使用 --tls-cert 和 --tls-key 参数指定证书文件${NC}" + exit 1 + ;; + *) + echo -e "${RED}无效选择,请输入1或2${NC}" + ;; + esac + done + else + # 非交互式模式,自动生成证书 + local domain=$(echo "$auth_service_address" | sed -E 's|https?://([^:/]+).*|\1|') + generate_cert_domain="$domain" + echo -e "${GREEN}非交互式模式:将为域名 '$domain' 自动生成自签名证书${NC}" + fi + fi + fi + + echo -e "${GREEN}authelia HTTPS要求检查通过${NC}" + fi +} + +# 创建TLS Secret +create_tls_secret() { + if [ "$use_tls" = "true" ] && [ -n "$tls_cert_path" ] && [ -n "$tls_key_path" ]; then + echo -e "${BLUE}创建TLS Secret...${NC}" + + local secret_name="eulercopilot-tls-secret" + + # 删除现有Secret(如果存在) + kubectl delete secret "$secret_name" -n "$NAMESPACE" 2>/dev/null || true + + # 创建TLS Secret + if kubectl create secret tls "$secret_name" \ + --cert="$tls_cert_path" \ + --key="$tls_key_path" \ + -n "$NAMESPACE"; then + echo -e "${GREEN}TLS Secret创建成功: $secret_name${NC}" + else + echo -e "${RED}错误: TLS Secret创建失败${NC}" >&2 + exit 1 + fi + fi +} # 安装成功信息显示函数 show_success_message() { @@ -84,6 +262,12 @@ show_success_message() { if [ -n "$auth_service_address" ]; then echo -e "鉴权服务 (${auth_service_type}): ${auth_service_address}" fi + if [ "$use_tls" = "true" ]; then + echo -e "TLS证书: 已启用" + if [ -n "$tls_cert_path" ]; then + echo -e "证书文件: ${tls_cert_path}" + fi + fi echo -e "\n${YELLOW}系统信息:${NC}" echo -e "内网IP: ${host}" @@ -172,12 +356,174 @@ get_address_input() { echo -e "${YELLOW}注意: 鉴权服务将自动检测并提供选择${NC}" } +# 检测已部署的Authelia认证策略 +detect_authelia_policy() { + echo -e "${BLUE}检测已部署的Authelia认证策略...${NC}" + + # 首先尝试从配置文件读取 + local config_file="/tmp/authelia_auth_policy.conf" + if [ -f "$config_file" ]; then + source "$config_file" + if [ -n "$AUTH_POLICY" ]; then + auth_policy="$AUTH_POLICY" + echo -e "${GREEN}从配置文件检测到认证策略:$auth_policy${NC}" + return 0 + fi + fi + + # 如果配置文件不存在,从Kubernetes ConfigMap检测 + if kubectl get configmap authelia-config -n euler-copilot &>/dev/null; then + local default_policy + default_policy=$(kubectl get configmap authelia-config -n euler-copilot -o yaml | grep -E "default_policy:" | awk '{print $2}' | head -1) + + if [ -n "$default_policy" ]; then + auth_policy="$default_policy" + echo -e "${GREEN}从ConfigMap检测到认证策略:$auth_policy${NC}" + else + # 默认策略 + auth_policy="one_factor" + echo -e "${YELLOW}无法检测到认证策略,使用默认值:$auth_policy${NC}" + fi + else + auth_policy="one_factor" + echo -e "${YELLOW}未找到Authelia配置,使用默认认证策略:$auth_policy${NC}" + fi + + return 0 +} + +# 选择兼容的认证策略 +select_compatible_policy() { + echo -e "${BLUE}根据已部署的Authelia服务选择兼容的认证策略...${NC}" + + if [ "$auth_policy" = "two_factor" ]; then + echo -e "${YELLOW}检测到Authelia配置为双因子认证${NC}" + echo -e "${BLUE}请选择EulerCopilot的认证策略:${NC}" + echo "1) two_factor - 双因子认证(与Authelia保持一致)" + echo "2) one_factor - 单因子认证(降级使用)" + echo -n "请输入选项编号(1-2): " + + local choice + read -r choice + + case $choice in + 1) + echo -e "${GREEN}选择双因子认证,与Authelia保持一致${NC}" + ;; + 2) + auth_policy="one_factor" + echo -e "${YELLOW}选择单因子认证,将使用降级模式${NC}" + ;; + *) + echo -e "${YELLOW}无效选择,默认使用双因子认证${NC}" + ;; + esac + else + echo -e "${GREEN}检测到Authelia配置为单因子认证,EulerCopilot将使用相同策略${NC}" + fi + + return 0 +} + +# 使用新的authelia客户端管理工具创建OIDC客户端 +create_authelia_client() { + local client_manager="${DEPLOY_DIR}/scripts/9-other-script/authelia_client_manager.sh" + + echo -e "${BLUE}正在使用authelia客户端管理工具创建OIDC客户端...${NC}" + + # 检查客户端管理工具是否存在 + if [ ! -f "$client_manager" ]; then + echo -e "${RED}错误:未找到authelia客户端管理工具: $client_manager${NC}" + return 1 + fi + + # 确保脚本可执行 + chmod +x "$client_manager" + + # 检测并选择认证策略 + detect_authelia_policy + select_compatible_policy + + # 生成客户端名称和重定向URI + local client_name="EulerCopilot" + local redirect_uri="${eulercopilot_address}/api/auth/login" + + echo -e "${BLUE}创建OIDC客户端配置:${NC}" + echo -e " 客户端名称: $client_name" + echo -e " 重定向URI: $redirect_uri" + + # 创建OIDC客户端 + local temp_output + temp_output=$(mktemp) + + if "$client_manager" create "$client_name" "$redirect_uri" "" "" "$auth_policy" > "$temp_output" 2>&1; then + # 从输出中提取客户端信息 + local output_content + output_content=$(cat "$temp_output") + + # 提取client_id和client_secret + client_id=$(echo "$output_content" | grep -oP 'Client ID:\s*\K[^\s]+' | head -1) + client_secret=$(echo "$output_content" | grep -oP 'Client Secret:\s*\K[^\s]+' | head -1) + + rm -f "$temp_output" + + if [ -n "$client_id" ] && [ -n "$client_secret" ]; then + echo -e "${GREEN}✓ OIDC客户端创建成功:${NC}" + echo -e " Client ID: $client_id" + echo -e " Client Secret: $client_secret" + + # 设置服务信息 + auth_service_type="authelia" + local host_ip + host_ip=$(hostname -I | awk '{print $1}') + auth_service_address="https://${host_ip}:30091" + + return 0 + else + echo -e "${RED}错误:无法从客户端管理工具输出中提取客户端信息${NC}" + echo -e "${YELLOW}脚本输出:${NC}" + cat "$temp_output" + rm -f "$temp_output" + return 1 + fi + else + echo -e "${RED}错误:authelia客户端管理工具执行失败${NC}" + cat "$temp_output" + rm -f "$temp_output" + return 1 + fi +} + get_client_info_auto() { # 获取用户输入地址 get_address_input echo -e "${BLUE}正在自动获取认证信息...${NC}" + # 检测鉴权服务类型 + echo -e "${BLUE}检测已部署的鉴权服务...${NC}" + + # 检查authelia服务 + if kubectl get service authelia -n euler-copilot &>/dev/null; then + echo -e "${GREEN}检测到authelia服务,使用authelia_automation.sh创建客户端${NC}" + auth_service_type="authelia" + + # 获取authelia服务地址 + local host_ip + host_ip=$(hostname -I | awk '{print $1}') + auth_service_address="https://${host_ip}:30091" + + # 使用authelia_automation.sh创建客户端 + if create_authelia_client; then + return 0 + else + echo -e "${YELLOW}authelia客户端创建失败,尝试使用备用方法...${NC}" + fi + fi + + # 如果authelia方法失败,回退到原来的方法 + echo -e "${BLUE}使用通用脚本获取认证信息...${NC}" + # 创建临时文件 local temp_file temp_file=$(mktemp) @@ -339,13 +685,40 @@ modify_yaml() { # 添加其他必填参数 set_args+=( "--set" "globals.arch=$arch" - "--set" "login.client.id=${client_id}" - "--set" "login.client.secret=${client_secret}" "--set" "domain.euler_copilot=${eulercopilot_address}" ) - # 注意:authhub和authelia的域名将通过Helm模板自动构建 - # 不再需要在这里手动设置domain.authhub和domain.authelia + # 根据检测到的认证服务类型设置相应的配置 + if [ "$auth_service_type" = "authelia" ]; then + set_args+=( + "--set" "login.provider=authelia" + "--set" "login.authelia.url=${auth_service_address}" + "--set" "login.authelia.client_id=${client_id}" + "--set" "login.authelia.client_secret=${client_secret}" + "--set" "domain.authelia=${auth_service_address}" + ) + echo -e "${GREEN}配置认证服务类型: authelia (${auth_service_address})${NC}" >&2 + elif [ "$auth_service_type" = "authhub" ] || [ "$auth_service_type" = "authHub" ]; then + set_args+=( + "--set" "login.provider=authhub" + "--set" "login.client.id=${client_id}" + "--set" "login.client.secret=${client_secret}" + "--set" "domain.authhub=${auth_service_address}" + ) + echo -e "${GREEN}配置认证服务类型: ${auth_service_type} (${auth_service_address})${NC}" >&2 + else + echo -e "${RED}错误:未知的认证服务类型: ${auth_service_type}${NC}" >&2 + exit 1 + fi + + # 如果启用了TLS,添加TLS相关配置 + if [ "$use_tls" = "true" ]; then + set_args+=( + "--set" "tls.enabled=true" + "--set" "tls.secretName=eulercopilot-tls-secret" + ) + echo -e "${GREEN}已启用TLS配置${NC}" >&2 + fi # 如果不需要保留模型配置,则添加模型相关的参数 if [[ "$preserve_models" != [Yy]* ]]; then @@ -462,6 +835,12 @@ main() { get_client_info_manual fi + # 检查authelia的HTTPS要求 + check_authelia_https_requirements + + # 管理TLS证书 + manage_tls_certificates + check_directories # 交互式提示优化 @@ -483,6 +862,9 @@ main() { echo -e "${BLUE}开始修改YAML配置...${NC}" modify_yaml "$host" "$preserve_models" + # 创建TLS Secret(在Helm安装之前) + create_tls_secret + echo -e "${BLUE}开始Helm安装...${NC}" execute_helm_install diff --git a/deploy/scripts/9-other-script/authelia_client_manager.sh b/deploy/scripts/9-other-script/authelia_client_manager.sh new file mode 100755 index 0000000000000000000000000000000000000000..41a774545f5bf3ba3f193f56d06efc19b89b2774 --- /dev/null +++ b/deploy/scripts/9-other-script/authelia_client_manager.sh @@ -0,0 +1,361 @@ +#!/bin/bash + +# Authelia OIDC客户端动态管理工具 +# 通过更新ConfigMap来实现客户端的动态注册 + +set -eo pipefail + +# 颜色定义 +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +NC='\033[0m' + +NAMESPACE="euler-copilot" +CONFIGMAP_NAME="authelia-config" + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 生成UUID格式的客户端ID +generate_client_id() { + python3 -c "import uuid; print(str(uuid.uuid4()))" +} + +# 生成客户端密钥 +generate_client_secret() { + python3 -c "import secrets, string; print(''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64)))" +} + +# 检查authelia服务是否存在 +check_authelia_service() { + if ! kubectl get service authelia -n "$NAMESPACE" &>/dev/null; then + log_error "未找到authelia服务,请确保authelia已正确部署" + return 1 + fi + return 0 +} + +# 备份当前ConfigMap +backup_configmap() { + local backup_file="/tmp/authelia-config-backup-$(date +%Y%m%d_%H%M%S).yaml" + kubectl get configmap "$CONFIGMAP_NAME" -n "$NAMESPACE" -o yaml > "$backup_file" + # 使用纯文本输出,避免颜色代码干扰 + echo "ConfigMap已备份到: $backup_file" >&2 + echo "$backup_file" +} + +# 添加或更新OIDC客户端 +add_or_update_client() { + local client_id="$1" + local client_name="$2" + local client_secret="$3" + local redirect_uri="$4" + local auth_policy="${5:-two_factor}" # 新增:认证策略参数,默认为two_factor + + if [ -z "$client_id" ] || [ -z "$client_name" ] || [ -z "$client_secret" ] || [ -z "$redirect_uri" ]; then + log_error "参数不完整: client_id, client_name, client_secret, redirect_uri 都是必需的" + return 1 + fi + + log_info "准备添加/更新OIDC客户端:" + log_info " Client ID: $client_id" + log_info " Client Name: $client_name" + log_info " Redirect URI: $redirect_uri" + log_info " Auth Policy: $auth_policy" + log_info " PKCE Required: false" + + # 备份ConfigMap + local backup_file + backup_file=$(backup_configmap) + + # 获取当前配置 + local current_config + current_config=$(kubectl get configmap "$CONFIGMAP_NAME" -n "$NAMESPACE" -o jsonpath='{.data.authelia\.yml}') + + # 检查客户端是否已存在 + if echo "$current_config" | grep -q "client_id: \"$client_id\""; then + log_info "客户端 $client_id 已存在,将更新配置" + + # 使用 Python 脚本精确更新现有客户端 + local updated_config + updated_config=$(python3 << EOF +import yaml +import sys + +try: + # 解析当前配置 + config_yaml = '''$current_config''' + config = yaml.safe_load(config_yaml) + + # 查找并更新现有客户端 + clients = config['identity_providers']['oidc']['clients'] + for i, client in enumerate(clients): + if client.get('client_id') == '$client_id': + # 更新客户端配置 + clients[i].update({ + 'client_name': '$client_name', + 'client_secret': '$client_secret', + 'authorization_policy': '$auth_policy', + 'require_pkce': True, # 确保 PKCE 设置正确 + 'redirect_uris': ['$redirect_uri'] + }) + break + + # 输出更新后的配置 + print(yaml.dump(config, default_flow_style=False)) + +except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) +EOF +) + else + log_info "添加新的OIDC客户端(替换占位符客户端)" + + # 使用 Python 脚本精确控制客户端配置 + local updated_config + updated_config=$(python3 << EOF +import yaml +import sys + +try: + # 解析当前配置 + config_yaml = '''$current_config''' + config = yaml.safe_load(config_yaml) + + # 确保结构存在 + if 'identity_providers' not in config: + config['identity_providers'] = {} + if 'oidc' not in config['identity_providers']: + config['identity_providers']['oidc'] = {} + if 'clients' not in config['identity_providers']['oidc']: + config['identity_providers']['oidc']['clients'] = [] + + # 创建新的客户端配置 + new_client = { + 'client_id': '$client_id', + 'client_name': '$client_name', + 'client_secret': '$client_secret', + 'public': False, + 'authorization_policy': '$auth_policy', + 'token_endpoint_auth_method': 'client_secret_basic', + 'require_pkce': True, # 明确设置为 True + 'pkce_challenge_method': 'S256', + 'redirect_uris': ['$redirect_uri'], + 'scopes': ['openid', 'profile', 'email', 'groups'], + 'response_types': ['code'], + 'grant_types': ['authorization_code'], + 'response_modes': ['query', 'form_post'], + 'userinfo_signed_response_alg': 'none', + 'consent_mode': 'implicit', + 'pre_configured_consent_duration': '1y' + } + + # 检查是否存在占位符客户端,如果存在则替换,否则添加 + clients = config['identity_providers']['oidc']['clients'] + placeholder_found = False + + for i, client in enumerate(clients): + if client.get('client_id') == 'placeholder-client': + clients[i] = new_client + placeholder_found = True + break + + if not placeholder_found: + # 检查是否已存在相同的客户端ID + existing_found = False + for i, client in enumerate(clients): + if client.get('client_id') == '$client_id': + clients[i] = new_client + existing_found = True + break + + if not existing_found: + clients.append(new_client) + + # 输出更新后的配置 + print(yaml.dump(config, default_flow_style=False)) + +except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) +EOF +) + fi + + # 验证生成的配置是否有效 + if [ -z "$updated_config" ]; then + log_error "生成的配置为空,操作失败" + return 1 + fi + + # 创建临时文件存储新配置 + local temp_file + temp_file=$(mktemp) + echo "$updated_config" > "$temp_file" + + # 验证 YAML 格式是否正确 + if ! python3 -c "import yaml; yaml.safe_load(open('$temp_file'))" 2>/dev/null; then + log_error "生成的 YAML 配置格式不正确" + rm -f "$temp_file" + return 1 + fi + + # 使用更可靠的方法更新 ConfigMap + # 创建新的 ConfigMap YAML 文件 + local new_configmap_file + new_configmap_file=$(mktemp) + + # 获取当前 ConfigMap 的元数据 + kubectl get configmap "$CONFIGMAP_NAME" -n "$NAMESPACE" -o yaml | \ + sed '/^ resourceVersion:/d' | \ + sed '/^ uid:/d' | \ + sed '/^ creationTimestamp:/d' > "$new_configmap_file" + + # 使用 Python 脚本更新 ConfigMap 中的 authelia.yml + python3 << EOF +import yaml +import sys + +try: + # 读取当前 ConfigMap + with open('$new_configmap_file', 'r') as f: + configmap = yaml.safe_load(f) + + # 读取新的 authelia.yml 内容 + with open('$temp_file', 'r') as f: + new_authelia_config = f.read() + + # 更新 ConfigMap 中的 authelia.yml + configmap['data']['authelia.yml'] = new_authelia_config + + # 写回 ConfigMap 文件 + with open('$new_configmap_file', 'w') as f: + yaml.dump(configmap, f, default_flow_style=False) + + print("ConfigMap 文件更新成功") + +except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) +EOF + + # 应用更新后的 ConfigMap + if kubectl apply -f "$new_configmap_file"; then + log_success "ConfigMap 更新成功" + rm -f "$new_configmap_file" + else + log_error "ConfigMap 更新失败,正在恢复备份" + kubectl apply -f "$backup_file" + rm -f "$temp_file" "$new_configmap_file" + return 1 + fi + + # 清理临时文件 + rm -f "$temp_file" + + log_success "ConfigMap更新成功" + + # 重启authelia以加载新配置 + log_info "重启authelia服务以加载新配置..." + kubectl rollout restart deployment/authelia -n "$NAMESPACE" + + # 等待重启完成 + if kubectl rollout status deployment/authelia -n "$NAMESPACE" --timeout=120s; then + log_success "authelia服务重启完成" + log_success "OIDC客户端 $client_id 配置成功" + + # 输出客户端信息 + echo "" + log_success "客户端信息:" + echo " Client ID: $client_id" + echo " Client Secret: $client_secret" + echo " Redirect URI: $redirect_uri" + + return 0 + else + log_error "authelia服务重启失败" + log_warning "正在恢复备份配置..." + kubectl apply -f "$backup_file" + return 1 + fi +} + +# 创建新的OIDC客户端 +create_client() { + local client_name="$1" + local redirect_uri="$2" + local client_id="${3:-$(generate_client_id)}" + local client_secret="${4:-$(generate_client_secret)}" + local auth_policy="${5:-two_factor}" # 新增:认证策略参数 + + if [ -z "$client_name" ] || [ -z "$redirect_uri" ]; then + log_error "用法: create_client [client_id] [client_secret]" + return 1 + fi + + add_or_update_client "$client_id" "$client_name" "$client_secret" "$redirect_uri" "$auth_policy" +} + +# 显示帮助信息 +show_help() { + echo -e "${GREEN}Authelia OIDC客户端动态管理工具${NC}" + echo "" + echo -e "${BLUE}用法:${NC}" + echo " $0 create [client_id] [client_secret]" + echo " $0 update " + echo "" + echo -e "${BLUE}示例:${NC}" + echo " # 创建新客户端(自动生成ID和密钥)" + echo " $0 create EulerCopilot https://10.211.55.10/api/auth/login" + echo "" + echo " # 创建客户端(指定ID和密钥)" + echo " $0 create EulerCopilot https://10.211.55.10/api/auth/login my-client-id my-secret" + echo "" + echo " # 更新现有客户端" + echo " $0 update existing-client-id EulerCopilot new-secret https://new-uri/callback" +} + +# 主函数 +main() { + local command="$1" + shift + + case "$command" in + "create") + check_authelia_service || exit 1 + create_client "$@" + ;; + "update") + check_authelia_service || exit 1 + add_or_update_client "$@" + ;; + "--help"|"help"|"") + show_help + ;; + *) + log_error "未知命令: $command" + show_help + exit 1 + ;; + esac +} + +# 执行主函数 +main "$@" diff --git a/deploy/scripts/9-other-script/tls_cert_manager.sh b/deploy/scripts/9-other-script/tls_cert_manager.sh new file mode 100755 index 0000000000000000000000000000000000000000..0869c5294280ca880a7a140cd922c298f2da27dd --- /dev/null +++ b/deploy/scripts/9-other-script/tls_cert_manager.sh @@ -0,0 +1,412 @@ +#!/bin/bash + +set -eo pipefail + +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +NC='\033[0m' + +# 脚本目录 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CERT_DIR="${SCRIPT_DIR}/certs" + +# 打印帮助信息 +print_help() { + echo -e "${GREEN}TLS证书管理工具" + echo -e "用法: $0 [选项]" + echo -e "选项:" + echo -e " --help 显示帮助信息" + echo -e " --generate <域名> 生成自签名证书" + echo -e " --install <证书路径> <私钥路径> 安装已有证书" + echo -e " --list 列出已有证书" + echo -e " --remove <证书名称> 删除指定证书" + echo -e " --verify <证书路径> 验证证书" + echo -e "" + echo -e "示例:" + echo -e " $0 --generate authelia.local" + echo -e " $0 --install /path/to/cert.crt /path/to/key.key" + echo -e " $0 --verify /path/to/cert.crt${NC}" + exit 0 +} + +# 创建证书目录 +create_cert_dir() { + if [[ ! -d "$CERT_DIR" ]]; then + mkdir -p "$CERT_DIR" + echo -e "${GREEN}创建证书目录: $CERT_DIR${NC}" + fi +} + +# 生成自签名证书 +generate_self_signed_cert() { + local domain="$1" + local cert_name="${domain//[^a-zA-Z0-9]/_}" + local cert_path="$CERT_DIR/${cert_name}" + + if [[ -z "$domain" ]]; then + echo -e "${RED}错误:请提供域名${NC}" + return 1 + fi + + echo -e "${BLUE}==> 为域名 '$domain' 生成自签名证书...${NC}" + + create_cert_dir + mkdir -p "$cert_path" + + # 生成私钥 + openssl genrsa -out "$cert_path/tls.key" 2048 + + # 生成证书签名请求配置 + cat > "$cert_path/cert.conf" <> "$cert_path/cert.conf" + fi + + # 生成证书 + openssl req -new -x509 -key "$cert_path/tls.key" -out "$cert_path/tls.crt" -days 365 -config "$cert_path/cert.conf" -extensions v3_req + + # 生成证书信息文件 + cat > "$cert_path/info.txt" < 安装已有证书...${NC}" + + # 验证证书和私钥匹配 + local cert_hash key_hash + cert_hash=$(openssl x509 -noout -modulus -in "$cert_file" | openssl md5) + key_hash=$(openssl rsa -noout -modulus -in "$key_file" | openssl md5) + + if [[ "$cert_hash" != "$key_hash" ]]; then + echo -e "${RED}错误:证书和私钥不匹配!${NC}" + return 1 + fi + + # 获取证书的CN作为名称 + local cn + cn=$(openssl x509 -noout -subject -in "$cert_file" | sed -n 's/.*CN=\([^,]*\).*/\1/p' | tr -d ' ') + local cert_name="${cn//[^a-zA-Z0-9]/_}" + + if [[ -z "$cert_name" ]]; then + cert_name="imported_$(date +%Y%m%d_%H%M%S)" + fi + + local cert_path="$CERT_DIR/${cert_name}" + + create_cert_dir + mkdir -p "$cert_path" + + # 复制证书文件 + cp "$cert_file" "$cert_path/tls.crt" + cp "$key_file" "$cert_path/tls.key" + + # 生成证书信息文件 + cat > "$cert_path/info.txt" < 已安装的证书:${NC}" + + if [[ ! -d "$CERT_DIR" ]] || [[ -z "$(ls -A "$CERT_DIR" 2>/dev/null)" ]]; then + echo -e "${YELLOW}未找到已安装的证书${NC}" + return 0 + fi + + local count=0 + for cert_path in "$CERT_DIR"/*; do + if [[ -d "$cert_path" ]] && [[ -f "$cert_path/tls.crt" ]]; then + count=$((count + 1)) + local cert_name=$(basename "$cert_path") + echo -e "\n${GREEN}[$count] $cert_name${NC}" + + if [[ -f "$cert_path/info.txt" ]]; then + cat "$cert_path/info.txt" | sed 's/^/ /' + else + echo -e " 证书路径: $cert_path/tls.crt" + echo -e " 私钥路径: $cert_path/tls.key" + fi + + # 显示证书有效期 + local validity + validity=$(openssl x509 -in "$cert_path/tls.crt" -noout -dates 2>/dev/null || echo "无法读取证书信息") + echo -e " 有效期: $validity" | sed 's/^/ /' + fi + done + + if [[ $count -eq 0 ]]; then + echo -e "${YELLOW}未找到有效的证书${NC}" + fi +} + +# 删除指定证书 +remove_certificate() { + local cert_name="$1" + + if [[ -z "$cert_name" ]]; then + echo -e "${RED}错误:请提供证书名称${NC}" + return 1 + fi + + local cert_path="$CERT_DIR/$cert_name" + + if [[ ! -d "$cert_path" ]]; then + echo -e "${RED}错误:证书不存在: $cert_name${NC}" + return 1 + fi + + echo -e "${YELLOW}确认删除证书 '$cert_name'?(y/N): ${NC}" + read -p "" confirm + + if [[ "$confirm" =~ ^[Yy]$ ]]; then + rm -rf "$cert_path" + echo -e "${GREEN}证书 '$cert_name' 已删除${NC}" + else + echo -e "${YELLOW}操作已取消${NC}" + fi +} + +# 验证证书 +verify_certificate() { + local cert_file="$1" + + if [[ -z "$cert_file" ]]; then + echo -e "${RED}错误:请提供证书文件路径${NC}" + return 1 + fi + + if [[ ! -f "$cert_file" ]]; then + echo -e "${RED}错误:证书文件不存在: $cert_file${NC}" + return 1 + fi + + echo -e "${BLUE}==> 验证证书: $cert_file${NC}" + + # 检查证书格式 + if ! openssl x509 -in "$cert_file" -noout 2>/dev/null; then + echo -e "${RED}错误:无效的证书格式${NC}" + return 1 + fi + + echo -e "${GREEN}证书格式有效${NC}" + + # 显示证书详细信息 + echo -e "\n${BLUE}证书详细信息:${NC}" + openssl x509 -in "$cert_file" -text -noout + + # 检查证书是否过期 + if openssl x509 -in "$cert_file" -checkend 0 >/dev/null 2>&1; then + echo -e "\n${GREEN}证书有效(未过期)${NC}" + else + echo -e "\n${RED}警告:证书已过期!${NC}" + fi + + return 0 +} + +# 创建Kubernetes Secret +create_k8s_secret() { + local cert_name="$1" + local secret_name="$2" + local namespace="${3:-euler-copilot}" + + if [[ -z "$cert_name" ]]; then + echo -e "${RED}错误:请提供证书名称${NC}" + return 1 + fi + + if [[ -z "$secret_name" ]]; then + secret_name="$cert_name-tls-secret" + fi + + local cert_path="$CERT_DIR/$cert_name" + + if [[ ! -d "$cert_path" ]] || [[ ! -f "$cert_path/tls.crt" ]] || [[ ! -f "$cert_path/tls.key" ]]; then + echo -e "${RED}错误:证书不存在或不完整: $cert_name${NC}" + return 1 + fi + + echo -e "${BLUE}==> 创建Kubernetes TLS Secret...${NC}" + echo -e "证书名称: $cert_name" + echo -e "Secret名称: $secret_name" + echo -e "命名空间: $namespace" + + # 删除现有Secret(如果存在) + kubectl delete secret "$secret_name" -n "$namespace" 2>/dev/null || true + + # 创建TLS Secret + kubectl create secret tls "$secret_name" \ + --cert="$cert_path/tls.crt" \ + --key="$cert_path/tls.key" \ + -n "$namespace" || { + echo -e "${RED}创建Kubernetes Secret失败!${NC}" + return 1 + } + + echo -e "${GREEN}Kubernetes TLS Secret创建成功!${NC}" + echo -e "使用方法:" + echo -e " 在Helm values中设置: authelia.config.server.tls.enabled=true" + echo -e " Secret名称: $secret_name" +} + +# 解析命令行参数 +parse_args() { + if [[ $# -eq 0 ]]; then + print_help + fi + + while [[ $# -gt 0 ]]; do + case "$1" in + --help) + print_help + ;; + --generate) + if [[ -n "$2" ]]; then + generate_self_signed_cert "$2" + exit $? + else + echo -e "${RED}错误:--generate 需要提供域名${NC}" + exit 1 + fi + ;; + --install) + if [[ -n "$2" ]] && [[ -n "$3" ]]; then + install_existing_cert "$2" "$3" + exit $? + else + echo -e "${RED}错误:--install 需要提供证书文件和私钥文件路径${NC}" + exit 1 + fi + ;; + --list) + list_certificates + exit 0 + ;; + --remove) + if [[ -n "$2" ]]; then + remove_certificate "$2" + exit $? + else + echo -e "${RED}错误:--remove 需要提供证书名称${NC}" + exit 1 + fi + ;; + --verify) + if [[ -n "$2" ]]; then + verify_certificate "$2" + exit $? + else + echo -e "${RED}错误:--verify 需要提供证书文件路径${NC}" + exit 1 + fi + ;; + --create-secret) + if [[ -n "$2" ]]; then + create_k8s_secret "$2" "$3" "$4" + exit $? + else + echo -e "${RED}错误:--create-secret 需要提供证书名称${NC}" + exit 1 + fi + ;; + *) + echo -e "${RED}未知参数: $1${NC}" + print_help + ;; + esac + done +} + +main() { + parse_args "$@" +} + +trap 'echo -e "${RED}操作被中断!${NC}"; exit 1' INT +main "$@" diff --git a/deploy/scripts/9-other-script/update_auth_config.sh b/deploy/scripts/9-other-script/update_auth_config.sh new file mode 100755 index 0000000000000000000000000000000000000000..2cbcf2e4f83c8543faa931800ec077011d90bcea --- /dev/null +++ b/deploy/scripts/9-other-script/update_auth_config.sh @@ -0,0 +1,362 @@ +#!/bin/bash + +set -eo pipefail + +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +NC='\033[0m' + +# 脚本目录 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CHART_DIR="$(dirname "$SCRIPT_DIR")/chart" +VALUES_FILE="$CHART_DIR/euler_copilot/values.yaml" + +# 生成UUID +generate_uuid() { + # 尝试使用系统的uuidgen命令 + if command -v uuidgen >/dev/null 2>&1; then + uuidgen | tr '[:upper:]' '[:lower:]' + else + # 如果没有uuidgen,使用openssl生成类似UUID的字符串 + printf '%08x-%04x-%04x-%04x-%012x\n' \ + $((RANDOM * RANDOM)) \ + $((RANDOM % 65536)) \ + $((RANDOM % 65536 | 16384)) \ + $((RANDOM % 65536 | 32768)) \ + $((RANDOM * RANDOM * RANDOM)) + fi +} + +# 打印帮助信息 +print_help() { + echo -e "${GREEN}认证服务配置更新工具" + echo -e "用法: $0 [选项]" + echo -e "选项:" + echo -e " --help 显示帮助信息" + echo -e " --auth-service <类型> 认证服务类型 (authhub|authelia)" + echo -e " --auth-address <地址> 认证服务的完整地址" + echo -e " --client-id OIDC客户端ID(仅Authelia,留空自动生成UUID)" + echo -e " --client-secret <密钥> OIDC客户端密钥(仅Authelia)" + echo -e " --generate-uuid 生成新的UUID客户端ID" + echo -e " --show-config 显示当前配置" + echo -e " --validate 验证配置" + echo -e "" + echo -e "示例:" + echo -e " $0 --auth-service authelia --auth-address https://10.211.55.10:30091" + echo -e " $0 --auth-service authhub --auth-address http://192.168.1.100:30081" + echo -e " $0 --generate-uuid" + echo -e " $0 --show-config" + echo -e " $0 --validate${NC}" + exit 0 +} + +# 检查文件是否存在 +check_files() { + if [[ ! -f "$VALUES_FILE" ]]; then + echo -e "${RED}错误:找不到values.yaml文件: $VALUES_FILE${NC}" + exit 1 + fi +} + +# 显示当前配置 +show_config() { + echo -e "${BLUE}==> 当前认证服务配置:${NC}" + + if [[ -f "$VALUES_FILE" ]]; then + echo -e "\n${GREEN}域名配置:${NC}" + grep -A 10 "^domain:" "$VALUES_FILE" | grep -E "(euler_copilot|authhub|authelia):" || true + + echo -e "\n${GREEN}端口配置:${NC}" + grep -A 5 "^ports:" "$VALUES_FILE" | grep -E "(authhub|authelia):" || true + + echo -e "\n${GREEN}登录提供者:${NC}" + grep -A 15 "^login:" "$VALUES_FILE" | grep -E "(provider|client_id|client_secret):" || true + else + echo -e "${RED}找不到配置文件${NC}" + fi +} + +# 验证地址格式 +validate_address() { + local address="$1" + + if [[ ! "$address" =~ ^https?://[^/]+$ ]]; then + echo -e "${RED}错误:地址格式不正确,应该是 http://host:port 或 https://host:port 格式${NC}" + return 1 + fi + + return 0 +} + +# 提取地址信息 +extract_address_info() { + local address="$1" + local protocol host port + + protocol=$(echo "$address" | sed -E 's|^(https?)://.*|\1|') + host=$(echo "$address" | sed -E 's|^https?://([^:/]+).*|\1|') + port=$(echo "$address" | sed -E 's|^https?://[^:]+:([0-9]+).*|\1|') + + # 如果没有端口,使用默认端口 + if [[ "$port" == "$address" ]]; then + if [[ "$protocol" == "https" ]]; then + port="443" + else + port="80" + fi + fi + + echo "$protocol $host $port" +} + +# 更新values.yaml中的域名配置 +update_domain_config() { + local auth_service="$1" + local auth_address="$2" + + echo -e "${BLUE}==> 更新域名配置...${NC}" + + # 备份原文件 + cp "$VALUES_FILE" "$VALUES_FILE.backup.$(date +%Y%m%d_%H%M%S)" + + # 使用更精确的sed更新配置(只匹配domain部分) + case "$auth_service" in + "authhub") + # 只更新 domain 部分下的 authhub 配置 + sed -i '/^domain:/,/^[^[:space:]]/ { /^ authhub:/ s|^ authhub:.*| authhub: '$auth_address'| }' "$VALUES_FILE" + echo -e "${GREEN}已更新 domain.authhub = $auth_address${NC}" + ;; + "authelia") + # 只更新 domain 部分下的 authelia 配置 + sed -i '/^domain:/,/^[^[:space:]]/ { /^ authelia:/ s|^ authelia:.*| authelia: '$auth_address'| }' "$VALUES_FILE" + echo -e "${GREEN}已更新 domain.authelia = $auth_address${NC}" + ;; + *) + echo -e "${RED}错误:不支持的认证服务类型: $auth_service${NC}" + return 1 + ;; + esac +} + +# 更新Authelia的OIDC配置 +update_authelia_oidc_config() { + local client_id="$1" + local client_secret="$2" + + # 如果没有提供client_id,自动生成UUID + if [[ -z "$client_id" ]]; then + client_id=$(generate_uuid) + echo -e "${YELLOW}未提供Client ID,自动生成UUID: $client_id${NC}" + fi + + if [[ -n "$client_id" ]]; then + # 只更新 login.authelia 部分下的 client_id + sed -i '/^ authelia:/,/^[^[:space:]]/ { /^ client_id:/ s|^ client_id:.*| client_id: '$client_id'| }' "$VALUES_FILE" + echo -e "${GREEN}已更新 login.authelia.client_id = $client_id${NC}" + fi + + if [[ -n "$client_secret" ]]; then + # 只更新 login.authelia 部分下的 client_secret + sed -i '/^ authelia:/,/^[^[:space:]]/ { /^ client_secret:/ s|^ client_secret:.*| client_secret: '$client_secret'| }' "$VALUES_FILE" + echo -e "${GREEN}已更新 login.authelia.client_secret = [已设置]${NC}" + fi +} + +# 验证配置 +validate_config() { + echo -e "${BLUE}==> 验证配置...${NC}" + + local errors=0 + + # 检查必要的配置项 + if ! grep -q "euler_copilot: http" "$VALUES_FILE"; then + echo -e "${RED}错误:euler_copilot域名未配置${NC}" + errors=$((errors + 1)) + fi + + # 检查登录提供者配置 + local provider + provider=$(grep "provider:" "$VALUES_FILE" | head -1 | sed 's/.*provider: *//' | tr -d '"') + + case "$provider" in + "authhub") + if grep -q "authhub:$" "$VALUES_FILE"; then + echo -e "${YELLOW}警告:使用authhub但未配置authhub域名,将使用自动构建的地址${NC}" + fi + ;; + "authelia") + if grep -q "authelia:$" "$VALUES_FILE"; then + echo -e "${YELLOW}警告:使用authelia但未配置authelia域名,将使用自动构建的地址${NC}" + fi + if grep -q "client_secret: your-client-secret-here" "$VALUES_FILE"; then + echo -e "${RED}错误:Authelia客户端密钥未更新${NC}" + errors=$((errors + 1)) + fi + ;; + *) + echo -e "${RED}错误:未知的登录提供者: $provider${NC}" + errors=$((errors + 1)) + ;; + esac + + if [[ $errors -eq 0 ]]; then + echo -e "${GREEN}配置验证通过${NC}" + return 0 + else + echo -e "${RED}发现 $errors 个配置错误${NC}" + return 1 + fi +} + +# 生成配置建议 +generate_config_suggestions() { + local auth_service="$1" + local auth_address="$2" + + echo -e "\n${BLUE}==> 配置建议:${NC}" + + case "$auth_service" in + "authhub") + echo -e "1. 确保AuthHub服务在 $auth_address 可访问" + echo -e "2. 检查AuthHub的客户端ID和密钥配置" + echo -e "3. 重新部署euler-copilot以应用新配置:" + echo -e " ${YELLOW}helm upgrade euler-copilot $CHART_DIR/euler_copilot -n euler-copilot${NC}" + ;; + "authelia") + echo -e "1. 确保Authelia服务在 $auth_address 可访问" + echo -e "2. 确认OIDC客户端ID和密钥正确配置" + echo -e "3. 检查Authelia的OIDC配置中的redirect_uris包含:" + local euler_copilot_domain + euler_copilot_domain=$(grep "euler_copilot:" "$VALUES_FILE" | head -1 | sed 's/.*euler_copilot: *//') + echo -e " ${YELLOW}$euler_copilot_domain/api/auth/login${NC}" + echo -e "4. 重新部署euler-copilot以应用新配置:" + echo -e " ${YELLOW}helm upgrade euler-copilot $CHART_DIR/euler_copilot -n euler-copilot${NC}" + ;; + esac +} + +# 主函数 +main() { + local auth_service="" + local auth_address="" + local client_id="" + local client_secret="" + local show_config_flag=false + local validate_flag=false + local generate_uuid_flag=false + + # 解析命令行参数 + while [[ $# -gt 0 ]]; do + case "$1" in + --help) + print_help + ;; + --auth-service) + if [[ -n "$2" ]] && [[ "$2" =~ ^(authhub|authelia)$ ]]; then + auth_service="$2" + shift 2 + else + echo -e "${RED}错误:--auth-service 需要提供有效参数 (authhub|authelia)${NC}" + exit 1 + fi + ;; + --auth-address) + if [[ -n "$2" ]]; then + auth_address="$2" + shift 2 + else + echo -e "${RED}错误:--auth-address 需要提供地址参数${NC}" + exit 1 + fi + ;; + --client-id) + if [[ -n "$2" ]]; then + client_id="$2" + shift 2 + else + echo -e "${RED}错误:--client-id 需要提供参数${NC}" + exit 1 + fi + ;; + --client-secret) + if [[ -n "$2" ]]; then + client_secret="$2" + shift 2 + else + echo -e "${RED}错误:--client-secret 需要提供参数${NC}" + exit 1 + fi + ;; + --show-config) + show_config_flag=true + shift + ;; + --validate) + validate_flag=true + shift + ;; + --generate-uuid) + generate_uuid_flag=true + shift + ;; + *) + echo -e "${RED}未知参数: $1${NC}" + print_help + ;; + esac + done + + check_files + + # 处理不同的操作 + if [[ "$show_config_flag" == true ]]; then + show_config + return 0 + fi + + if [[ "$validate_flag" == true ]]; then + validate_config + return $? + fi + + if [[ "$generate_uuid_flag" == true ]]; then + local new_uuid + new_uuid=$(generate_uuid) + echo -e "${GREEN}生成的UUID: ${new_uuid}${NC}" + echo -e "${BLUE}可以使用以下命令更新配置:${NC}" + echo -e "./update_auth_config.sh --auth-service authelia --auth-address <地址> --client-id $new_uuid" + return 0 + fi + + # 更新配置 + if [[ -n "$auth_service" ]] && [[ -n "$auth_address" ]]; then + validate_address "$auth_address" || exit 1 + + echo -e "${BLUE}==> 更新认证服务配置...${NC}" + echo -e "认证服务类型: $auth_service" + echo -e "认证服务地址: $auth_address" + + update_domain_config "$auth_service" "$auth_address" || exit 1 + + if [[ "$auth_service" == "authelia" ]]; then + update_authelia_oidc_config "$client_id" "$client_secret" + fi + + echo -e "\n${GREEN}配置更新完成!${NC}" + + # 验证更新后的配置 + validate_config + + # 生成配置建议 + generate_config_suggestions "$auth_service" "$auth_address" + + else + echo -e "${YELLOW}请提供认证服务类型和地址,或使用 --help 查看帮助${NC}" + exit 1 + fi +} + +trap 'echo -e "${RED}操作被中断!${NC}"; exit 1' INT +main "$@"