└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # 如何搭建一个树洞 2 | 3 | ## 0 开始搭建树洞之前 4 | 5 | 如果你在看这篇文章,可能或多或少你对搭建树洞有着一定的兴趣。因此,首先需要强调的是:**搭建并运营一个树洞是一个工作量巨大并且难度很高的事情**。树洞的运营是一个一定需要一支团队才可以胜任的任务,并且这个团队至少需要有以下几点能力: 6 | 7 | - 技术:搭建树洞、树洞功能更新时都需要技术能力 8 | - 网络安全:树洞的隐私保护是重中之重,也是树洞用户非常关心的问题 9 | - 日常管理:团队中最好需要管理员7/24值班管理树洞,管理员还需要有极高的舆情处理能力,同时看得懂各种黑话 10 | - 法务:日常管理中遇到的各种问题都可能需要法务帮忙 11 | 12 | **在你想要开始搭建树洞之前,请确保你身边有一群你们相互信任的伙伴,你们的合力可以覆盖上面几点能力**。 13 | 14 | ## 1 对技术人员的要求 15 | 16 | 树洞的技术栈目前已经较为复杂,后端使用的是Linux+Nginx+MySQL+Redis+Go;Web前端使用的是React;Android前端主要是Kotlin;iOS前端是SwiftUI。如果要完成树洞后端的搭建,技术人员至少需要掌握: 17 | 18 | - Linux的基本使用方法和常用bash命令,比如vim、top、ps、tmux 19 | - 基础网络协议知识,如HTTP协议等 20 | - 基础的密码学知识,至少需要可以看懂https://github.com/treehollow/treehollow-v3-encryption-doc 21 | - 熟悉git、cron 22 | 23 | 如果你掌握了这些基础,那么请继续往下看吧!**搭建树洞至少需要1天的时间,请保持耐心。** 24 | 25 | ## 2 选择一个云服务器提供商和操作系统 26 | 27 | 树洞的后端需要运行在一个有公网IP的服务器中,至少需要1核2G内存,最好2核4G内存或以上。市面上现在有很多服务器提供商,国外大厂商有Amazon AWS、Google Cloud、Microsoft Azure等,小厂商有Digitalocean、Vultr、Linode等,国内知名的有阿里云、腾讯云、华为云等。它们之间的对比如下表: 28 | 29 | | | 备案 | 价格 | 网络速度 | 30 | | ---------- | ------ | -------- | -------------------- | 31 | | 国外大厂商 | 不需要 | 比较贵 | 晚上国际出口可能很慢 | 32 | | 国外小厂商 | 不需要 | 比较便宜 | 晚上国际出口可能很慢 | 33 | | 国内厂商 | 需要 | 很贵 | 快 | 34 | 35 | 选择完厂商之后,就选择一个操作系统吧,对于服务器来说,操作系统最重要的特性就是**稳定**。因此,我们推荐**CentOS 7**,它的官方支持将在2024年6月才结束。 36 | 37 | ## 3 注册一个域名 38 | 39 | 注册域名实在是太简单了,网上的教程很多。这里需要强调一下安全问题 40 | 41 | ### 【⚠必须采取的安全措施】:***请务必确保时刻没有子域名DNS解析到服务器真实IP,并且服务器真实IP必须始终保密!*** 42 | 43 | **服务器的IP如果泄露,DDoS等情况就有可能发生,同时还会极大地增加攻击面。因此,服务器的HTTP流量必须始终通过CDN(比如Cloudflare CDN)。在https://securitytrails.com/dns-trails 网站上,一个域名的全部子域名和DNS历史记录都可以轻松查到,因此不能有任何时刻让服务器真实IP暴露在外。** 44 | 45 | ### 【建议的安全措施】:请使用堡垒机登录ssh 46 | 47 | 堡垒机可以减少一定的攻击面,对服务器安全是很有益的。如果树洞运营团队有条件,请使用堡垒机。 48 | 49 | ## 4 在服务器上搭建后端 50 | 51 | ### 第一步:设置时区为Asia/Shanghai 52 | 53 | ```bash 54 | sudo timedatectl set-timezone Asia/Shanghai 55 | ``` 56 | 57 | 请相信,这会节省很多麻烦的 58 | 59 | ### 第二步(可选):安装swap空间 60 | 61 | 有swap空间可以让服务器在内存吃紧的时候有着更好的表现。如果你的服务器硬盘是SSD的,那么swap会消耗SSD寿命;不过如果你是租的服务器,谁会在意寿命呢:) 62 | 63 | 参考教程:https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-centos-7 64 | 65 | ### 第三步:安装并配置Nginx,同时配置firewalld或iptables防火墙 66 | 67 | 参考教程:https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-centos-7 68 | 69 | ### 【⚠必须采取的安全措施】:***请务必配置好iptables或firewalld或使用服务器提供商的安全组功能,仅允许CDN流量访问80/443端口*** 70 | 71 | **否则,攻击者可以通过全网扫描的方式获取服务器的真实IP,参考:https://cloud.tencent.com/developer/article/1079482** 72 | 73 | 如果你使用的是基于iptables的防火墙,那么我们在此提供一个示例脚本,你可以使用以下脚本方便地添加Cloudflare的节点进入iptables白名单: 74 | 75 | ```bash 76 | #!/bin/bash 77 | iptables -F 78 | ipset destroy cf4 79 | ipset create cf4 hash:net 80 | for x in $(curl https://www.cloudflare.com/ips-v4); do ipset add cf4 $x; done 81 | 82 | iptables -A INPUT -m set --match-set cf4 src -p tcp -m multiport --dports 80,443 -j ACCEPT 83 | iptables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP 84 | iptables-save 85 | ``` 86 | 87 | 这个脚本要求ipset已经被安装。如果你看不懂这段脚本在干什么,请先搜索学习相关知识之后,再进行后续步骤。 88 | 89 | ### 第四步:安装并配置MySQL 8.0,Redis,Go 90 | 91 | 参考教程: 92 | 93 | https://www.mysqltutorial.org/install-mysql-centos/ 94 | 95 | https://www.digitalocean.com/community/tutorials/how-to-install-secure-redis-centos-7 96 | 97 | https://golang.org/doc/install 98 | 99 | 【注意】:MySQL的中文搜索引擎分词在默认情况下有bug,请参考https://github.com/treehollow/treehollow-backend/issues/43 解决 100 | 101 | ### 第五步(可选):安装并配置Nginx amplify 102 | 103 | Nginx Amplify是一个可以监控服务器健康状态的免费平台,详细介绍和安装方法请见https://amplify.nginx.com/ 104 | 105 | ### 第六步:注册Google reCAPTCHA 106 | 107 | 请到https://developers.google.com/recaptcha/intro 分别注册一个reCAPTCHA v3和reCAPTCHA v2 Checkbox,未来将会在后端和前端配置文件中用到 108 | 109 | ### 第七步:购买或搭建一个SMTP邮件发送服务器 110 | 111 | 国外有mailgun和sendgrid,国内有阿里云,也可以自己搭建(可能会很难),具体做法请你根据自身情况搜索 112 | 113 | ### 第八步:配置Nginx 114 | 115 | 本文假设读者已经对Nginx配置文件的结构和用法已经有了基本的了解,下面只介绍几个重点。 116 | 117 | #### 重点1:如何让Nginx在CDN后面获取IP 118 | 119 | 由于服务器只和CDN边缘节点进行网络通信,获取客户IP时就会有难度。还好,几乎所有CDN都会在HTTP的**X-Forwarded-For** header中转发客户IP。请参考:https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers- 120 | 121 | 但是我们不能完全信任X-Forwarded-For的结果,因为如果攻击者构造一个含X-Forwarded-For的包,就可以达到IP欺骗的效果。为此,Nginx的比较新的版本里都带一个叫ngx_http_realip_module的模块,只要将CDN的IP列表添加进Nginx信任的列表里,就可以正确解析出客户IP。参考:https://cloud.tencent.com/developer/article/1521273 122 | 123 | 我们在此提供一个脚本,以Cloudflare CDN为例,生成Nginx所需要的配置文件: 124 | 125 | ```bash 126 | #!/bin/bash 127 | echo "# generated by updateNginxWhitelist.sh" > /etc/nginx/whitelist.conf 128 | 129 | echo "# Cloudflare IPs" >> /etc/nginx/whitelist.conf 130 | for x in $(curl https://www.cloudflare.com/ips-v4); do echo "set_real_ip_from $x;" >> /etc/nginx/whitelist.conf; done 131 | 132 | echo >> /etc/nginx/whitelist.conf 133 | 134 | echo "real_ip_header X-Forwarded-For;" >> /etc/nginx/whitelist.conf 135 | echo "real_ip_recursive on;" >> /etc/nginx/whitelist.conf 136 | nginx -t 137 | nginx -s reload 138 | ``` 139 | 140 | 运行此脚本前,在`nginx.conf`中加入`include whitelist.conf;`就可以了。如果你看不懂这段脚本在干什么,请先搜索学习相关知识之后,再进行后续步骤。 141 | 142 | #### 重点2:如何让Go程序在Nginx后面获取IP 143 | 144 | 以T大树洞API的`/v3/security`块为例,Nginx配置文件会类似: 145 | 146 | ``` 147 | ... 148 | location /v3/security { 149 | proxy_set_header Host $host; 150 | proxy_set_header X-Real-IP $remote_addr; 151 | proxy_set_header X-Forwarded-For $remote_addr; 152 | 153 | proxy_pass http://unix:/somewhere/treehollow-security-api.sock; 154 | error_page 502 = @fallback; 155 | } 156 | ... 157 | location @fallback { 158 | if ($request_method = 'OPTIONS') { 159 | add_header 'Access-Control-Allow-Origin' 'https://web.thuhole.com'; 160 | add_header 'Access-Control-Allow-Headers' 'TOKEN'; 161 | add_header 'Content-Length' 0; 162 | return 204; 163 | } 164 | 165 | proxy_pass http://unix:/somewhere/treehollow-fallback.sock; 166 | proxy_set_header Host $host; 167 | proxy_set_header X-Real-IP $remote_addr; 168 | proxy_set_header X-Forwarded-For $remote_addr; 169 | add_header Access-Control-Allow-Origin "https://web.thuhole.com"; 170 | } 171 | ... 172 | ``` 173 | 174 | 如果你看不懂这段配置**大概**在干什么,请先搜索学习相关知识之后,再进行后续步骤。 175 | 176 | #### 重点3:Nginx中Websocket的配置 177 | 178 | 树洞Android客户端的推送服务使用了Websocket协议的方式,Nginx处理Websocket时的配置应该类似这样: 179 | 180 | ``` 181 | ... 182 | server_name ws.thuhole.com; 183 | 184 | location / { 185 | proxy_set_header Host $host; 186 | proxy_set_header X-Real-IP $remote_addr; 187 | proxy_set_header X-Forwarded-For $remote_addr; 188 | 189 | proxy_http_version 1.1; 190 | proxy_set_header Upgrade $http_upgrade; 191 | proxy_set_header Connection "Upgrade"; 192 | proxy_set_header X-Forwarded-Proto http; 193 | proxy_redirect http:// $scheme://; 194 | 195 | proxy_connect_timeout 3m; 196 | proxy_send_timeout 3m; 197 | proxy_read_timeout 3m; 198 | 199 | proxy_pass http://unix:/somewhere/treehollow-push-api.sock; 200 | } 201 | ... 202 | ``` 203 | 204 | 这里如果看不懂没关系,后面会更仔细介绍T大树洞后端的结构 205 | 206 | ### 第九步:为树洞后端的运行环境添加新的用户 207 | 208 | 添加新的低权限Linux用户: 209 | 210 | ```bash 211 | useradd -m username 212 | ``` 213 | 214 | 添加新的MySQL数据库及用户:https://matomo.org/faq/how-to-install/faq_23484/ 215 | 216 | ### 第十步(可选):下载GeoIP库 217 | 218 | T大树洞使用GeoIP库进行风控等操作,这里你如果不知道用什么库,就用免费的Maxmind数据库:https://dev.maxmind.com/geoip/geoipupdate/ 219 | 220 | 安装并运行geoipupdate,然后把geoipupdate放到crontab里 221 | 222 | ### 第十一步:下载、编译运行树洞后端代码 223 | 224 | 树洞后端代码位于https://github.com/treehollow/treehollow-backend 225 | 226 | 安装方式:`git clone`下来之后,进入项目文件夹,然后运行: 227 | 228 | ``` 229 | go install ./... 230 | ``` 231 | 232 | 这时会在`$GOPATH/bin`生成几个可执行文件,有用的几个分别是: 233 | 234 | `treehollow-v3-push-api`: 用于处理Android和iOS消息推送服务的可执行程序 235 | 236 | `treehollow-v3-security-api`: 用于处理`/v3/security` 中所有API的可执行程序 237 | 238 | `treehollow-v3-services-api`: 用于处理`/v3/` 中security以外API的可执行程序 239 | 240 | `treehollow-v3-fallback`: 当服务更新时,用于向前端显示错误信息的可执行程序 241 | 242 | 将`example.config.yml`复制到`config.yml`之后修改参数即可运行。在生产环境中,这四个程序应该都在运行 243 | 244 | ### 第十二步【⚠必须采取的安全措施】:***配置SELinux*** 245 | 246 | SELinux也许是个讨厌的东西,但它对服务器安全至关重要。为了树洞服务的安全,树洞运营团队的技术人员应该花几个小时学习以下SELinux的使用。这里我们提供一些有用的资料: 247 | 248 | 学习资料:https://www.digitalocean.com/community/tutorials/an-introduction-to-selinux-on-centos-7-part-1-basic-concepts 249 | 250 | 实际用的时候其实基本就是用两次audit2allow:https://www.server-world.info/en/note?os=CentOS_7&p=selinux&f=9 251 | 252 | (注:如果两次audit2allow不行,那就四次) 253 | 254 | ### 第十三步:检查各个域名的正常运行、配置前端的config 255 | 256 | T大树洞前端代码位于:https://github.com/treehollow/webhole 配置环境变量之后通过GitHub Actions即可成功运行部署 257 | 258 | Android和iOS客户端需要配置文件才能正常运行,请在这个模板下修改:https://github.com/treehollow/thuhole-config/blob/main/main.txt 259 | 260 | 在T大树洞的生产环境中,至少有8个域名会解析运行: 261 | 262 | 1. `thuhole.com`: 主页 263 | 2. `www.thuhole.com`: 跳转到`thuhole.com` 264 | 3. `tapi.thuhole.com`: 主要API都在这个域名 265 | 4. `ws.thuhole.com`: Android消息推送时的Websocket服务器 266 | 5. `re.thuhole.com`: 给app登录时显示reCAPTCHA界面的网页,参考https://github.com/treehollow/thuhole-recaptcha-v2/blob/main/recaptcha/index.html 267 | 6. `i.thuhole.com`: 显示图片用的域名 268 | 7. `web.thuhole.com`: 网页版树洞 269 | 8. `terms.thuhole.com`: 用于放用户协议、隐私政策、树洞规范 270 | 271 | 把这些服务一个个上线是费时费力的事情,但是生产环境毕竟就是需要这么多服务。 272 | 273 | 274 | 275 | --------------------------------------------------------------------------------