Skip to main content

第 3 课:申请公网 IP

实验环境:光猫+Ubuntu 服务器

(一)家庭网络结构

1. 网络结构

默认家庭网络结构(拨号在路由器):

[公网互联网]

[光猫(Modem)] ——> 拨号权限交给
↓ 路由器
[家庭路由器(PPPoE拨号)]

[家庭局域网内设备:电脑、手机、服务器等]

在这个结构中,由路由器进行拨号(PPPoE),拿到公网或私网 IP,内部设备通过 NAT 上网。

而在本文中我们要实现的结构是:

[公网互联网]

[光猫(可能是桥接模式)]

[Ubuntu 服务器(自己拨号上网)]

此时,你的服务器自己拨号,不依赖路由器。这种结构适合需要让服务器直接暴露在公网、拥有公网 IP的场景,但其他设备将无法通过这台服务器共享网络。


2. 上网方式

上网方式特点
PPPoE 拨号需要账号密码,获取公网/内网 IP,最常见
DHCP(动态主机配置协议)不需要账号密码,插上就用(校园/公司/租房)
静态 IP 分配ISP 固定给你一个公网 IP,企业专线、静态公网
4G/5G蜂窝网络运营商通过基站分配 IP,适合移动路由器
桥接+服务器拨号专业配置,适合公网部署服务器 ✅

网络业务提供商(Internet Service Provider,简称ISP) 指比如:移动、电信、联通

这里我们要重点介绍:拨号上网(PPPoE)

PPPoE = Point-to-Point Protocol over Ethernet(以太网点对点协议)

它本质上是用宽带账号+密码登录 ISP 网络,以建立正式的“会话”,之后你才可以分配 IP(公网/内网)并访问互联网。


拨号动作由谁执行?

拨号者场景
光猫拨号企业/校园/一些老旧场景
路由器拨号家庭主流配置 ✅
服务器拨号进阶用户/特殊用途(公网服务器)✅

为什么需要账号密码?

  • 宽带账号 = 身份凭证(比如手机号+@isp)
  • ISP 使用这个来验证你是否是合法用户;
  • 相当于上网“登录”步骤;
  • 也用于计费、限速、流控等。

拨号后是否一定有公网 IP?

❌ **不一定!**拨号后获得的 IP 可以是:

类型是否公网
100.x.x.x(CGNAT)❌ 内网
192.168.x.x / 10.x.x.x❌ 内网
115.xxx.xxx.xxx / 121.xxx.xxx.xxx 等✅ 真正公网 IP

我们最终要实现的架构是:外网 → 光猫桥接口 → Ubuntu 拨号获得公网 IP



(二)检测是否有公网地址

要检测你的家庭服务器是否拥有公网 IP(也叫“外网 IP”)

1. 查看本机的 IP 地址:

首先要查看本机是否直接具有 公网IP 地址

  • Windows:运行 ipconfig
  • Linux/MacOS:运行 ip aifconfig

如果你看到的 IP 是以下格式,说明是现在是家庭路由器 NAT 分发的 内网 IP,不是公网IP:

  • 192.168.x.x
  • 10.x.x.x
  • 172.16.x.x ~ 172.31.x.x

说明你的服务器在 NAT 后的内网中,由路由器统一管理公网出口。


2. 查看家庭的 IP 地址

即使 本机IP 地址可能是路由器分发的私网IP,路由器的出口 本身也可能具有公网 IP。

这个网址测试 是否具有 IPv6:IPv6 测试

这个网址测试 详细的 IPv4 信息:IPCheck.ing

可以让朋友或你自己用另一台设备(如手机关闭 WiFi 用 4G 网络)尝试访问你的服务器端口(例如通过公网 IP:端口号访问 Web 服务):

curl http://你的公网IP:端口

若可以访问,说明:

  • 你有公网 IP;
  • 该端口没有被封锁;
  • 路由器已正确端口映射。

但是常见的情况是你的路由器其实也没有公网 IP

很多家庭网络使用运营商 NAT(CGNAT),你即使获取到了一个公网 IP,也无法真正用于端口映射,因为该公网 IP是运营商层的,并非你真正“拥有”的。


3. 例子

在线IP 测试网站反馈的路由器出口IP是:

你的公网 IPv4 地址是 115.234.230.5
你的运营商(ISP)是 CHINANET-BACKBONE No.31,Jin-rong Street
没有检测到 IPv6 地址 [更多信息]
你只接入了 IPv4 互联网,不能访问纯 IPv6 网站。
可向运营商咨询如何使用 IPv6,实现最佳的网络性能。 [更多信息]
你的 DNS 服务器(可能由运营商提供)已经接入 IPv6 互联网了。

而本机上显示的IP地址为:

以太网适配器 vEthernet (Default Switch):

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::7b22:b022:c1e1:625c%31
IPv4 地址 . . . . . . . . . . . . : 172.19.160.1
子网掩码 . . . . . . . . . . . . : 255.255.240.0
默认网关. . . . . . . . . . . . . :

说明如下:你没有公网 IPv4

你看到的本地 IP 地址是:172.19.160.1,而你的公网 IP 是 115.234.230.5,说明:

  • 你当前的服务器或电脑并没有被分配真实公网 IPv4;
  • 你处在 运营商 NAT 后的局域网中(172.16.x.x ~ 172.31.x.x 是典型的私有内网 IP 段);
  • 所谓的公网 IP(115.234.230.5)属于你宽带网关或运营商 NAT 网关,并非直接分配给你的设备;
  • 你无法直接通过该公网 IP + 端口访问你的家庭服务器,即使你做了端口映射,极可能是失败的。


(三)申请公网IP

那么现在我们需要运营商给我们分配一个 非 NAT 的公网IP。

现在我们有两个选择:

  • 申请动态公网 IPv4 (静态固定的公网 IPv4 需要运营商拉专线到家里,2000¥/月,多用于公司)
  • 申请动态或者固定的公网 IPv6(IPv6 的资源非常丰富,申请动态或者固定 理论上都可行)

我的运营商是电信,在和客服沟通后,他同意给我开通 动态公网 IPv4,没有同意开通 固定的公网 IPv6

我的家里使用的是 华为的 FTTR 路由 (Fiber To The Room,中文叫做 “光纤到房间”)。也就是把光纤接到家里的 每一个房间

因此有别于传统的 光猫+主路由器+子路由器 Mesh结构,这里使用的是 光猫+主路由器+子光猫/光路由器(AP) FTTR结构

因此只有一个路由器,也只有一个后台,登录IP和账号密码在 主路由器的背面。

如果设计架构为:外部访问 → 光猫 → 路由器 → 服务器, 那么后续还需要在路由器上设置拨号上网和端口转发等,比较麻烦。

因此我直接采取了:外部访问 → 光猫 → 服务器 的结构。

主路由器 华为K153 是一个 “光猫 + 路由器 + WiFi” 三合一设备,官方名称是 “家庭网关”,默认 LAN 口模式 是通过 K153 的内置路由器(自动拨号 + 分配内网 IP)。

因此需要拨打电信服务电话 10000,联系工程师上门把 K153 这个设备中某个 LAN 接口的网络模式,改成“桥接模式(Bridge),并询问宽带的账号和密码。等于直接从光猫 “拉出” 原始宽带信号,让连接到这个口的设备(Ubuntu 服务器)去自己拨号。

然后把 Ubuntu 服务器直接连接该接口,也就是没有经过路由器直接连接光猫,因此需要在 Ubuntu 上手动拨号上网。



(四)拨号上网

我们已经把光猫的某个端口设置为了桥接,并通过网线连接到 Ubuntu;

如果你让路由器的某个端口设置为“拨号上网口”,那么这就意味着:

  • Ubuntu 连接到这个口后,要自己拨号,否则是无法上网的;
  • 相当于你的 Ubuntu 是一个“拨号主机”,而不是从 DHCP 获取地址。

步骤 1:查看网络接口

在 Ubuntu 中运行:

ip link show

查找类似 enp0s25eth0 的接口,它是你的 有线网卡

例如可能显示:

2: enp0s25: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 28:d2:44:12:34:56 brd ff:ff:ff:ff:ff:ff

如果状态是 DOWN,说明网卡没启用,可以运行:

sudo ip link set enp0s25 up

步骤 2:发起拨号上网

安装拨号工具:(PPPoE)

sudo apt update
sudo apt install pppoeconf

然后运行拨号配置:

sudo pppoeconf

这将自动检测以太网口是否连接到了一个支持 PPPoE 的端口,然后引导你输入:

  • 用户名(ISP 提供)

  • 密码(ISP 提供)

  • 剩余选项默认同意即可

配置完成后,你可以运行:

sudo pon dsl-provider

断开拨号:

sudo poff dsl-provider

设置开机自动拨号(推荐)

sudo systemctl enable pppd-dsl.service

检查是否拨号成功并获取公网 IP:

ip addr

如果看到像 ppp0 这样的接口,并且有一个公网 IP 地址,比如 115.xxx.xxx.xxx,说明拨号成功了。


输出详细说明:

接口 1: lo 回环接口

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever

这是系统默认的本地通信接口:

  • IP 地址:127.0.0.1(IPv4)、::1(IPv6)
  • 作用:用于程序与自己通信,如 localhost,无任何联网意义
  • 无需改动或关注

接口 2: enp0s25(有线网卡)

2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 28:d2:44:b2:54:85 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.79/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s25
valid_lft 185752sec preferred_lft 185752sec
inet6 fe80::2c71:28be:21b0:8f98/64 scope link noprefixroute
valid_lft forever preferred_lft forever

这是你服务器的 以太网口,当前状态:

  • IP:192.168.0.79局域网内地址,由路由器通过 DHCP 分配;
  • 你现在连接在路由器上(非拨号接口);
  • MAC 地址为 28:d2:44:b2:54:85
  • UP, LOWER_UP 表示网卡已启用,连接正常;
  • 如果你将此网口接到了光猫路由器 K153 的 LAN 口,说明此接口可访问内网中其他设备。

💡 注意:你可以保留这个接口用于内网访问服务器,比如在本地管理时不依赖公网。


接口 3: wlp3s0(无线网卡)

3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether e8:2a:ea:ee:7e:5b brd ff:ff:ff:ff:ff:ff
inet 192.168.0.74/24 brd 192.168.0.255 scope global dynamic noprefixroute wlp3s0
valid_lft 190591sec preferred_lft 190591sec
inet6 fe80::e435:57b2:4728:7069/64 scope link noprefixroute
valid_lft forever preferred_lft forever

这是你的 无线网卡接口(Wi-Fi),当前状态:

  • IP:192.168.0.74,同样是局域网 IP;
  • 表明你当前也连着家中 Wi-Fi;
  • MAC 地址:e8:2a:ea:ee:7e:5b
  • 和有线网卡作用重复;

📌 建议:可以禁用此接口,避免多个默认网关冲突。方法如下:

nmcli device disconnect wlp3s0

接口 4: ppp0(拨号接口)

16: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qdisc fq_codel state UNKNOWN group default qlen 3
link/ppp
inet 115.234.251.61 peer 115.234.192.1/32 scope global ppp0
valid_lft forever preferred_lft forever
inet6 240e:392:3144:48c:a54b:fa11:e90a:e2c8/64 scope global temporary dynamic
valid_lft 537381sec preferred_lft 18711sec
inet6 240e:392:3144:48c:999:eeb4:fcbf:d6b1/64 scope global dynamic mngtmpaddr
valid_lft 2591784sec preferred_lft 604584sec
inet6 fe80::999:eeb4:fcbf:d6b1 peer fe80::d6c1:c8ff:fe93:8d30/128 scope link
valid_lft forever preferred_lft forever

这是你最关键的 拨号接口(PPP over Ethernet),说明拨号成功!

  • IPv4 公网地址:115.234.251.61(你在公网可访问的 IP)
  • 对端(ISP网关)IP:115.234.192.1
  • 状态:UP, LOWER_UP,表示连接正常、链路工作中
inet6 240e:392:3144:48c:a54b:fa11:e90a:e2c8/64 scope global temporary dynamic
inet6 240e:392:3144:48c:999:eeb4:fcbf:d6b1/64 scope global dynamic

你拥有两个全局 IPv6 地址:

  • 一个是 “临时 IPv6”(用于隐私保护,系统自动变化);
  • 一个是 “真实 IPv6”(长时间存在);
  • 说明你的运营商(中国电信)也为你开启了原生 IPv6。
inet6 fe80::999:eeb4:fcbf:d6b1 peer fe80::d6c1:c8ff:fe93:8d30/128

这是拨号链路上的 IPv6 本地地址(fe80:: 开头),用于与你 ISP 的 PPPoE 网关对接通信,不用关心。

最终网络结构

                     公网

┌──────┴───────┐
│ 115.234.251.61 │ ⇦ ppp0 拨号公网 IP
└───────────────┘

[Ubuntu服务器]
↙ ↘
内网IP 192.168.0.79 内网IP 192.168.0.74
enp0s25(网线) wlp3s0(Wi-Fi)
⇩ ⇩
光猫LAN口 (K153, 路由器功能启用)


(五)域名动态解析

当前获得的 IPv4:115.234.251.61 是动态公网 IP,意味着 每 48h 或者 72h, ISP 会主动断开拨号连接并分配一个新的 IPv4。因此要使用 DDNS(动态域名服务)来给动态的 IP 绑定域名。

DDNS(Dynamic DNS)会自动将你的 当前公网 IP 与你的域名绑定,即使 IP 变化,也能立即更新域名解析,保证公网访问不受影响。比如当前域名 proxy.heihet09.com 指向 IP 115.234.251.61,在 该IP更新后,DDNS 会让该域名会自动指向 新的IP。因此只要使用该域名,就始终指向这台服务器,不需要再使用变化的 IP了。

原理:我们使用的是阿里云的域名和DNS云解析,而阿里的云解析记录可以通过 账号秘钥API修改。因此只需要在服务器上部署一个客户端,每隔一段时间去检测一下本机的公网IP,如果更新了,就调用API 修改阿里云的 云DNS解析策略,使域名指向新的 IP。

第 1 步:创建密钥

  1. 登录 阿里云控制台

    image-20250706000757790

  2. 点击右上角头像,选择进入 AccessKey 管理

    image-20250706000728610

  3. 创建一组 AccessKey

    • 得到 AccessKey IDAccessKey Secret
    • 注意:只会显示一次,请复制保存

第 2 步:安装工具

你已经拥有:

  • 阿里云域名 heihet09.com
  • 子域名 proxy.heihet09.com
  • 获取了阿里云 DNS 的 AccessKey(AccessKey ID + AccessKey Secret)
  • 能通过 ip addr 获取拨号后的公网 IPv4 地址(例如 ppp0 网卡)

接下来我们在本机上安装 DDNS 工具 NewFuture/ddns,下载 Linux 二进制文件方式安装 DDNS

# 进入某个目录(比如 /opt)
cd /opt/DDNS

# 下载 glibc 版二进制(适用于 Ubuntu 20.04+)
curl -# -LO https://github.com/NewFuture/DDNS/releases/download/v4.0.2/ddns-glibc-linux_amd64

# 重命名并赋予可执行权限
mv ddns-glibc-linux_amd64 ddns
chmod +x ddns

你现在已有一个名为 ddns 的可执行文件。


第 3 步:配置域名绑定

在同一目录(或你想放的路径),创建 config.json 文件:

vim config.json

粘贴以下配置(替换成你的 AccessKey 和域名):

{
  "dns": "alidns",
  "id": "你的AccessKeyID",
  "token": "你的AccessKeySecret",
  "ipv4": ["proxy.heihet09.com"],
  "index4": "public",
  "ttl": 600
}

注:index4 使用 "public" 代表从公网接口自动获取 IPv4 地址(你也可以设置 "cmd:curl -s ipinfo.io/ip"


第 4 步:测试

/opt/DDNS 目录下直接运行:

./ddns -c config.json

如果一切配置正确,应该看到输出中包含:

2025-07-06T00:36:38 INFO [__main__]: proxy.heihet09.com(A) ==> 115.234.251.61 [via DIRECT]

你可以用 ping proxy.heihet09.com 检查是否已经指向当前公网 IP。

此时控制台也会同步更新这条解析记录

image-20250706004001071

你可以使用以下命令验证解析是否成功:

nslookup proxy.heihet09.com

结果如果是你当前公网 IPv4(如 115.234.251.61),就是 ✅ 成功。


第 5 步:设置定时任务

确保你在 /opt/DDNS 目录下,然后运行:

curl -sSL https://raw.githubusercontent.com/NewFuture/DDNS/master/systemd.sh | bash

如果出现下面这种输出,则是被墙了

curl: (7) Failed to connect to raw.githubusercontent.com port 443 after 9 ms: Couldn't connect to server

需要手动下载脚本并执行,确保你在 /opt/DDNS 目录下,然后运行:

sudo bash systemd.sh install

脚本作用如下:

  • 自动将 /opt/DDNS/ddns 复制到 /usr/share/DDNS/ddns
  • 自动将 /opt/DDNS/config.json 复制到 /etc/DDNS/config.json
  • 自动创建 /etc/systemd/system/ddns.serviceddns.timer
  • 启用并启动定时器(每 5 分钟运行一次)

检查是否安装成功

sudo systemctl status ddns.timer

你应看到 Active: active (waiting) 字样。


立即测试一次更新是否生效

sudo systemctl start ddns.service

然后可通过 tail 查看是否有更新成功:

journalctl -u ddns.service -n 20 --no-pager

此后,你的 DDNS 将每 5 分钟自动运行并保持解析最新的公网 IP。

原来你执行的 ./ddns -c config.json 不会影响或失效

你之前手动运行的是立即更新一次,定时任务是后台自动运行,两者不冲突。


danger

日志中可能会出现下面这个错误

7月 06 01:14:04 HeiheServer systemd[1]: Started ddns.service - NewFuture ddns.
7月 06 01:14:04 HeiheServer env[45557]: /usr/bin/env: "python": 没有那个文件或目录
7月 06 01:14:04 HeiheServer systemd[1]: ddns.service: Main process exited, code=exited, status=127/n/a
7月 06 01:14:04 HeiheServer systemd[1]: ddns.service: Failed with result 'exit-code'.

这说明 ddns.timer 定时器 是正常运行的,但是 systemd 调用的是「源码版本」的 ddns,即 python run.py,但你的系统 没有安装 python 命令。而我们现在使用的是 二进制版本的 ddns无需 Python,因此应该修改 systemd 的 ddns.service 文件,让它直接执行 /usr/share/DDNS/ddns。详细步骤如下:

① 修改 ddns.service 文件

运行:

sudo vim /usr/lib/systemd/system/ddns.service

将其中这行:

ExecStart=/usr/bin/env python /usr/share/DDNS/ddns -c /etc/DDNS/config.json

改为:

ExecStart=/usr/share/DDNS/ddns -c /etc/DDNS/config.json

② 重新加载 systemd 配置并重启服务

sudo systemctl daemon-reload
sudo systemctl restart ddns.service
sudo systemctl restart ddns.timer

③ 再次查看日志确认是否运行成功:

journalctl -u ddns.service -n 20 --no-pager

应该要有成功输出:

7月 06 01:48:26 HeiheServer ddns[62595]: 2025-07-06T01:48:26 INFO [__main__]: proxy.heihet09.com(A) ==> 115.234.251.61 [via DIRECT]

后续维护说明

  • 配置文件路径:/etc/DDNS/config.json 修改它后,定时器会自动读取,无需手动干预。

  • 观察服务状态

    sudo systemctl status ddns.timer
    sudo systemctl status ddns.service
    
    
    ● ddns.timer - NewFuture ddns timer
         Loaded: loaded (/usr/lib/systemd/system/ddns.timer; enabled; preset: enabled)
         Active: active (waiting) since Sun 2025-07-06 01:48:29 CST; 11min ago
        Trigger: Sun 2025-07-06 02:04:41 CST; 4min 46s left
       Triggers: ● ddns.service
    
    7月 06 01:48:29 HeiheServer systemd[1]: Stopped ddns.timer - NewFuture ddns timer.
    7月 06 01:48:29 HeiheServer systemd[1]: Stopping ddns.timer - NewFuture ddns timer...
    7月 06 01:48:29 HeiheServer systemd[1]: Started ddns.timer - NewFuture ddns timer.
    
    
    ○ ddns.service - NewFuture ddns
         Loaded: loaded (/usr/lib/systemd/system/ddns.service; disabled; preset: enabled)
         Active: inactive (dead) since Sun 2025-07-06 01:59:43 CST; 16s ago
       Duration: 1.686s
    TriggeredBy: ● ddns.timer
        Process: 63202 ExecStart=/usr/share/DDNS/ddns -c /etc/DDNS/config.json (code=exited, status=0/SUCCESS)
       Main PID: 63202 (code=exited, status=0/SUCCESS)
            CPU: 227ms
    
  • 可手动运行一次测试:

    sudo systemctl start ddns.service
    
  • 停用定时器:

    sudo systemctl disable --now ddns.timer
    
  • DDNS 已自动化运行,不建议再使用 crontab 方式,会冲突。

  • 不要断开拨号连接,否则公网 IP 会改变,你要重新拨号获取。

  • 现在 使用这个域名 已经完全等同于 使用其IP,可以用其来 SSH、代理等。