推弹的打击让大量极右、纳粹之类的互联网垃圾涌向了Fediverse,为了避免被这些肮脏的家伙沾上,我想我们很有必要来好好保护一下我们喵窝的Misskey服务。
对于版本 ≥1.19.4 的 nginx ,后文会提到一种更为简便的保护方案。
首先是配置nginx的默认证书。由于nginx的策略是没有被后续 server_name 捕获的请求都会被送往 default 文件中默认的 server_name _;
,没有的话就用配置文件中读取的第一个站点,因而80端口的流量确实已经是默认去了 Welcome to nginx! ,但对于HTTPS的443端口,第一个监听的站点就会被用来作为默认的承载站,进而导致源站的证书授权名泄露、IP面临被关联泄露的风险。为了同样保护起HTTPS协议的默认源站,我们可以使用openssl为自己签发一张不包含敏感信息的证书:
1 2 3 4 5 6 # 生成密钥 openssl genrsa -out server.key 4096 # 生成证书请求,内容随意填写 openssl req -new -key server.key -out server.csr # 生成公钥,有效期10年 openssl x509 -req -in server.csr -out server.cer -signkey server.key -days 3650
生成的 server.cer 即为SSL证书文件, server.key 则为SSL的私钥供nginx使用。修改/etc/nginx/sites-enabled/default
中的对应行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # ... server { listen 80 default_server; listen [::]:80 default_server; # SSL configuration # + listen 443 ssl default_server; + listen [::]:443 ssl default_server; + ssl_certificate /path/to/your/cert; + ssl_certificate_key /path/to/your/key; root /var/www/html; # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } # ...
但光是这样还不够,为了避免遭受中间人攻击,我们需要保证我们到CloudFlare的服务器的连接不被任何家伙干扰。
可以开启经过身份验证的源服务器拉取 选项。
参照CloudFlare官方的配置教程 ,下载 CloudFlare的CA公钥证书 ,并配置到nginx中您站点配置文件中的对应段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name yourdomain.ltd; ## SSL ssl_session_cache shared:ssl_session_cache:10m; ssl_certificate /path/to/site/cert; ssl_certificate_key /path/to/site/key; ## CloudFlare Pull Origin Authentication + ssl_client_certificate /path/to/your/cloudflare/origin-pull-ca.pem; + ssl_verify_client on; ## reverse proxy location / { proxy_pass http://upstream; client_max_body_size 0; include conf.d/shared/revproxy.conf; } }
经ADD-SP 大佬的提醒,还有一种更简便的方案可以实现SSL证书的保护:
在 nginx-1.19.4 或更高版本中,新增了配置 ssl_reject_handshake
,只要在未指定server_name的 server 块(即默认块)里设置为 on,就可以直接在握手阶段阻断,证书也不会泄露,且不会影响指定了server_name并正确配置了证书的块。只需要把下列 server 块写到默认配置(80端口的默认请求可以直接拒绝了):
1 2 3 4 5 6 7 8 9 server { listen 80; deny all; } server { listen 443 ssl; + ssl_reject_handshake on; }
可以参照 官方文档 获取更多信息与详细样例。
这样就能从应用层保护我们的源站安全。
然后是网关层,通过iptables和ipset,将80/443端口的访问权限制为仅有CloudFlare的IP可以访问,以便于进一步地阻碍攻击者的行为:
1 2 3 4 5 6 7 8 9 10 11 ipset create cloudflare_ips_v4 hash :net for x in $(curl https://www.cloudflare.com/ips-v4); do ipset add cloudflare_ips_v4 $x ; done iptables -A INPUT -m set --match-set cloudflare_ips_v4 src -p tcp -m multiport --dports http,https -j ACCEPT iptables -A INPUT -p tcp -m multiport --dport http,https -j DROP ipset create cloudflare_ips_v6 hash :net family inet6 for x in $(curl https://www.cloudflare.com/ips-v6); do ipset add cloudflare_ips_v6 $x ; done ip6tables -A INPUT -m set --match-set cloudflare_ips_v6 src -p tcp -m multiport --dports http,https -j ACCEPT ip6tables -A INPUT -p tcp -m multiport --dport http,https -j DROP
为了避免前后端的请求数据在公网明文传输,我们搭建了一个 WireGuard VPN 将两个节点加入对等的网络环境。由于是在 Debian 9 上搭建,您可以参照本站的 在 Debian 9 上安装 WireGuard VPN 这篇文章,我们成功完成了内网的组建。值得一提的是,文章中的wg0
并不是限定的,只要保证该conf文件的文件名和文件中涉及到的防火墙规则网卡名一致即可,WireGuard会自动读取配置目录下的配置文件,并且在启动时根据对应的文件自动初始化网卡。
我们新增了一些防火墙规则,将不是从我们WireGuard可信客户端中上传的数据包全部丢弃:
1 2 3 4 ipset create wgclients hash :ip ipset add wgclients 1.2.3.4 iptables -A INPUT -m set --match-set wgclients src -p udp --dport 51820 -j ACCEPT iptables -A INPUT -p udp -m multiport --dport 51820 -j DROP
测试一下ping,应该已经成功组建成对应的私有网络了。
为了方便Misskey的流量也走保护机的公网IP流出,我们为Misskey配置了HTTP代理。 在default.yml文件中取消proxy: http://127.0.0.1:3128
行前的注释号#
,将后面的地址改为我们的http代理地址。 例如根据本站使用的WireGuard配置,设置为http://192.168.8.1:3128
。
之后编辑保护机的squid配置,取消对应分配的 局域网IP地址行 和 访问请求控制行 前的注释号#
,便于让使用WireGuard的后端站可以访问到squid:
1 2 3 4 # 对应于您在WireGuard中设置的子网段 +acl localnet src 192.168.0.0/16 # 允许本地网络的代理请求 +http_access allow localnet
由于保护机使用的是多IP多网卡设置,因而需要手动指定squid的主机名和出口地址,以避免squid卡死在启动阶段一直报警告:
1 2 3 4 # 您的域名 +visible_hostname hostname.your.ltd # 您的出口地址 +tcp_outgoing_address 1.2.3.4
完成之后启动squid、重启Misskey即可。