OPENBSD安全配置使用
OpenBSD号称是最安全的操作系统,可以用于保护个人上网隐私,比如VPN Shockscoks等服务器,特别是VPN提供商如果泄漏我们的个人隐私怎么办?另外还有各种黑客攻击如OS vulnerability, OpenSSL/Heartbleed, Shellshock等等。下面谈谈如何设置OPENBSD如何保护个人上网环境。
为了更加安全,为了更加容易更新升级系统和包,可以使用 M:Tier的openup:
$ ftp https://stable.mtier.org/openup
$ chmod +x openup
$ sudo openup
===> Checking for openup update
===> Installing/updating binpatch(es)
===> Updating package(s)
1.保护裸机
OpenBSD缺省设置非常安全,没有服务监听端口,除了SSH,SSH监听的是缺省22端口,可以设定服务器只接受来自你的计算机的公共地址,其他IP地址全部屏蔽:
$ sudo vi /etc/pf.conf
block in quick from ! x.x.x.x # 这里是你的公共IP地址
pass out quick
改变完成后,通过下面命令激活:
$ sudo pfctl -f /etc/pf.conf
下面是创建SSH key,失效root登录,使得SSH监听另外其他端口。首先是创建key,在Linux/BSD下客户端下可以如下使用-t ed25519,如果是Windows客户端,可以使用-t rsa
$ ssh-keygen -t ed25519
客户端产生Key以后,拷贝这个公共key到~/.ssh/authorized_keys:
$ cp ~/.ssh/id_ed25519.pub ~/.ssh/authorized_keys
复制你的私有key到远程服务器上,注意赋予600权限,设置正常后,可以通过下面命令连接正常:
ssh -i your_private_key your_server_ip
第二步是修改端口,端口号越高越避免扫描。
Port 21598
# Authentication
PasswordAuthentication yes # temporary
PermitRootLogin no
AllowUsers YOUR_USER
AuthorizedKeysFile .ssh/authorized_keys
AllowTcpForwarding no
UsePrivilegeSeparation sandbox # Default for new installations.
Subsystem sftp /usr/libexec/sftp-server
重新启动sshd:
$ sudo /etc/rc.d/sshd restart
再从客户端连接服务器:"ssh -i your_private_key your_server_ip",如果一切正常,我们失效密码登录,修改/etc/ssh/sshd_config行:
PasswordAuthentication no
再重新启动sshd,测试是否成功。下面我们配置ssh key以十进制hex格式连接,ASCII图形方式:
VisualHostKey yes
至此,我们已经设置SSH监听在非标端口,root无法直接登录,授权password失效,基于SSH key进行授权。
2. 系统和网络
如果你的服务器是基于SSD固态硬盘,那么在fstab文件中加入mount选项softdep和noatime可以提高磁盘性能,同时可以阻止文件属性 "last access time" 的写入:
YourDiskDUID.a / ffs rw,noatime,softdep 1 1
YourDiskDUID.k /home ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.d /tmp ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.f /usr ffs rw,nodev,noatime,softdep 1 2
YourDiskDUID.g /usr/X11R6 ffs rw,nodev,noatime,softdep 1 2
YourDiskDUID.h /usr/local ffs rw,nodev,noatime,softdep 1 2
YourDiskDUID.j /usr/obj ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.i /usr/src ffs rw,nodev,nosuid,noatime,softdep 1 2
YourDiskDUID.e /var ffs rw,nodev,nosuid,noatime,softdep 1 2
如果你的服务器缺省基于DHCP,我们可以修改为手工DNS,这样减少被控制可能,类似windows下网卡中IP的配置,:
加入网关地址:
激活流量分发forward:
$ sudo vi /etc/sysctl.conf
3. DNS
我们可以使用DNSCrypt让我们的DNS请求更加加密安全,不绑定到任何本地DNS缓存:
$ sudo pkg_add dnscrypt-proxy
$ sudo vi /etc/rc.local
/usr/local/sbin/dnscrypt-proxy -a 127.0.0.1:40 -u _dnscrypt-proxy -d -l /dev/null -R dnscrypt.eu-dk
你能以下面方式选择dnscrypt激活你的DNS服务器:
$ sudo /usr/local/sbin/dnscrypt-proxy -a 127.0.0.1:40 -u _dnscrypt-proxy -d -l /dev/null -R dnscrypt.eu-dk
现在我们配置和激活unbound,这已经包含在基本系统中,配置在/var/unbound目录:
username: _unbound
directory: /var/unbound
chroot: /var/unbound
do-not-query-localhost: no
interface: 127.0.0.1
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 10.8.0.0/24 allow
hide-identity: yes
hide-version: yes
auto-trust-anchor-file: "/var/unbound/db/root.key"
forward-zone:
name: "." # use for ALL queries
forward-addr: 127.0.0.1@40 # dnscrypt-proxy
不要忘记修改/etc/resolv.conf:
运行Unbound,并且在系统启动时激活:
$ sudo vi /etc/rc.conf.local
unbound_flags="-c /var/unbound/etc/unbound.conf"
测试你的DNS链是否正常工作:
$ host openbsd.org
openbsd.org has address 129.128.5.194
openbsd.org mail is handled by 6 shear.ucar.edu.
openbsd.org mail is handled by 10 cvs.openbsd.org.
Unbound监听在端口53,当连接上后分发到dnscrypt监听的端口40,再接触到外部dnscrypt激活的DNS服务器。
4.防火墙
pf规则可以做很多事情:
- 拒绝所有连接,除了SSH 和OpenVPN
- 保护SSH免于SYN flood和bruteforce攻击
- 探测公共端口扫描,并且加入24小时黑名单
- 允许VPN客户端发出DNS请求到本地unbound (使用dnscrypt)
下面是一个配置样本:
# Guillaume Kaddouch: http://networkfilter.blogspot.com/
# ruleset last modified: 2015 January 11
# VARIABLES, MACRO, AND TABLES
# ---------------------------------------------------------------------------------------
egress="vio0" # your server network interface, modify accordingly
vpn="tun0"
vpn_ip="10.8.0.1"
all_networks="0.0.0.0/0"
private_networks="10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16"
ssh_port="21598" # just a random example, modify to match your chosen SSH port
vpn_port="21600" # just a random example, modify to match your chosen OpenVPN port
# ports watchlist example, modify as you wish.
# Match against this will be put in "badguys" table below, by an external scheduled script
bad_ports="{ ftp-data, ftp, ssh, telnet, smtp, domain, http, pop3, ntp, netbios-ns, \
netbios-dgm, netbios-ssn, https, microsoft-ds, ms-sql-s, ms-sql-m, \
mysql, postgresql }"
table <internet> const { $all_networks, !self, !$private_networks }
table <myself> const { self }
table <bruteforce> persist
table <badguys> persist
# GLOBAL POLICY
# ---------------------------------------------------------------------------------------
block log all
set block-policy drop
set loginterface egress
set skip on lo
match in all scrub (no-df max-mss 1440 random-id)
block in log quick from <bruteforce> label "bruteforce"
block in log quick from <badguys> label "old_guys"
# DEFAULT TRAFFIC TAGGING
# --------------------------------------------------------------------------------
match in on egress proto tcp from <internet> to port $ssh_port match in on egress proto { udp tcp } from <internet> to port $bad_ports match in on egress proto udp to port $vpn_port match in on $vpn proto { icmp udp tcp } match in on $vpn proto { udp tcp } to $vpn_ip port domain match out on egress tagged VPN_TUN_IN match out on egress proto tcp from <myself> to port { http https } match out on egress proto { udp tcp } from <myself> to port domain match out on egress proto udp from <myself> to port https match out on egress proto udp from <myself> to port ntp match in on egress from { no-route urpf-failed } to any match out on egress from any to no-route match inet6 all |
tag SSH_IN tag BAD_GUYS tag VPN_EGRESS_IN tag VPN_TUN_IN tag VPN_DNS_IN tag VPN_FORWARD tag HTTP_OUT tag DNS_OUT tag DNS_OUT tag NTP_OUT tag BAD_PACKET tag BAD_PACKET tag IPV6 |
# POLICY ENFORCEMENT
# ---------------------------------------------------------------------------------------
match in tagged VPN_EGRESS_IN set tos lowdelay set prio 6
match out tagged VPN_FORWARD nat-to (egress) set prio 6
# Blocking spoofed or malformed packets, IPv6, and some bad traffic
antispoof log quick for egress label "antispoof"
block quick log tagged BAD_PACKET label "noroute_urpf"
block quick log tagged IPV6 label "ipv6"
block quick log tagged BAD_GUYS label "new_guy"
# Standard rules
# protect SSH from SYN flood and bruteforce
pass in quick tagged SSH_IN synproxy state \
(max-src-conn 10, max-src-conn-rate 5/5, overload <bruteforce> flush global)
# Redirect VPN clients DNS requests to unbound
pass in quick inet tagged VPN_DNS_IN rdr-to 127.0.0.1 port domain
pass in quick tagged VPN_EGRESS_IN
pass in quick tagged VPN_TUN_IN
pass out quick tagged HTTP_OUT
pass out quick tagged DNS_OUT
pass out quick tagged VPN_FORWARD modulate state
pass out quick tagged NTP_OUT
# no log for
block in quick proto udp from 0.0.0.0 port bootpc to port bootps
block in quick proto udp from any port bootps to 255.255.255.255 port bootpc
如果没有错误启动应用:
$ sudo pfctl -nf /etc/pf.conf
$ sudo pfctl -f /etc/pf.conf
现在我们可以制作一个脚本寻找被屏蔽的IP,前提是因为这些IP匹配我们检测的端口,标记为"new_guy"。脚本你在 /home/user/scripts:
$ mkdir scripts
$ cd scripts
$ sudo ./pf_badguys.sh
# Modify to match your server IP address and network interface
trusted="YOUR COMPUTER/CLIENT PUBLIC IP HERE"
ext_if="vio0"
label="new_guy"
file_temp="/home/your_user/badguys.txt"
file_clean="/home/your_user/badguys_clean.txt"
# Find rule number to look the logs for, we added a label in pf ruleset to locate it easily
# pf starts its numbering at rule 0 (tcpdump logs), while grep starts at rule 1
grep_number=`pfctl -sr | grep -n $label | cut -d":" -f1`
rule_number=$(($grep_number - 1))
echo "" > $file_temp
echo "" > $file_clean
# Look in all blocked traffic the IP blocked because of matching our ports watchlist (rule_number above)
tcpdump -enr /var/log/pflog | grep "rule $rule_number/(match) block in on $ext_if" | cut -d':' -f4 | cut -d' ' -f2 | \
awk '{split($0,ip,"."); print ip[1]"."ip[2]"."ip[3]"."ip[4]}' | sort | uniq >> $file_temp
# Remove our own trusted client IP we connect from!
sed -e "s/$trusted//g" $file_temp > $file_clean
cat $file_clean
# Add the new IP addresses in the badguys table
# pfctl handles by itself duplicate IPs and does not add an already existing one
pfctl -t badguys -T add -f $file_clean
编辑crontab 定期执行这个脚本,每小时检测一下是否有超过24小时的过期IP,下面脚本中"/home/your_user"必须使用你的用户名替代:
*/5 * * * * /home/your_user/scripts/pf_badguys.sh
# Clear pf tables
0 * * * * pfctl -t bruteforce -T expire 86400
0 * * * * pfctl -t badguys -T expire 86400
你也能使用另外一个脚本检测blacklist/badguys表的状态,比如屏蔽的端口,屏蔽IP数量等等:
ext_if="vio0"
label_new="new_guy"
label_old="old_guy"
BAD_TOTAL=`pfctl -t badguys -T show | wc -l | sed -e 's/^ *//' -e 's/ *$//'`
BRUTE_TOTAL=`pfctl -t bruteforce -T show | wc -l | sed -e 's/^ *//' -e 's/ *$//'`
BAD_NEW=`pfctl -sl | grep $label_new | cut -d' ' -f3`
BAD_OLD=`pfctl -sl | grep $label_old | cut -d' ' -f3`
grep_number=`pfctl -sr | grep -n $label_new | cut -d":" -f1`
rule_number=$(($grep_number - 1))
PORTS_SCAN=`tcpdump -enr /var/log/pflog | grep "rule $rule_number/(match) block in on $ext_if" | \
cut -d':' -f4 | cut -d' ' -f4 | awk '{split($0,ip,"."); print ip[5]}'| sort | uniq`
echo ""
echo -e "Table <badguys> total/first/retry : $BAD_TOTAL/$BAD_NEW/$BAD_OLD"
echo "ports blocked :"
echo "$PORTS_SCAN"
echo ""
echo -e "Table <bruteforce> : $BRUTE_TOTAL"
现在有两个脚本执行:下面是执行输出结果:
$ chmod +x ./*.sh
$ sudo pf_show_tables.sh
Table <badguys> total/first/retry : 486/71/82
ports blocked :
123
137
1433
21
22
23
25
3306
445
80
Table <bruteforce> : 0
- "total" 代表屏蔽黑名单的IP地址数量
- "first" 是匹配监视端口的IP数量,但是没有加入黑名单
- "retry"是匹配监视端口应用加入黑名单的数量
- "ports blocked"是被屏蔽加入黑名单的访问端口
- "bruteforce" 是连接我们的开放SSH端口太多次的IP总数。
现在你已经有一个相对安全的防火墙。