为Visual SVN Server添加认证用户IP功能

为Visual SVN Server添加认证用户IP功能

背景

内网环境下,某位大佬觉得Visual SVN Server的安全性不够,要求用户只能在指定的一个或几个IP上登录???

方案

内网环境下,Visual SVN Server使用https进行通信,有两种可以实现IP与用户绑定:

  • 做一个网关,用户访问SVN时,都通过网关进行访问,然后就可以在网关上进行IP与用户名校验了。由于使用的是https通信,网关配置https并转发到SVN Server比较麻烦,网关方式不进行实现。
  • 写一个apache module,在处理请求之前进行拦截校验

从SVN请求中提取用户名

SVN的每次请求都会带有AuthorizationHeader参数,其内容就是使用Base64编码的用户名和密码,其格式为:“Basic Base64(用户名:密码)”。因此,只需要解析Authorization字段就可以获取到用户名。无论是使用添加网关还是添加apache module,都比较容易取得Authorization参数。

主要实现

apache module实际上是一个动态库,apache httpd在启动的时候会动态加载该动态库,加载配置参数后正式进行工作,以下是主要模块的主要代码:

#include <stdio.h>
#include <http_protocol.h>
#include <httpd.h>
#include <http_config.h>
#include <apr_want.h>
#include <ap_config.h>
#include <apr_base64.h>
#include "host_bind_filter/host_bind_filter.h"

module AP_MODULE_DECLARE_DATA auth_host_bind_module;

static int auth_host_bind_handler(request_rec *r) {
    int len = 0;
    char* user = NULL;
    const char* authorization = apr_table_get(r->headers_in, "Authorization");
    // Basic Base64(用户名:密码)
    if(authorization != NULL && (authorization = strstr(authorization, "Basic ")) != NULL) {
        authorization += 6;// 跳过 "Basic ",然后进行base64解密

        len = apr_base64_decode_len(authorization);
        user = (char*)malloc(len);
        apr_base64_decode(user, authorization);
        // 通过user_host_filter函数进行用户名IP校验,其实现完整Demo.
        int res = user_host_filter(user, r->useragent_ip);
        free(user);
        show_request("host=%s, ip=%s", r->useragent_host, r->useragent_ip);
        if (res != 0) { // 如果校验不通过时,直接截断HTTP调用链
            ap_rprintf(r, "user cannot log in on the host[%s]", r->useragent_ip); // 写入返回信息
            return OK; // 返回OK,请求就截断了
        }
    } 

    return DECLINED; // 没有authorization参数或校验通过时,返回DECLINED,请求会继续进行,让Visual SVN Server处理。
}

static void register_hooks(apr_pool_t *p) {
    ap_hook_handler(auth_host_bind_handler, NULL, NULL, APR_HOOK_REALLY_FIRST); // APR_HOOK_REALLY_FIRST 表示在最前面进行IP认证处理。
}

typedef struct context_host_filename_config {
    char filename[1024];
} context_host_filename_config;

/* 读取设置字符串方法 */
static const char *set_context_host_filename(cmd_parms *cmd,
                                       void *mconfig,
                                       const char *name)
{
    context_host_filename_config *conf = (context_host_filename_config *) mconfig;
    strcpy_s(conf->filename, sizeof(conf->filename), name);
    load_context_host(name); // 加载用户IP绑定表,更好的方式是通过读取数据库来进行校验,这里使用cvs文件来绑定
    return NULL;
}

static const command_rec auth_context_host_filename_cmd[] =
        {
                AP_INIT_TAKE1("HostBindFileName", set_context_host_filename,   NULL, OR_FILEINFO,
                              "set HostBindFileName."),
                { NULL }
        };

/* init per dir */
static void *create_context_host_filename_config(apr_pool_t *p, char *d)
{
    context_host_filename_config *conf = (context_host_filename_config *)apr_pcalloc(p, sizeof(*conf));
    if(conf == NULL) return NULL;
    memset(conf->filename, 0, sizeof(conf->filename));
    return conf;
}

/* module structure */
module AP_MODULE_DECLARE_DATA
auth_host_bind_module = {
        STANDARD20_MODULE_STUFF,
        create_context_host_filename_config, /* dir config creater */
        NULL,                                /* dir merger — default is to override */
        NULL,                                /* server config */
        NULL,                                /* merge server configs */
        auth_context_host_filename_cmd,      /* command apr_table_t */
        register_hooks                       /* register hooks */
};

最后

Apache Module完整Demo源码

上一篇:node.js学习(二)--Node.js控制台(REPL)&&Node.js的基础和语法


下一篇:Orleans安装