家庭宽带建站指南
众所周知,家庭宽带虽然可以获取公网IP并通过DDNS的方式建站,但是运营商把80/443端口ban了,导致访问服务必须加端口号,十分丑陋且不方便
使用CloudFlare代理非80/443端口,实现http/https直接通过域名访问家庭宽带部署的服务,不再需要拼端口号
由于所有流量都会经过CloudFlare,延迟会直接起飞,不适用于对延迟敏感的服务,例如teamspeak
0.准备工作:
- 一台物理机:闲置的PC、Mac;Nas;软路由;树莓派等等
- 公网IP:找家庭网所在的运营商开通
- 域名:一个一级域名
1.测试
-
将服务部署在物理机中并使用局域网IP+端口成功访问
-
在路由器中配置端口转发,将服务的端口转发到公网,并在公网成功访问
2.CloudFlare
- 注册一个账号并在右上角切换到【简体中文】
- 将域名添加到CloudFlare,计划选择【Free】即可
- 出现提示 删除以下名称服务器 添加Cloudflare名称服务器
- 登录到域名注册机构平台,修改DNS服务器,将默认的DNS服务器删除,添加CloudFlare给的服务器,修改完成后回到CloudFlare检查是否成功解析
- 成功解析后,我们先手动配一个服务进行尝试:
- 在CloudFlare的【DNS记录】中添加一条:【类型:A;名称:二级域名,比如域名为baidu.com,这里填map,最终访问的地址就是map.baidu.com;IPv4 地址:你的公网IP;代理状态:开启】
- 在左侧导航栏找到【规则】,选择【Origin Rules】
- 配置一个规则:【字段:SSL/HTTPS,值:否,重写到:你本地服务映射的公网端口】,部署
- 使用这里上面配置的完整域名访问服务
- 如果上一步成功访问,你的服务已经初步脱离了家庭宽带端口的限制
3.DDNS
-
由于家庭宽带公网IP会变化,需要一个脚本更新CloudFlare中的DNS配置
-
在CloudFlare右上角点击【我的个人资料】,找到【API令牌】,找到【Global API Key】,点击查看
-
这里提供一个Python脚本来更新IP,你也可以使用自己熟悉的语言来请求CloudFlare API
import requests import json import datetime def check_ip(): with open("./ip.txt", "r+") as f: lines = f.readlines() last_line = lines[-1] now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") if last_line != ip: print(f"{now}:ip changed to {ip}") f.write(f"\n{ip}") return False else: print(f"{now}:ip not change") return True def get_ipv4(): res = requests.get("https://ident.me") return res.text class CloudFlareDDNSUpdater: def __init__(self): self.headers = { "X-Auth-Email": Auth_Email, "X-Auth-Key": Auth_Key, "Content-Type": "application/json", } self.login_verify() def login_verify(self): url = "https://api.cloudflare.com/client/v4/user/" res = requests.get(url=url, headers=self.headers) data = json.loads(res.text) if not data["success"]: print(data["errors"]) exit() def list_zone_identifier(self): url = "https://api.cloudflare.com/client/v4/zones" res = requests.get(url=url, headers=self.headers) data = json.loads(res.text) return data["result"] def list_zone_record(self, domain_id): url = f"https://api.cloudflare.com/client/v4/zones/{domain_id}/dns_records?page=1&per_page=20&order=type&direction=asc" res = requests.get(url=url, headers=self.headers) data = json.loads(res.text) return data["result"] def update_A_record(self, domain_name): domains = self.list_zone_identifier() target_domain = list(filter(lambda x: x["name"] in domain_name, domains))[0] records = self.list_zone_record(target_domain["id"]) target_record = list(filter(lambda x: x["name"] == domain_name, records))[0] domain_id, record_id = target_domain["id"], target_record["id"] url = f"https://api.cloudflare.com/client/v4/zones/{domain_id}/dns_records/{record_id}" data = { "type": "A", "name": domain_name, "content": ip, "ttl": 1, "proxied": True, } res = requests.put(url=url, headers=self.headers, data=json.dumps(data)) data = json.loads(res.text) if data["success"]: print(f"[+] Now {data['result']['name']} {data['result']['type']} record is {data['result']['content']}") print(f"[i] May will take effect in {int(data['result']['ttl'] / 60)} minutes") else: print(data["errors"]) exit() return data["result"] if __name__ == "__main__": ip = get_ipv4() if not check_ip(): Auth_Email = "" # 你的CloudFlare邮箱 Auth_Key = "" # 你的CloudFlare API Key updater = CloudFlareDDNSUpdater() updater.update_A_record("") # 你的完整域名,例如:map.baidu.com
-
在脚本最后填写你的信息,如果配置了多个二级域名DNS记录,可以复制多次最后一行填写不同的域名
-
把脚本放在任意目录,并创建一个ip.txt用来记录历史ip
-
创建一个一分钟执行一次的定时任务来执行该脚本,根据不同的设备自行搜索实现方式
4.Nginx
- 当服务较多时,可以使用Nginx作为入口网关进行反向代理,使用同一个CloudFlare规则,只暴露一个端口,通过配置路由来区分不同服务
- 可以先通过端口转发用IP+端口测试Nginx服务是否正常
- 在局域网中部署一个Nginx服务
- 在Nginx目录的conf.d中添加【完整域名.conf】,listen任意端口,并配置需要反代的服务
- 将Nginx配置中listen的端口转发到公网
- 修改CloudFlare配置,将Origin Rules中的端口改为上一步转发的端口
- 使用域名访问服务
- 如果成功访问,建议将其他暴露的端口关闭
5.SSL/HTTPS
-
在阿里云或腾讯云申请免费SSL证书
-
创建证书,填入完整域名,选择手工DNS认证,获取到DNS信息后返回CloudFlare,在DNS配置中配置一个类型为TXT的DNS解析记录,名称和内容按申请证书提示的填
-
申请完耐心等待,通过后下载证书,类型选择Nginx,pem/key
-
在Nginx目录中创建新目录ssl,将下载的pem文件和key文件重命名为【完整域名.pem】和【完整域名.key】后放入ssl目录中
-
编辑3中conf.d目录下的【完整域名.conf】,SSL相关配置去网上抄一下
-
这里提供一段我自己的Nginx配置
server { listen 443 ssl; server_name i.potato.com; # 增加ssl ssl_certificate /ssl/i.potato.com.pem; ssl_certificate_key /ssl/i.potato.com.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; # 指定密码为openssl支持的格式 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 密码加密方式 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; # 依赖SSLv3和TLSv1协议的服务器密码将优先于客户端密码 ssl_prefer_server_ciphers on; access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } error_page 404 /404.html; location = /404.html { root /usr/share/nginx/html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # 配置自己的服务 location /api/ { proxy_pass http://192.168.31.254:8000; } }
-
重启Nginx服务
-
将【2】中【Origin Rules】配置的【SSL/HTTPS】改为【是】
-
在CloudFlare的左侧导航栏找到【SSL/TLS】,将加密模式改为【严格】
-
使用【https://完整域名/】访问服务
以上为2023年5月10日的配置过程