后端配置

后端配置 #

配置安全模块的第一步是确定如何验证用户身份。尽管 Easysearch 本身可以充当一个内部用户数据库,但许多人更喜欢集成企业现有的身份认证体系,例如 LDAP/Active Directory 服务器,或两者组合。设置身份验证和授权服务端的主要配置文件位于 config/security/config.yml。它定义了安全模块如何检索用户凭据、如何验证这些凭据以及如何从后端系统获取其他角色(可选)。

config.yml 主要包含三大部分:

config:
  dynamic:
    http: ...
    authc: ...
    authz: ...

HTTP #

http 部分具有以下配置项:

http:
  anonymous_auth_enabled: <true|false>
  xff:
    enabled: <true|false>
    internalProxies: '<regex pattern>'
    remoteIpHeader: '<header name>'

匿名认证 #

设置 anonymous_auth_enabled 可以选择是否开启匿名访问。如果启用,当请求中未提供任何凭据时,用户将以 anonymous 身份通过认证,并被分配 anonymous_backendrole 角色。如果禁用匿名身份验证,则至少在 authc 里面提供一个认证后端,否则安全模块将不予初始化。默认为 false

提示: 如果启用匿名认证,所有 HTTP 认证器将不会发起 challenge(即不会返回 401 要求客户端提供凭据)。

XFF(X-Forwarded-For)配置 #

如果 Easysearch 位于一个或多个代理或负载均衡器之后,xff 部分可用于配置从 HTTP 头中解析客户端真实 IP 地址的行为。这对于 IP 速率限制和代理认证尤为重要。

参数描述默认值
enabled是否启用 XFF 解析false
internalProxies可信内部代理 IP 地址的正则表达式。只有来自匹配 IP 的请求才会解析 XFF 头。例如 192\.168\.0\.10\|192\.168\.0\.11。设置为 .* 信任所有代理。
remoteIpHeader包含客户端真实 IP 的 HTTP 请求头名称x-forwarded-for

示例:

http:
  anonymous_auth_enabled: false
  xff:
    enabled: true
    internalProxies: '192\.168\.0\.10|192\.168\.0\.11'
    remoteIpHeader: 'x-forwarded-for'

认证(authc) #

认证配置 authc 具有以下格式:

authc:
  <domain_name>:
    description: "<描述信息>"
    http_enabled: <true|false>
    transport_enabled: <true|false>
    order: <integer>
    http_authenticator:
      type: <type>
      challenge: <true|false>
      config:
        ...
    authentication_backend:
      type: <type>
      config:
        ...

配置 authc 里面的每一项被称为 身份验证域(Authentication Domain)。它指定了从何处获取用户凭据以及应针对哪个后端对它们进行身份验证。

您可以使用多个身份验证域。每个身份验证域都有一个名称(例如 basic_internal_auth_domain)、可选的 description 描述字段、http_enabled/transport_enabled 开关和排序参数 order。安全模块按 order 顺序依次使用它们。如果用户成功通过一个域进行了身份验证,安全模块将跳过剩余的验证域。

关于 Challenge #

challenge 参数决定当未提供凭据时,HTTP 认证器是否应该向客户端发起认证挑战(如返回 HTTP 401 响应和 WWW-Authenticate 头)。默认值为 true

注意: 如果定义了多个 HTTP 认证器,请将不发起 challenge 的认证器(如 proxyclientcertjwt)放在前面,将发起 challenge 的认证器(如 basic)放在最后。因为无法同时用两种不同的认证方法向客户端发起 challenge。

HTTP 认证器类型 #

http_authenticator 指定了在 HTTP 层上使用的身份验证方法。

类型说明Challenge备注
basicHTTP 基本身份验证(用户名/密码)最常用的认证方式
proxy代理认证,从 HTTP 请求头中提取用户信息需要配合 XFF
extended-proxy扩展代理认证,支持自定义属性头继承 proxy 的所有功能
clientcert通过客户端 TLS 证书进行身份验证需要 HTTPS
kerberos通过 Kerberos/SPNEGO 进行身份验证需要企业功能
jwt通过 JSON Web Token 进行身份验证需要企业功能
openid通过 OpenID Connect 进行身份验证需要企业功能
saml通过 SAML 进行身份验证需要企业功能

Basic 认证 #

HTTP 基本身份验证是最简单也是最常用的认证方式。它从 HTTP Authorization 请求头中提取用户名和密码。

http_authenticator:
  type: basic
  challenge: true

无需额外配置。通常与 internalldap 认证后端配合使用。

代理认证(Proxy) #

代理认证用于当 Easysearch 位于一个已经完成用户认证的代理之后的场景。代理通过 HTTP 请求头将已认证的用户名和角色传递给 Easysearch。

注意: 使用代理认证时,必须先启用 XFF(xff.enabled: true),并正确配置 internalProxies 以限定可信代理的 IP 范围。否则任何人都可以通过伪造请求头来冒充用户。

proxy_auth_domain:
  http_enabled: true
  transport_enabled: true
  order: 3
  http_authenticator:
    type: proxy
    challenge: false
    config:
      user_header: "x-proxy-user"
      roles_header: "x-proxy-roles"
      roles_separator: ","
  authentication_backend:
    type: noop
参数描述默认值
user_header包含已认证用户名的 HTTP 请求头
roles_header包含用户角色(逗号分隔)的 HTTP 请求头
roles_separator角色分隔符,

扩展代理认证(Extended Proxy) #

扩展代理认证继承了代理认证的所有功能,并额外支持通过 HTTP 请求头传递自定义用户属性。

http_authenticator:
  type: extended-proxy
  challenge: false
  config:
    user_header: "x-proxy-user"
    roles_header: "x-proxy-roles"
    attr_header_prefix: "x-proxy-ext-"
参数描述默认值
attr_header_prefix自定义属性头的前缀。所有以此前缀开头的请求头都会被提取为用户属性。

例如,如果 attr_header_prefix 设置为 x-proxy-ext-,则请求头 x-proxy-ext-department: engineering 将为用户添加属性 attr.proxy.department=engineering

客户端证书认证(ClientCert) #

通过客户端 TLS 证书进行身份验证。客户端证书必须受节点信任库中的根 CA 之一的信任。

clientcert_auth_domain:
  http_enabled: true
  transport_enabled: true
  order: 2
  http_authenticator:
    type: clientcert
    challenge: false
    config:
      username_attribute: cn
  authentication_backend:
    type: noop
参数描述默认值
username_attribute从客户端证书 DN 中提取用户名所使用的属性。例如 cnou 等。如果不设置,则使用完整的 DN 作为用户名。无(使用完整 DN)
roles_attribute从客户端证书 DN 中提取角色所使用的属性。

JWT 认证 #

通过 JSON Web Token(JWT)进行身份验证。JWT 通常由外部身份提供商签发,Easysearch 通过验证签名来确认用户身份。

jwt_auth_domain:
  http_enabled: true
  transport_enabled: true
  order: 0
  http_authenticator:
    type: jwt
    challenge: false
    config:
      signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key"
      jwt_header: "Authorization"
      jwt_url_parameter: null
      roles_key: null
      subject_key: null
  authentication_backend:
    type: noop
参数描述默认值
signing_key用于验证 JWT 签名的密钥。对于 HMAC 使用 Base64 编码的密钥,对于 RSA/ECDSA 使用 PEM 格式的公钥。无(必填)
jwt_header包含 JWT 的 HTTP 请求头名称Authorization
jwt_url_parameter如果 JWT 通过 URL 参数传递,指定参数名
roles_keyJWT payload 中包含角色的字段名
subject_keyJWT payload 中包含用户名的字段名无(使用 sub

Kerberos 认证 #

通过 Kerberos/SPNEGO 协议进行身份验证,常用于与 Active Directory 集成的企业环境。

kerberos_auth_domain:
  http_enabled: true
  transport_enabled: true
  order: 6
  http_authenticator:
    type: kerberos
    challenge: true
    config:
      krb_debug: false
      strip_realm_from_principal: true
  authentication_backend:
    type: noop
参数描述默认值
krb_debug是否输出 Kerberos/安全相关的调试日志到标准输出false
strip_realm_from_principal是否从 Kerberos principal 中去除 realm 部分。例如将 user@REALM.COM 简化为 usertrue

认证后端类型 #

authentication_backend 指定了对 HTTP 认证器提取的凭据进行验证的后端系统。

类型说明
noop不进行进一步的身份验证。适用于 HTTP 认证器已经完全验证了用户身份的情况,例如 JWT、Kerberos、客户端证书或代理认证。
internal使用 user.yml 中定义的内部用户数据库进行认证。
ldap使用 LDAP 或 Active Directory 进行认证。

Internal 认证后端 #

使用 Easysearch 内置的用户数据库(config/security/user.yml)进行认证。这是最简单的认证后端,适合用户数量较少或测试环境。

authentication_backend:
  type: internal

无需额外配置。

LDAP 认证后端 #

使用 LDAP 或 Active Directory 对用户进行身份验证。

ldap_auth_domain:
  http_enabled: true
  transport_enabled: true
  order: 1
  http_authenticator:
    type: basic
    challenge: true
  authentication_backend:
    type: ldap
    config:
      enable_ssl: false
      enable_start_tls: false
      enable_ssl_client_auth: false
      verify_hostnames: false
      hosts:
        - localhost:389
      bind_dn: "cn=admin,dc=example,dc=com"
      password: "password"
      userbase: 'ou=people,dc=example,dc=com'
      usersearch: '(uid={0})'
      username_attribute: null

连接配置:

参数描述默认值
hostsLDAP 服务器地址列表(host:port 格式)无(必填)
bind_dn用于连接 LDAP 服务器的绑定 DN
password绑定 DN 的密码
connect_timeout连接超时时间(毫秒)
response_timeout响应超时时间(毫秒)

SSL/TLS 配置:

参数描述默认值
enable_ssl是否启用 LDAPSfalse
enable_start_tls是否启用 StartTLS(enable_ssl 应为 falsefalse
enable_ssl_client_auth是否发送客户端证书false
verify_hostnames是否验证 LDAP 服务器主机名false
trust_all是否信任所有 LDAP 服务器证书false
cert_alias客户端证书别名(JKS)
ca_aliasCA 证书别名(JKS)
key_file客户端私钥文件路径(PEM)
cert_file客户端证书文件路径(PEM)
ca_fileCA 证书文件路径(PEM)
enabled_ssl_ciphers允许的 SSL 加密套件
enabled_ssl_protocols允许的 SSL 协议

用户搜索配置:

参数描述默认值
userbase用户搜索的基础 DN无(必填)
usersearch搜索用户的 LDAP 过滤器。{0} 将被替换为用户名。无(必填)
username_attribute使用用户条目中的此属性作为用户名。如果不设置则使用 DN。
search_all_bases是否在所有基础 DN 下搜索false

授权(authz) #

对用户进行身份验证后,安全模块可以选择从后端权限系统收集用户的其他角色。

授权配置 authz 具有以下格式:

authz:
  <domain_name>:
    description: "<描述信息>"
    http_enabled: <true|false>
    transport_enabled: <true|false>
    authorization_backend:
      type: <type>
      config: ...

您可以在本节中定义多个条目。在授权阶段,执行顺序不相关,因此没有 order 字段。

授权后端类型 #

类型说明
noop跳过额外的角色收集。
ldap从 LDAP 或 Active Directory 获取用户的后端角色。

LDAP 授权后端 #

从 LDAP 或 Active Directory 获取用户的角色信息。

authz:
  roles_from_ldap:
    http_enabled: true
    transport_enabled: true
    authorization_backend:
      type: ldap
      config:
        enable_ssl: false
        enable_start_tls: false
        enable_ssl_client_auth: false
        verify_hostnames: false
        hosts:
          - localhost:389
        bind_dn: "cn=admin,dc=example,dc=com"
        password: "password"
        rolebase: 'ou=groups,dc=example,dc=com'
        rolesearch: '(member={0})'
        userroleattribute: null
        userrolename: disabled
        rolename: cn
        resolve_nested_roles: true
        userbase: 'ou=people,dc=example,dc=com'
        usersearch: '(uid={0})'

LDAP 授权的连接和 SSL/TLS 配置与 LDAP 认证后端 相同。

角色搜索配置:

参数描述默认值
rolebase角色搜索的基础 DN无(必填)
rolesearch搜索角色的 LDAP 过滤器。{0} 将被替换为用户的 DN,{1} 替换为用户名,{2} 替换为 userroleattribute 指定的属性值。无(必填)
userroleattribute用户条目中包含角色信息的属性名。其值将替换 rolesearch 中的 {2}
userrolename用户条目中直接包含角色名的属性(如 memberOf)。设置为 disabled 禁用。disabled
rolename角色条目中包含角色名称的属性。也可以设置为 dn 使用完整 DN 作为角色名。name
resolve_nested_roles是否递归解析嵌套角色(角色的角色)true
skip_users跳过匹配的用户,支持通配符和正则表达式
rolesearch_enabled是否启用角色搜索true
nested_role_filter嵌套角色过滤器
max_nested_depth最大嵌套角色解析深度
custom_attr_maxval_len自定义属性最大值长度
custom_attr_whitelist允许的自定义属性白名单

认证失败监听器(auth_failure_listeners) #

安全模块支持配置认证失败监听器,用于实现基于 IP 或用户名的速率限制,防止暴力破解攻击。

config:
  dynamic:
    auth_failure_listeners:
      ip_rate_limiting:
        type: ip
        allowed_tries: 10
        time_window_seconds: 3600
        block_expiry_seconds: 600
        max_blocked_clients: 100000
        max_tracked_clients: 100000
      internal_authentication_backend_limiting:
        type: username
        authentication_backend: internal
        allowed_tries: 10
        time_window_seconds: 3600
        block_expiry_seconds: 600
        max_blocked_clients: 100000
        max_tracked_clients: 100000

IP 速率限制 #

基于 IP 地址的速率限制,无论使用哪个用户名,只要某个 IP 地址在指定时间窗口内认证失败次数超过阈值,就会被临时封禁。

参数描述默认值
type设置为 ip无(必填)
allowed_tries在被封禁前允许的失败尝试次数10
time_window_seconds计数失败尝试的时间窗口(秒)3600
block_expiry_seconds封禁持续时间(秒)600
max_blocked_clients最大被封禁客户端数量100000
max_tracked_clients最大追踪客户端数量100000

用户名速率限制 #

基于用户名的速率限制,针对特定认证后端的用户名进行限制。

参数描述默认值
type设置为 username无(必填)
authentication_backend要限制的认证后端名称(如 internal无(必填)
allowed_tries在被封禁前允许的失败尝试次数10
time_window_seconds计数失败尝试的时间窗口(秒)3600
block_expiry_seconds封禁持续时间(秒)600
max_blocked_clients最大被封禁客户端数量100000
max_tracked_clients最大追踪客户端数量100000

完整配置示例 #

以下是一个包含多种认证方式的完整配置示例:

_meta:
  type: "config"
  config_version: 2

config:
  dynamic:
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: true
        internalProxies: '192\.168\.0\.10|192\.168\.0\.11'
        remoteIpHeader: 'x-forwarded-for'
    authc:
      basic_internal_auth_domain:
        description: "Authenticate via HTTP Basic against internal users database"
        http_enabled: true
        transport_enabled: true
        order: 4
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: internal
      proxy_auth_domain:
        description: "Authenticate via proxy"
        http_enabled: false
        transport_enabled: false
        order: 3
        http_authenticator:
          type: proxy
          challenge: false
          config:
            user_header: "x-proxy-user"
            roles_header: "x-proxy-roles"
        authentication_backend:
          type: noop
      jwt_auth_domain:
        description: "Authenticate via Json Web Token"
        http_enabled: false
        transport_enabled: false
        order: 0
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key"
            jwt_header: "Authorization"
            jwt_url_parameter: null
            roles_key: null
            subject_key: null
        authentication_backend:
          type: noop
      clientcert_auth_domain:
        description: "Authenticate via SSL client certificates"
        http_enabled: false
        transport_enabled: false
        order: 2
        http_authenticator:
          type: clientcert
          challenge: false
          config:
            username_attribute: cn
        authentication_backend:
          type: noop
      ldap_auth_domain:
        description: "Authenticate via LDAP or Active Directory"
        http_enabled: false
        transport_enabled: false
        order: 1
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: ldap
          config:
            enable_ssl: false
            enable_start_tls: false
            enable_ssl_client_auth: false
            verify_hostnames: false
            hosts:
              - localhost:389
            bind_dn: "cn=admin,dc=example,dc=com"
            password: "password"
            userbase: 'ou=people,dc=example,dc=com'
            usersearch: '(uid={0})'
            username_attribute: null
    authz:
      basic_authz:
        http_enabled: true
        transport_enabled: true
        authorization_backend:
          type: noop
      roles_from_ldap:
        description: "Authorize via LDAP or Active Directory"
        http_enabled: false
        transport_enabled: false
        authorization_backend:
          type: ldap
          config:
            enable_ssl: false
            enable_start_tls: false
            enable_ssl_client_auth: false
            verify_hostnames: false
            hosts:
              - localhost:389
            bind_dn: "cn=admin,dc=example,dc=com"
            password: "password"
            rolebase: 'ou=groups,dc=example,dc=com'
            rolesearch: '(member={0})'
            userroleattribute: null
            userrolename: disabled
            rolename: cn
            resolve_nested_roles: true
            userbase: 'ou=people,dc=example,dc=com'
            usersearch: '(uid={0})'
    auth_failure_listeners:
      ip_rate_limiting:
        type: ip
        allowed_tries: 10
        time_window_seconds: 3600
        block_expiry_seconds: 600
        max_blocked_clients: 100000
        max_tracked_clients: 100000
      internal_authentication_backend_limiting:
        type: username
        authentication_backend: internal
        allowed_tries: 10
        time_window_seconds: 3600
        block_expiry_seconds: 600
        max_blocked_clients: 100000
        max_tracked_clients: 100000