Clarify Feishu app setup and OpenVPN OIDC source

This commit is contained in:
opsx committed 2026-10-22 10:12:06 +08:00
1 parent 63cdda4ae1
commit 5ef1cb48cf
10 files changed
+154 -34

No files matched your search

+15 -2
View File
@@ -101,10 +101,23 @@ func prepareOIDC(t *model.Tunnel, cfg map[string]interface{}) error {
if err != nil {
return err
}
if service.Str(cfg, "oidc_client_secret") == "" {
oldCfg, err := store.TunnelConfig(t)
if err != nil {
return err
}
builtin := service.Str(cfg, "oidc_provider") == "builtin"
if !builtin && service.Str(oldCfg, "oidc_provider") != "builtin" && service.Str(cfg, "oidc_client_secret") == "" {
cfg["oidc_client_secret"] = old["oidc_client_secret"]
}
if secret, ok := identityprovider.ClientForIssuer(service.Str(cfg, "oidc_issuer"), service.Str(cfg, "oidc_client_id")); ok {
if builtin {
issuer, clientID, ready := identityprovider.PublicClient()
if !ready || issuer != service.Str(cfg, "oidc_issuer") || clientID != service.Str(cfg, "oidc_client_id") || strings.TrimSuffix(issuer, "/id") != strings.TrimRight(service.Str(cfg, "oidc_base_url"), "/") {
return fmt.Errorf("内置飞书身份服务未启用或与当前专线配置不匹配;请在系统设置中启用后重新选择")
}
secret, ok := identityprovider.ClientForIssuer(issuer, clientID)
if !ok {
return fmt.Errorf("无法获取内置 VPN 客户端凭据")
}
cfg["oidc_client_secret"] = secret
}
if service.Str(cfg, "oidc_client_secret") == "" {
+6 -1
View File
@@ -115,6 +115,11 @@ func (s *Service) Validate(cfg map[string]interface{}) (map[string]interface{},
return nil, fmt.Errorf("auth_mode 必须为 certificate、password、certificate+password 或 oidc")
}
if service.Str(out, "auth_mode") == "oidc" {
switch service.Str(out, "oidc_provider") {
case "", "external", "builtin":
default:
return nil, fmt.Errorf("oidc_provider 必须为内置飞书或外部 OIDC")
}
for _, key := range []string{"oidc_issuer", "oidc_base_url"} {
u, err := url.Parse(service.Str(out, key))
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || (key == "oidc_base_url" && u.Path != "" && u.Path != "/") {
@@ -133,7 +138,7 @@ func (s *Service) Validate(cfg map[string]interface{}) (map[string]interface{},
}
out["mfa_mode"] = "off" // 飞书 MFA 由身份提供方负责
} else {
for _, key := range []string{"oidc_issuer", "oidc_base_url", "oidc_client_id", "oidc_client_secret", "oidc_http_port", "oidc_http_secret", "oidc_management_password"} {
for _, key := range []string{"oidc_provider", "oidc_issuer", "oidc_base_url", "oidc_client_id", "oidc_client_secret", "oidc_http_port", "oidc_http_secret", "oidc_management_password"} {
delete(out, key)
}
}
@@ -105,6 +105,31 @@ func TestOIDCRejectsUnsafeURLs(t *testing.T) {
}
}
func TestSwitchingAwayFromBuiltInCannotReuseItsClientSecret(t *testing.T) {
dir := t.TempDir()
t.Setenv("VPN_DATA_DIR", dir)
if err := store.Init(dir); err != nil {
t.Fatal(err)
}
secret, err := store.EncryptSecret(`{"oidc_client_secret":"old-built-in-secret"}`)
if err != nil {
t.Fatal(err)
}
tunnel := &model.Tunnel{ID: 43, Protocol: "openvpn", Config: `{"oidc_provider":"builtin"}`, Secrets: secret}
cfg := map[string]interface{}{
"oidc_provider": "external", "oidc_issuer": "https://external.example.com/id",
"oidc_base_url": "https://login.example.com", "oidc_client_id": "external-client",
}
if err := prepareOIDC(tunnel, cfg); err == nil {
t.Fatal("external provider reused a built-in client secret")
}
cfg["oidc_provider"] = "builtin"
cfg["oidc_client_secret"] = "forged"
if err := prepareOIDC(tunnel, cfg); err == nil {
t.Fatal("built-in provider accepted an unregistered issuer and client")
}
}
func TestOIDCRequiresOpenVPN262(t *testing.T) {
for _, version := range []string{"OpenVPN 2.5.11 x86_64", "OpenVPN 2.6.1 x86_64", "unknown"} {
if err := validateOIDCOpenVPNVersion([]byte(version)); err == nil {