Apache、SSL 客户端证书、LDAP 授权

标签 apache ssl ldap

我在 serverfault.com 上发布了这个问题,但没有答案,所以我在这里尝试... 是否可以混合使用 mod_ssl 和 mod_auth_ldap,以便使用客户端证书完成身份验证并使用 mod_auth_ldap(需要 ldap-group)进行授权?如果是这样,你能给我一些指示吗?提前致谢

最佳答案

好吧,对于那些感兴趣的人,apache 需要 AuthType 指令的存在和某些模块对用户名的验证。

所以我编写了一个非常短的模块,它接受 AuthType Any 并接受任何用户名。

配置看起来像这样:

<Location /slaptest>
    Allow from all
    SSLVerifyClient require
    SSLVerifyDepth 1

    SSLUserName SSL_CLIENT_S_DN_CN

    AuthType Any
    AuthAnyAuthoritative on

    AuthLDAPURL "ldaps://vldap-rectech/ou=XXX,ou=YYY,o=ZZZ?cn"
    AuthzLDAPAuthoritative on
    AuthLDAPBindDN "cn=UUU,ou=Users,ou=XXX,ou=YYY,o=ZZZ"
    AuthLDAPBindPassword "******"
    AuthLDAPGroupAttributeIsDN on
    AuthLDAPGroupAttribute member
    AuthLDAPRemoteUserIsDN off
    Require valid-user
    Require ldap-group cn=ADMIN,ou=Groups,ou=XXX,ou=YYY,o=ZZZ
</Location>

更新:

模块代码如下。它定义了以下指令:

AuthAnyAuthoritative 开/关

AuthAnyCheckBasic 开/关

如果 AuthAnyCheckBasic 打开,模块将检查从证书中获取的用户名是否与授权 header 中的打开相匹配。

#include "apr_strings.h"
#include "apr_md5.h"            /* for apr_password_validate */
#include "apr_lib.h"            /* for apr_isspace */
#include "apr_base64.h"         /* for apr_base64_decode et al */
#define APR_WANT_STRFUNC        /* for strcasecmp */
#include "apr_want.h"

#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
#include "ap_provider.h"

#include "mod_auth.h"

typedef struct {
    int authoritative;
    int checkBasic;
} auth_any_config_rec;

static void *create_auth_any_dir_config(apr_pool_t *p, char *d)
{
    auth_any_config_rec *conf = apr_pcalloc(p, sizeof(*conf));

    /* Any failures are fatal. */
    conf->authoritative = 1;
    conf->checkBasic = 0;

    return conf;
}

static const command_rec auth_any_cmds[] =
{
    AP_INIT_FLAG("AuthAnyAuthoritative", ap_set_flag_slot,
                 (void *)APR_OFFSETOF(auth_any_config_rec, authoritative),
                 OR_AUTHCFG,
                 "Set to 'Off' to allow access control to be passed along to "
                 "lower modules if the UserID is not known to this module"),
    AP_INIT_FLAG("AuthAnyCheckBasic", ap_set_flag_slot,
                 (void *)APR_OFFSETOF(auth_any_config_rec, checkBasic),
                 OR_AUTHCFG,
                 "Set to 'On' to compare the username with the one in the "
                 "Authorization header"),
    {NULL}
};

module AP_MODULE_DECLARE_DATA auth_any_module;

static void note_basic_auth_failure(request_rec *r)
{
    apr_table_setn(r->err_headers_out,
                   (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
                                                   : "WWW-Authenticate",
                   apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r),
                               "\"", NULL));
}

/* Determine user ID, and check if it really is that user, for HTTP
 * basic authentication...
 */
static int authenticate_any_user(request_rec *r)
{
    auth_any_config_rec *conf = ap_get_module_config(r->per_dir_config,
                                                       &auth_any_module);

    /* Are we configured to be Basic auth? */
    const char *current_auth = ap_auth_type(r);
    if (!current_auth || strcasecmp(current_auth, "Any")) {
        return DECLINED;
    }

    if (!r->user) {
        return conf->authoritative ? HTTP_UNAUTHORIZED : DECLINED;
    }

    if (conf->checkBasic) {
        /* Get the appropriate header */
        const char *auth_line = apr_table_get(r->headers_in,
                (PROXYREQ_PROXY == r->proxyreq)
                        ? "Proxy-Authorization"
                        : "Authorization");

        if (!auth_line) {
            note_basic_auth_failure(r);
            return HTTP_UNAUTHORIZED;
        }

        if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
            /* Client tried to authenticate using wrong auth scheme */
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                          "client used wrong authentication scheme: %s", r->uri);
            note_basic_auth_failure(r);
            return HTTP_UNAUTHORIZED;
        }

        /* Skip leading spaces. */
        while (apr_isspace(*auth_line)) {
            auth_line++;
        }

        char *decoded_line = apr_palloc(r->pool, apr_base64_decode_len(auth_line) + 1);
        int length = apr_base64_decode(decoded_line, auth_line);
        /* Null-terminate the string. */
        decoded_line[length] = '\0';

        const char *user = ap_getword_nulls(r->pool, (const char**)&decoded_line, ':');

        if (strcasecmp(user, r->user)) {
            return HTTP_UNAUTHORIZED;
        }
    }

    r->ap_auth_type = "Any";
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
            "Accepting user: %s", r->user);

    return OK;
}

static void register_hooks(apr_pool_t *p)
{
    ap_hook_check_user_id(authenticate_any_user,NULL,NULL,APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA auth_any_module =
{
    STANDARD20_MODULE_STUFF,
    create_auth_any_dir_config,    /* dir config creater */
    NULL,                          /* dir merger --- default is to override */
    NULL,                          /* server config */
    NULL,                          /* merge server config */
    auth_any_cmds,                 /* command apr_table_t */
    register_hooks                 /* register hooks */
};

关于Apache、SSL 客户端证书、LDAP 授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7635380/

相关文章:

java - jersey 和 Resteasy 位于一个 servlet 容器中

apache - 如何在 CentOS 9 上使用 Let's Encrypt 保护 Apache?

ruby-on-rails-3 - 错误错误的请求行 - 强制 ssl - rails

java - Spring LDAP 身份验证(自动与否?)

apache - 在整个网站上启用 www + HTTPS

apache - XAMPP 端口自行更改

nginx - Nest.js GRPC 客户端在启用 SSL 连接到 NGINX 后面的服务器时使用代码 2 获取 RST_STREAM

php - 如何在 cURL 上使用 TLS 协议(protocol)?

windows - 使用 Windows 凭据进行 Web 登录

c# - .net 核心 System.DirectoryServices 不可用?