前面几节介绍了整体架构和Open×××/IPtables的主要方向。本节将会增加一些干货,具体来分析Connect/Disconnect脚本以及相关的配置数据库的设计,跑通整个登录和访问权限设置的流程。
1首先,介绍一下connect/disconnect能够使用的环境变量。完整的环境变量列表可以manopen***,然后查看Environmental Variables这一段的内容。这里我挑了一些常用的和特别需要关注的。
bytes_received:接收到的字节数 bytes_sent:发送的字节数 这两个变量可以用来作为open***计费用。你可以很容易的计算一个用户的流量,唯一不方便的是,这个值只有在disconnect之后才能够获取到。 common_name:X509格式的用户名 ifconfig_pool_remote_ip:客户端获得的地址 password:用户密码 trusted_ip:客户端请求登陆时的地址 username:用户名 |
你需要在connect/disconnect脚本里面使用${Variable name}来调用相关的参数。
2一个简单的配置数据库的结构说明如下。
user/user_group_names/network_group_names/port_group_names定义各种名字;user_group/network_group/port_group定义相关的组;Rules表,定义具体规则。
相关的字段的意义都比较直白。
建表脚本如下:
open***.sql CREATE DATABASE`open***`; USE `open***`; CREATE TABLE `user`( `user_id` bigint(20) NOT NULL AUTO_INCREMENT, `user_name` varchar(255) NOT NULL, `full_name` varchar(255) NOT NULL, PRIMARY KEY (`user_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `user_group_names`( `user_group_name_id` bigint(20) NOT NULL AUTO_INCREMENT, `user_group_name` varchar(255) NOT NULL, PRIMARY KEY (`user_group_name_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `user_group`( `user_group_id` bigint(20) NOT NULL AUTO_INCREMENT, `user_group_name_id` bigint(20) NOT NULL, `user_id` bigint(20) NOT NULL, PRIMARY KEY (`user_group_id`), UNIQUE KEY `user_group_uq` (`user_group_name_id`,`user_id`), FOREIGN KEY `user_group_user_id_fkey` (`user_id`) REFERENCES `user`(`user_id`), FOREIGN KEY `user_group_user_group_name_id_fkey` (`user_group_name_id`) REFERENCES `user_group_names`(`user_group_name_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `network_group_names`( `network_group_name_id` bigint(20) NOT NULL AUTO_INCREMENT, `network_group_name` varchar(255) NOT NULL, PRIMARY KEY (`network_group_name_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `network_group`( `network_group_id` bigint(20) NOT NULL AUTO_INCREMENT, `network_group_name_id` bigint(20) NOT NULL, `network` varchar(15) NOT NULL, `netmask` int NOT NULL, PRIMARY KEY (`network_group_id`), UNIQUE KEY `network_group_uq` (`network_group_name_id`,`network`,`netmask`), FOREIGN KEY `network_group_network_group_name_id_fkey` (`network_group_name_id`) REFERENCES `network_group_names`(`network_group_name_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `port_group_names`( `port_group_name_id` bigint(20) NOT NULL AUTO_INCREMENT, `port_group_name` varchar(255) NOT NULL, PRIMARY KEY (`port_group_name_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `port_group`( `port_group_id` bigint(20) NOT NULL AUTO_INCREMENT, `port_group_name_id` bigint(20) NOT NULL, `port_type` enum('TCP','UDP','ICMP'), `start_port` int NOT NULL, `end_port` int NOT NULL, PRIMARY KEY (`port_group_id`), UNIQUE KEY `port_group_uq` (`port_group_name_id`,`port_type`,`start_port`,`end_port`), FOREIGN KEY `port_group_network_group_name_id_fkey` (`port_group_name_id`) REFERENCES `port_group_names`(`port_group_name_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `rules` ( `rule_id` bigint(20) NOT NULL AUTO_INCREMENT, `user_group_name_id` bigint(20) NOT NULL, `network_group_name_id` bigint(20) NOT NULL, `port_group_name_id` bigint(20) NOT NULL, PRIMARY KEY (`rule_id`), UNIQUE KEY `rule_uq` (`user_group_name_id`,`network_group_name_id`,`port_group_name_id`), FOREIGN KEY `rule_user_group_name_fkey` (user_group_name_id) REFERENCES user_group_names(user_group_name_id), FOREIGN KEY `rule_network_group_name_fkey` (network_group_name_id) REFERENCES network_group_names(network_group_name_id), FOREIGN KEY `port_network_group_name_fkey` (port_group_name_id) REFERENCES port_group_names(port_group_name_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
为了能够对系统进行测试,我们插入一些测试数据。
INSERT INTO `port_group_names` VALUES (1,'RemoteControl'), (2,'Web'); INSERT INTO `port_group` VALUES (1,1,'TCP',22,22), (2,1,'TCP',3389,3389), (3,2,'TCP',80,80), (4,2,'TCP',443,443); INSERT INTO `network_group_names` VALUES (1,'AllProduct'), (2,'WebSite'); INSERT INTO `network_group` VALUES (1,1,'10.0.0.0',8), (2,2,'10.1.1.0',24), (3,2,'10.1.2.0',24); INSERT INTO `user` VALUES (1,'admin','Admin User'), (2,'testuser','Test User'); INSERT INTO `user_group_names` VALUES (1,'admin'), (2,'user'); INSERT INTO `user_group` VALUES (1,1,1), (2,2,2); INSERT INTO `rules` VALUES (1,1,1,1), (2,1,1,2), (3,2,2,2); |
3 系统初始化。系统启动时,IPTABLES需要先初始化相关的规则。这样当用户登录时,只需要把该用户的IP地址加入到UserGroup Chain中即可。本例仅仅使用Bash来实现这一功能,Bash对于Mysql的操作界面不是很友好,你也可以使用其它语言例如perl python或者C来实现。
#!/bin/sh #首先需要清理自定义的所有Chain;清理Chain是有顺序要求的,引用其它Chain的Chain必须首先被清除,否则被引用的Chain无法清除;本例的顺序为清空FORWARD链->清空和删除User Group相关链->清空和删除Rule相关链->清空和删除Port相关链 echo "Clean All Chains" iptables -F FORWARD iptables –I FORWARD –j DROP user_group_chains=(`iptables -L -n | grep 'Chain user_group_' | awk '{print $2}'`) let "user_group_chain_length=${#user_group_chains[@]}" for (( user_group_chain_idx=0 ; user_group_chain_idx<$user_group_chain_length ; user_group_chain_idx++ )) do user_group_chain_name=${user_group_chains[$user_group_chain_idx]} iptables -F $user_group_chain_name iptables -X $user_group_chain_name done echo ">>Clean "$user_group_chain_length" Chains" rule_chains=(`iptables -L -n | grep 'Chain rule_' | awk '{print $2}'`) let "rule_chain_length=${#rule_chains[@]}" for (( rule_chain_idx=0 ; rule_chain_idx<$rule_chain_length ; rule_chain_idx++ )) { rule_chain_name=${rule_chains[$rule_chain_idx]} iptables -F $rule_chain_name iptables -X $rule_chain_name } echo ">>Clean "$rule_chain_length" Chains" port_group_chains=(`iptables -L -n | grep 'Chain port_group_' | awk '{print $2}'`) let "port_group_chain_length=${#port_group_chains[@]}" for (( port_group_chain_idx=0 ; port_group_chain_idx<$port_group_chain_length ; port_group_chain_idx++ )) { port_group_chain_name=${port_group_chains[$port_group_chain_idx]} iptables -F $port_group_chain_name iptables -X $port_group_chain_name } echo ">>Clean "$rule_chain_length" Chains" #初始化各个Port Chain #获取Port List echo "Init Port Chains" port_list=(`mysql -uroot open***<<EOF select port_group_name_id,port_type,start_port,end_port from port_group order by port_group_name_id; EOF`); let "port_idx_length=${#port_list[@]}/4" for (( port_idx=1 ; port_idx<$port_idx_length ; port_idx++ )) #mysql命令返回的第一行是列名,需要取除 do let "port_group_name_id_idx=( $port_idx * 4 )" let "port_type_idx=( $port_idx * 4 ) + 1" let "start_port_idx=( $port_idx * 4 ) + 2" let "end_port_idx=( $port_idx * 4 ) + 3" port_group_name_id=${port_list[$port_group_name_id_idx]} port_type=${port_list[$port_type_idx]} start_port=${port_list[$start_port_idx]} end_port=${port_list[$end_port_idx]} port_group_chain_name="port_group_"$port_group_name_id iptables -L $port_group_chain_name -n 1>/dev/null 2>/dev/null if [ $? == 1 ] then #当chain不存在时,创建之,默认处理方式为DROP echo ">>Create The Port Group Chain "$port_group_chain_name iptables -N $port_group_chain_name iptables -I $port_group_chain_name -j DROP fi #插入规则 iptables -I $port_group_chain_name -p $port_type --dport=$start_port:$end_port -j ACCEPT done #初始化Rule Chain #获取Rule List echo "Init Rule Chains" rule_list=(`mysql -uroot open***<<EOF select rule_id,network,netmask,port_group_name_id,user_group_name_id from rules a,network_group b where a.network_group_name_id=b.network_group_id order by user_group_name_id EOF`); let "rule_idx_length=${#rule_list[@]}/5" for (( rule_idx=1 ; rule_idx<rule_idx_length ; rule_idx++ )) #mysql命令返回的第一行是列名,需要取除 do let "rule_id_idx=( $rule_idx * 5 )" let "network_idx=( $rule_idx * 5 ) + 1" let "netmask_idx=( $rule_idx * 5 ) + 2" let "port_group_name_id_idx=( $rule_idx * 5 ) + 3" let "user_group_name_id_idx=( $rule_idx * 5 ) + 4" rule_id=${rule_list[$rule_id_idx]} network=${rule_list[$network_idx]} netmask=${rule_list[$netmask_idx]} port_group_name_id=${rule_list[$port_group_name_id_idx]} user_group_name_id=${rule_list[$user_group_name_id_idx]} port_group_chain_name="port_group_"$port_group_name_id user_group_chain_name="user_group_"$user_group_name_id rule_chain_name="rule_"$rule_id iptables -L $rule_chain_name -n 1>/dev/null 2>/dev/null if [ $? == 1 ] then #当Rule chain不存在时,创建之,默认处理方式为DROP echo ">>Create The Rule Chain "$rule_chain_name iptables -N $rule_chain_name iptables -I $rule_chain_name -j DROP iptables -L $user_group_chain_name -n 1>/dev/null 2>/dev/null if [ $? == 1 ] then #当User Group chain不存在时,创建之,默认处理方式为DROP echo ">>Create The User Group Chain "$user_group_chain_name iptables -N $user_group_chain_name iptables -I $user_group_chain_name -j DROP fi iptables -I $user_group_chain_name -j $rule_chain_name fi #插入规则 echo ">>>>Insert Rule To Chain "$rule_chain_name iptables -I $rule_chain_name -s $network/$netmask -j $port_group_chain_name done |
这时我们可以看一下IPTABLES创建的规则列表:
我们可以看到此时FORWARD Chain应该只有一条DROP的默认规则。
Chain FORWARD (policy ACCEPT) targetprot opt sourcedestination DROPall--0.0.0.0/00.0.0.0/0 Chain port_group_1 (1 references) targetprot opt sourcedestination ACCEPTtcp--0.0.0.0/00.0.0.0/0tcp dpt:3389 ACCEPTtcp--0.0.0.0/00.0.0.0/0tcp dpt:22 DROPall--0.0.0.0/00.0.0.0/0 Chain port_group_2 (2 references) targetprot opt sourcedestination ACCEPTtcp--0.0.0.0/00.0.0.0/0tcp dpt:443 ACCEPTtcp--0.0.0.0/00.0.0.0/0tcp dpt:80 DROPall--0.0.0.0/00.0.0.0/0 Chain rule_1 (1 references) targetprot opt sourcedestination port_group_1all--10.0.0.0/80.0.0.0/0 DROPall--0.0.0.0/00.0.0.0/0 Chain rule_2 (1 references) targetprot opt sourcedestination port_group_2all--10.0.0.0/80.0.0.0/0 DROPall--0.0.0.0/00.0.0.0/0 Chain rule_3 (1 references) targetprot opt sourcedestination port_group_2all--10.1.1.0/240.0.0.0/0 DROPall--0.0.0.0/00.0.0.0/0 Chain user_group_1 (0 references) targetprot opt sourcedestination rule_2all--0.0.0.0/00.0.0.0/0 rule_1all--0.0.0.0/00.0.0.0/0 DROPall--0.0.0.0/00.0.0.0/0 Chain user_group_2 (0 references) targetprot opt sourcedestination rule_3all--0.0.0.0/00.0.0.0/0 DROPall--0.0.0.0/00.0.0.0/0 |
4 Connect
前面介绍了IPTables初始化的工作。我们在这里假设用户已经正常通过验证,开始connect脚本了。
#!/bin/bash username=${common_name} tunnelip=${ifconfig_pool_remote_ip} [ "x$username" == 'x' ] && exit 0 [ "x$tunnelip" == 'x' ] && exit 0 #首先需要清理自定义的所有该IP相关Chain echo "Clean All Chains" user_chains=(`iptables -L -n | grep $tunnelip | awk '{print $1" "$4}'`) let "user_chain_length=${#user_chains[@]}/2" for (( user_chain_idx=0 ; user_chain_idx < $user_chain_length ; user_chain_idx++ )) do let "chain_name_idx=user_chain_idx * 2" let "ip_idx=( user_chain_idx * 2 ) + 1 " chain_name=${user_chains[$chain_name_idx]} ip=${user_chains[$ip_idx]} iptables -D FORWARD -s $ip/32 -j $chain_name done #创建用户相关规则 user_groups=(`mysql -uroot open***<<EOF select user_group_name_id from user a,user_group b where a.user_name="$username" and b.user_id=a.user_id EOF`) let "user_group_length=${#user_groups[@]}" for (( user_group_idx=1 ; user_group_idx<$user_group_length ; user_group_idx++ )) do user_group_chain_name="user_group_"${user_groups[$user_group_idx]} iptables -I FORWARD -s $tunnelip/32 -j $user_group_chain_name done |
假设我们以admin用户登录,Open×××分配的IP地址为172.18.1.1。用户登录Open×××
之后,IPTABLES的FILTER/FORWARD Chain增加如下规则:
Chain FORWARD (policy ACCEPT) targetprot opt sourcedestination user_group_1 all -- 172.18.1.10.0.0.0/0 DROP all -- 0.0.0.0/0 0.0.0.0/0 |
5 Disconnect
用户正常登出后,应该清除用户所属IP的相关规则,这实际上是connect脚本的上半段。
#!/bin/bash username=${common_name} tunnelip=${ifconfig_pool_remote_ip} [ "x$username" == 'x' ] && exit 0 [ "x$tunnelip" == 'x' ] && exit 0 #清理自定义的所有该IP相关Chain echo "Clean All Chains" user_chains=(`iptables -L -n | grep $tunnelip | awk '{print $1" "$4}'`) let "user_chain_length=${#user_chains[@]}/2" for (( user_chain_idx=0 ; user_chain_idx < $user_chain_length ; user_chain_idx++ )) do let "chain_name_idx=user_chain_idx * 2" let "ip_idx=( user_chain_idx * 2 ) + 1 " chain_name=${user_chains[$chain_name_idx]} ip=${user_chains[$ip_idx]} iptables -D FORWARD -s $ip/32 -j $chain_name done |
前面用户登出之后,FORWARD Chain中的记录被清空:
Chain FORWARD (policy ACCEPT) targetprot opt sourcedestination DROP all -- 0.0.0.0/00.0.0.0/0 |
本节详细介绍了Connect/Disconnect动作的所有操作。你还可以在Connect和Disconnect脚本中插入流量或者计费的功能,这里我就不赘述了。所有脚本都在附件的压缩包里。
转载于:https://blog.51cto.com/wuliyasutai/1350981