零成本远程访问家庭PC:WireGuard+DDNS+Python全自动方案
你是不是也有过这种冲动?
人在外地,家里那台精心组装的电脑却闲着。想远程连回去,搜了一圈教程,发现不是要买树莓派,就是要给运营商交钱搞静态IP,甚至还得换个带DDNS的企业级路由器。钱包一听就怂了。
别急。这篇文章就是为你准备的。我从零开始,只用了三样东西:
- WireGuard(免费开源VPN)
- No-IP(免费动态DNS)
- 一个Python脚本(自己写的)
就搞定了从孟买远程连接喀拉拉邦家里电脑的全套方案。不用花钱,不用额外硬件,而且全自动——电脑一开机,脚本自动跑,IP变了也不怕。
核心问题:动态IP + 没公网IP?
大多数家庭宽带都没有固定的公网IP,路由器重启或者ISP刷新,你的WAN IP就变了。就算用WireGuard搭好VPN,IP一变连接就断。
解决方案就是动态DNS(DDNS):把一个固定的域名(比如 ximangh.ddns.net)始终指向你最新的公网IP。No-IP提供免费的DDNS服务,但问题来了——IP变了之后,得有人去更新这个记录。
手动更新?不可能每次都盯着。所以我写了个Python脚本,自动搞定这件事。
我的自动化Python脚本
脚本逻辑很简单:
- 调用API获取当前公网IP
- 和上次保存的IP比较
- 如果变了,就自动更新No-IP的DDNS记录
- 把新IP存下来,下次对比用
下面是带中文注释的完整代码,你可以直接复制使用:
import requests
import os
import ipaddress
import logging
# 设置日志,方便看执行情况
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
# 从环境变量读No-IP账号密码,不要写死在代码里
NOIP_USER = os.environ.get("NOIP_USER")
NOIP_PASS = os.environ.get("NOIP_PASS")
HOSTNAME = os.environ.get("DDNS_HOSTNAME", "ximangh.ddns.net") # 你的DDNS域名
IP_FILE = "public_ip.txt" # 存上一次IP的文件
def get_public_ip():
"""从 ipify 获取当前公网IP,并验证格式"""
response = requests.get(
"https://api.ipify.org?format=json",
timeout=10,
verify=True
)
response.raise_for_status()
ip = response.json()["ip"]
ipaddress.ip_address(ip) # 校验,非法IP会报错
logging.info(f"获取到当前公网IP: {ip}")
return ip
def read_saved_ip(filename):
"""读取上次保存的IP"""
if not os.path.exists(filename):
logging.info("没有找到保存IP的文件")
return None
with open(filename, "r") as file:
ip = file.read().strip()
logging.info(f"上次保存的IP: {ip}")
return ip
def save_ip(filename, ip):
"""把新的IP保存到文件"""
with open(filename, "w") as file:
file.write(ip)
logging.info(f"新IP已保存: {ip}")
def update_noip(ip):
"""通过No-IP的API更新DDNS记录"""
if not NOIP_USER or not NOIP_PASS:
logging.error("环境变量NOIP_USER或NOIP_PASS未设置!")
return
response = requests.get(
"https://dynupdate.no-ip.com/nic/update",
params={"hostname": HOSTNAME, "myip": ip},
auth=(NOIP_USER, NOIP_PASS),
headers={"User-Agent": "my-ddns-updater/1.0"},
timeout=10,
verify=True
)
result = response.text.strip()
if result.startswith("good"):
logging.info(f"No-IP更新成功: {result}")
elif result.startswith("nochg"):
logging.info(f"No-IP: IP未变化: {result}")
elif result == "nohost":
logging.error("No-IP错误:主机名不存在")
elif result == "badauth":
logging.error("No-IP错误:认证失败")
elif result == "abuse":
logging.error("No-IP错误:账号被滥用封禁")
else:
logging.warning(f"No-IP未知响应: {result}")
def main():
try:
current_ip = get_public_ip()
saved_ip = read_saved_ip(IP_FILE)
if saved_ip == current_ip:
logging.info("IP未变化,无需更新")
else:
logging.info(f"IP已变化: {saved_ip} → {current_ip}")
save_ip(IP_FILE, current_ip)
update_noip(current_ip)
except ValueError as e:
logging.error(f"获取到无效IP: {e}")
except requests.RequestException as e:
logging.error(f"网络错误: {e}")
if __name__ == "__main__":
main()
怎么让它全自动跑?
把脚本设为开机自启就行。Windows用户:
- 按
Win+R输入shell:startup,打开启动文件夹 - 创建一个
.bat文件,内容写python your_script.py - 或者直接用任务计划程序设置
这样每次电脑一开机联网,脚本就自动检查IP,变了就更新DDNS。你再也不用手动登录No-IP了。
最终效果
- WireGuard VPN 让你远程连接像在家一样
- No-IP DDNS 让域名始终指向最新IP
- Python脚本 让更新完全自动化
现在我用手机、笔记本,随便哪里都能远程连回喀拉拉邦的家里电脑。零成本,纯自建,安全可靠。
你唯一需要额外做的事情:在路由器上设置一个端口转发(WireGuard默认端口51820),或者直接用UPnP自动映射。
安全注意:我把No-IP密码存在环境变量里,而不是硬编码。公网IP也做了校验,请求加了超时和TLS验证。WireGuard用了现代加密,且为每位客户端分配独立的/32地址,严格控制访问。
学到了什么?
如果当时我有钱,可能直接买现成的远程方案,那就永远学不会DDNS、VPN、API调用和自动化脚本这些东西。限制反而逼出了创造力。
这就是我现在的远程方案——每次连回去,都觉得自己挺牛。
直达网址:No-IP 官方免费DDNS服务
