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

  第二步是修改端口,端口号越高越避免扫描。

$ sudo vi /etc/ssh/sshd_config
# 修改缺省监听端口
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图形方式:

$ sudo vi /etc/ssh/ssh_config
# Display fingerprint in hex and ASCII graphic when connecting
VisualHostKey yes

  至此,我们已经设置SSH监听在非标端口,root无法直接登录,授权password失效,基于SSH key进行授权。

2. 系统和网络

  如果你的服务器是基于SSD固态硬盘,那么在fstab文件中加入mount选项softdep和noatime可以提高磁盘性能,同时可以阻止文件属性 "last access time" 的写入:

$ sudo vi /etc/fstab
YourDiskDUID.b none swap sw
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的配置,:

$ sudo vi /etc/hostname.vio0
inet 你的服务器公共地址 网络netmask

  加入网关地址:

$ sudo vi /etc/mygate
服务器网关地址

  激活流量分发forward:

$ sudo sysctl net.inet.ip.forwarding=1
$ sudo vi /etc/sysctl.conf
net.inet.ip.forwarding=1

 

3. DNS

  我们可以使用DNSCrypt让我们的DNS请求更加加密安全,不绑定到任何本地DNS缓存:

$ export PKG_PATH=http://ftp.fr.openbsd.org/pub/OpenBSD/5.6/packages/amd64/
$ sudo pkg_add dnscrypt-proxy
$ sudo vi /etc/rc.local
# DNSCrypt
/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目录:

$ sudo vi /var/unbound/etc/unbound.conf
server:
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:

$ sudo vi /etc/resolv.conf
nameserver 127.0.0.1 # unbound 是在监听53端口

  运行Unbound,并且在系统启动时激活:

$ sudo /etc/rc.d/unbound start
$ sudo vi /etc/rc.conf.local
# Unbound
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规则可以做很多事情:

  1. 拒绝所有连接,除了SSH 和OpenVPN
  2. 保护SSH免于SYN flood和bruteforce攻击
  3. 探测公共端口扫描,并且加入24小时黑名单
  4. 允许VPN客户端发出DNS请求到本地unbound (使用dnscrypt)

  下面是一个配置样本:

$ sudo vi /etc/pf.conf
# "Being your own VPN provider with OpenBSD" 
# 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:

$ cd ~
$ mkdir scripts
$ cd scripts
$ sudo ./pf_badguys.sh
#!/bin/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"必须使用你的用户名替代:

$ sudo crontab -e
# add badguys to the pf table to be blocked
*/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数量等等:

$ sudo vi ./pf_show_tables.sh
#!/bin/sh
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总数。

现在你已经有一个相对安全的防火墙。

使用OpenBSD架设OpenVPN

Node.js+Shadowsocks建立科学上网代理