chore: support LDAPS

This commit is contained in:
Feng Yue
2025-08-13 16:09:26 +08:00
parent 39c6e3146c
commit 1f3fd9c285
3 changed files with 127 additions and 8 deletions

View File

@@ -58,4 +58,40 @@ ENABLE_CORS=true
TRUST_PROXY=true TRUST_PROXY=true
# 🔒 客户端限制(可选) # 🔒 客户端限制(可选)
# ALLOW_CUSTOM_CLIENTS=false # ALLOW_CUSTOM_CLIENTS=false
# 🔐 LDAP 认证配置
LDAP_ENABLED=false
LDAP_URL=ldaps://ldap-1.test1.bj.yxops.net:636
LDAP_BIND_DN=cn=admin,dc=example,dc=com
LDAP_BIND_PASSWORD=admin_password
LDAP_SEARCH_BASE=dc=example,dc=com
LDAP_SEARCH_FILTER=(uid={{username}})
LDAP_SEARCH_ATTRIBUTES=dn,uid,cn,mail,givenName,sn
LDAP_TIMEOUT=5000
LDAP_CONNECT_TIMEOUT=10000
# 🔒 LDAP TLS/SSL 配置 (用于 ldaps:// URL)
# 是否忽略证书验证错误 (设置为false可忽略自签名证书错误)
LDAP_TLS_REJECT_UNAUTHORIZED=true
# CA 证书文件路径 (可选用于自定义CA证书)
# LDAP_TLS_CA_FILE=/path/to/ca-cert.pem
# 客户端证书文件路径 (可选,用于双向认证)
# LDAP_TLS_CERT_FILE=/path/to/client-cert.pem
# 客户端私钥文件路径 (可选,用于双向认证)
# LDAP_TLS_KEY_FILE=/path/to/client-key.pem
# 服务器名称 (可选,用于 SNI)
# LDAP_TLS_SERVERNAME=ldap.example.com
# 🗺️ LDAP 用户属性映射
LDAP_USER_ATTR_USERNAME=uid
LDAP_USER_ATTR_DISPLAY_NAME=cn
LDAP_USER_ATTR_EMAIL=mail
LDAP_USER_ATTR_FIRST_NAME=givenName
LDAP_USER_ATTR_LAST_NAME=sn
# 👥 用户管理配置
USER_MANAGEMENT_ENABLED=false
DEFAULT_USER_ROLE=user
USER_SESSION_TIMEOUT=86400000
MAX_API_KEYS_PER_USER=5

View File

@@ -131,7 +131,20 @@ const config = {
searchFilter: process.env.LDAP_SEARCH_FILTER || '(uid={{username}})', searchFilter: process.env.LDAP_SEARCH_FILTER || '(uid={{username}})',
searchAttributes: process.env.LDAP_SEARCH_ATTRIBUTES ? process.env.LDAP_SEARCH_ATTRIBUTES.split(',') : ['dn', 'uid', 'cn', 'mail', 'givenName', 'sn'], searchAttributes: process.env.LDAP_SEARCH_ATTRIBUTES ? process.env.LDAP_SEARCH_ATTRIBUTES.split(',') : ['dn', 'uid', 'cn', 'mail', 'givenName', 'sn'],
timeout: parseInt(process.env.LDAP_TIMEOUT) || 5000, timeout: parseInt(process.env.LDAP_TIMEOUT) || 5000,
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT) || 10000 connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT) || 10000,
// TLS/SSL 配置
tls: {
// 是否忽略证书错误 (用于自签名证书)
rejectUnauthorized: process.env.LDAP_TLS_REJECT_UNAUTHORIZED !== 'false', // 默认验证证书设置为false则忽略
// CA证书文件路径 (可选用于自定义CA证书)
ca: process.env.LDAP_TLS_CA_FILE ? require('fs').readFileSync(process.env.LDAP_TLS_CA_FILE) : undefined,
// 客户端证书文件路径 (可选,用于双向认证)
cert: process.env.LDAP_TLS_CERT_FILE ? require('fs').readFileSync(process.env.LDAP_TLS_CERT_FILE) : undefined,
// 客户端私钥文件路径 (可选,用于双向认证)
key: process.env.LDAP_TLS_KEY_FILE ? require('fs').readFileSync(process.env.LDAP_TLS_KEY_FILE) : undefined,
// 服务器名称 (用于SNI可选)
servername: process.env.LDAP_TLS_SERVERNAME || undefined
}
}, },
userMapping: { userMapping: {
username: process.env.LDAP_USER_ATTR_USERNAME || 'uid', username: process.env.LDAP_USER_ATTR_USERNAME || 'uid',

View File

@@ -12,20 +12,76 @@ class LdapService {
// 🔗 创建LDAP客户端连接 // 🔗 创建LDAP客户端连接
createClient() { createClient() {
try { try {
const client = ldap.createClient({ const clientOptions = {
url: this.config.server.url, url: this.config.server.url,
timeout: this.config.server.timeout, timeout: this.config.server.timeout,
connectTimeout: this.config.server.connectTimeout, connectTimeout: this.config.server.connectTimeout,
reconnect: true reconnect: true
}) }
// 如果使用 LDAPS (SSL/TLS),添加 TLS 选项
if (this.config.server.url.toLowerCase().startsWith('ldaps://')) {
const tlsOptions = {}
// 证书验证设置
if (this.config.server.tls) {
if (typeof this.config.server.tls.rejectUnauthorized === 'boolean') {
tlsOptions.rejectUnauthorized = this.config.server.tls.rejectUnauthorized
}
// CA 证书
if (this.config.server.tls.ca) {
tlsOptions.ca = this.config.server.tls.ca
}
// 客户端证书和私钥 (双向认证)
if (this.config.server.tls.cert) {
tlsOptions.cert = this.config.server.tls.cert
}
if (this.config.server.tls.key) {
tlsOptions.key = this.config.server.tls.key
}
// 服务器名称 (SNI)
if (this.config.server.tls.servername) {
tlsOptions.servername = this.config.server.tls.servername
}
}
clientOptions.tlsOptions = tlsOptions
logger.debug('🔒 Creating LDAPS client with TLS options:', {
url: this.config.server.url,
rejectUnauthorized: tlsOptions.rejectUnauthorized,
hasCA: !!tlsOptions.ca,
hasCert: !!tlsOptions.cert,
hasKey: !!tlsOptions.key,
servername: tlsOptions.servername
})
}
const client = ldap.createClient(clientOptions)
// 设置错误处理 // 设置错误处理
client.on('error', (err) => { client.on('error', (err) => {
logger.error('🔌 LDAP client error:', err) if (err.code === 'CERT_HAS_EXPIRED' || err.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
logger.error('🔒 LDAP TLS certificate error:', {
code: err.code,
message: err.message,
hint: 'Consider setting LDAP_TLS_REJECT_UNAUTHORIZED=false for self-signed certificates'
})
} else {
logger.error('🔌 LDAP client error:', err)
}
}) })
client.on('connect', () => { client.on('connect', () => {
logger.info('🔗 LDAP client connected successfully') if (this.config.server.url.toLowerCase().startsWith('ldaps://')) {
logger.info('🔒 LDAPS client connected successfully')
} else {
logger.info('🔗 LDAP client connected successfully')
}
}) })
client.on('connectTimeout', () => { client.on('connectTimeout', () => {
@@ -280,16 +336,30 @@ class LdapService {
// 📊 获取LDAP配置信息不包含敏感信息 // 📊 获取LDAP配置信息不包含敏感信息
getConfigInfo() { getConfigInfo() {
return { const configInfo = {
enabled: this.config.enabled, enabled: this.config.enabled,
server: { server: {
url: this.config.server.url, url: this.config.server.url,
searchBase: this.config.server.searchBase, searchBase: this.config.server.searchBase,
searchFilter: this.config.server.searchFilter, searchFilter: this.config.server.searchFilter,
timeout: this.config.server.timeout timeout: this.config.server.timeout,
connectTimeout: this.config.server.connectTimeout
}, },
userMapping: this.config.userMapping userMapping: this.config.userMapping
} }
// 添加 TLS 配置信息(不包含敏感数据)
if (this.config.server.url.toLowerCase().startsWith('ldaps://') && this.config.server.tls) {
configInfo.server.tls = {
rejectUnauthorized: this.config.server.tls.rejectUnauthorized,
hasCA: !!this.config.server.tls.ca,
hasCert: !!this.config.server.tls.cert,
hasKey: !!this.config.server.tls.key,
servername: this.config.server.tls.servername
}
}
return configInfo
} }
} }