内网环境下,某位大佬觉得Visual SVN Server的安全性不够,要求用户只能在指定的一个或几个IP上登录???
内网环境下,Visual SVN Server使用https进行通信,有两种可以实现IP与用户绑定:
SVN的每次请求都会带有Authorization
Header参数,其内容就是使用Base64编码的用户名和密码,其格式为:“Basic Base64(用户名:密码)”。因此,只需要解析Authorization
字段就可以获取到用户名。无论是使用添加网关还是添加apache module,都比较容易取得Authorization
参数。
apache module实际上是一个动态库,apache httpd在启动的时候会动态加载该动态库,加载配置参数后正式进行工作,以下是主要模块的主要代码:
#include
#include
#include
#include
#include
#include
#include
#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 */
};
编译成功后,将生成的mod_auth_host_bind.dll
重命名为mod_auth_host_bind.so
,将其拷贝到${svn_root}/bin
目录中, ${svn_root}
为Visrual SVN Server的安装目录,默认路径为:C:\Program Files\VisualSVN Server
。
修改${svn_root}/conf/httpd.conf
配置文件,在第一个LoadModule***
上方添加一行:
LoadModule auth_host_bind_module bin/mod_auth_host_bind.so
${svn_root}/conf/httpd.conf
配置文件,在
节点下添加绑定文件配置:
AuthName "VisualSVN Server"
AuthType Basic
AuthBasicProvider file
AuthUserFile "E:/Repositories/htpasswd"
HostBindFileName "E:/Repositories/HostBind.csv" # 这是添加的,其他的保持不变
Require valid-user
HostBind.csv
文件结构示例:
D00371,192.168.1.103
D00371,192.168.1.102
Apache Module完整Demo源码