在shell脚本中获取外部IP地址
1. 简介
尤其是在云计算时代,我们经常需要一台机器的外部IP地址。由于脚本在不同的环境中执行,因此有一种从许多位置自动获取此类地址的方法很方便,而且通常是必要的。
在本教程中,我们将讨论如何在 Linux 下的 Bash 中获取外部 IP 地址。首先,我们讨论 IP 地址和网络配置。之后,我们将看到 Bash 如何检查和过滤网络接口信息。接下来,我们将探讨本地路由器和第三方服务如何成为我们外部 IP 的来源。最后,我们讨论一些替代方案。
我们使用 GNU Bash 5.1.4 在 Debian 11 (Bullseye) 上测试了本教程中的代码。它是 POSIX 兼容的,应该可以在任何这样的环境中工作。
2. 互联网协议
让我们从一个简短的复习开始。 Internet 协议 (IP) 地址可以是私有的也可以是公共的。IPv4 和 IPv6 的私有 IP 都在特定范围内。我们将这些范围之外的地址称为公共地址。
此外,IP 可以是内部(本地)或外部的。重要的是,后者只能来自公共范围,因为只有公共 IP 可以通过 Internet 路由 。因此,内部和外部的主要区别在于链接所有者。
事实上,互联网服务提供商 (ISP) 通常是外部 IP 地址的所有者。
3. 组网配置
要选择通过 Bash shell 脚本获取机器外部地址的最佳方式,我们应该知道以下几点:
- 网络中的设备
- 网络接口
- 网络设置
- 执行脚本的位置 实际上,互联网访问通常有单独的硬件 。此外,还可以有一个用于本地网络的接口。
因此,我们将简要介绍不同的设置并用大写字母标记它们。此外,M是我们的客户端机器,*External IP*是我们感兴趣的地址。eif0接口保存该地址。
3.1. 直接连接 (A)
一种可能的网络设置将外部 IP 直接分配给执行脚本的机器:
_____
________ \*外部 IP\* ____| |
(互联网)______________/eif1| 中号 |
(________) 169.254.6.66 \____|_____|
在这个简单的网络中,我们有一台带有一个外部接口的机器。
3.2. 路由设置 (B)
用户设置通常包括使用路由器外部接口的本地计算机:
___
________ \*外部 IP\* __________________ 内部 IP ____| |
(互联网)______________/eif1| 路由器 |iif0\_____________/iif0| 中号 |
(____) 169.254.6.66 \____|________|____/ 192.168.1.x \____|___|
在这里,机器无法直接访问外部接口。
3.3. 路由器主机 (C)
在更专业的环境中,我们可以有一个充当路由器的服务器:
________ ___
________ \*外部 IP\* ____| 路由器 |____ 内部 IP ____| |
(互联网)______________/eif1| 服务器 |iif0\_____________/iif0| 中号 |
(____) 169.254.6.66 \____|________|____/ 192.168.1.x \____|___|
请注意,**简单的路由器没有像路由器服务器那样复杂的命令环境。**虽然设置 B 和 C 之间的差异很小,但它很重要,我们将在下一节中看到。
4. Bash 中的外部网络接口处理
在探索了常见的网络配置之后,让我们通过 Bash shell 脚本获取机器的外部 IP。
4.1. 接口查询
通常,获取任何 IP 地址都是通过ip 命令完成的:
$ ip address show
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
valid_lft forever preferred_lft forever
2: eif0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 06:66:06:66:00:10 brd ff:ff:ff:ff:ff:ff
inet 169.254.6.66/24 brd 169.254.6.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 2001:db8:666:666::10/64 scope global
valid_lft forever preferred_lft forever
3: iif1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 06:66:06:66:00:11 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0
valid_lft forever preferred_lft forever
当然,要使用ip,我们应该在具有外部网络的机器上本地运行它。
在场景 A 和 C 中,我们直接使用接口名称并(通过perl )提取IP 地址:
$ ip address show eif0 | perl -nwe 'print /^\s+inet\s+(.*?)\//;'
169.254.6.66
$ ip address show eif0 | perl -nwe 'print /^\s+inet6\s+(.*?)\//;'
2001:db8:666:666::10
当然,不同之处在于这些命令的运行位置。虽然这是场景 A 中的客户端,但在场景 C 中,我们应该找到一种在路由器服务器上运行上述内容的方法。
4.2. 远程外部 IP 检查
实际上,有一些标准协议可以从远程机器运行本地命令。通常,我们使用ssh :
$ ssh itcodingman@blogdemo 'ip address show eif0'
[...]
为方便起见,我们建议 SSH 密钥已经是:
同样,我们可以使用telnet 、ftp 和其他终端协议。由于许多简单的路由器可能仍然不提供终端访问,我们应该有更多的选择。
5.远程检查外部IP
不管其他协议如何,路由设备通常都有一个 HTTP/S 配置接口。它的结构和可用信息取决于制造商。
不过,我们可以使用wget或curl 之类的工具来获取我们想要的页面。之后,我们可以再次通过perl或类似工具进行过滤。
由于特定的 HTTP/S 门户众多,我们只能提供示例:
$ curl --user USERNAME:PASSWORD http://router/
[...]
$ curl "http://router/" --data "frashnum=&action=login&Frm_Logintoken=TOKEN&Username=USERNAME&Password=PASSWORD"
[...]
获取页面后,我们可以再次使用正则表达式进行过滤,或者使用专门的抓取工具,如quickscrape 。
虽然不建议为此目的在没有库的情况下解析 HTML DOM,但我们仍然可以这样做以轻松提取数据:
$ echo "${PAGE_DATA}" | perl -nwe 'print /input id="internet_ip_address".*?snapshot="(.*?)"/;'
169.254.6.66
尽管有所有这些选项,但有时我们不知道如何在我们的案例中应用它们。我们也可能没有所需的信息,例如用户名或密码。存在一种更通用的方式。
6. 外部IP echo
注意图 A、B 和 C 之间的共同元素——互联网。除非经过特别过滤,否则我们几乎可以在所有配置中使用外部资源来获取面向 Internet 的 IP。虽然方便,但此方法将查询发送到本地网络之外,这可能是不可取的。
6.1. ip
有很多服务可以通过不同的协议获取我们的地址:
$ ssh sshmyip.com
{
"comment": "## Your IP Address is 169.254.6.66 (2904) ##",
"family": "ipv4",
"ip": "169.254.6.66",
"port": "2904",
"protocol": "ssh",
"version": "v1.3.0",
"force_ipv4": "ipv4.telnetmyip.com",
"force_ipv6": "ipv6.telnetmyip.com",
"website": "https://github.com/packetsar/checkmyip",
[...]
}
Connection to sshmyip.com closed by remote host.
Connection to sshmyip.com closed.
$ telnet telnetmyip.com
[...]
$ curl telnetmyip.com
[...]
$ wget -qO- telnetmyip.com
[...]
在本例中,格式是JSON 对象。此类服务的提供者可以返回:
- 只是我们的 IP 地址作为字符串
- 基于我们 IP 地址的位置信息
- 基于协议版本的 IP 地址,例如 IPv4 或 IPv6
- 复杂的数据格式和更多信息
为了获得更多隐私,有一种方法可以在不向未知第三方请求的情况下获取我们的外部 IP 地址。
6.2. 域名服务 (DNS) IP 查询
由于很少有机器没有连接到至少一个 DNS 服务器,我们可以使用我们的 DNS 解析器通过dig 或host 命令获取外部 IP :
$ dig +short myip.opendns.com @resolver1.opendns.com
169.254.6.66
$ host myip.opendns.com resolver1.opendns.com
169.254.6.66
$ dig +short txt ch whoami.cloudflare @1.1.1.1
169.254.6.66
$ dig +short txt o-o.myaddr.test.l.google.com @ns1.google.com
169.254.6.66
在这里,我们使用不同的提供商,但为了增强安全性,我们应该使用为机器配置的 DNS 服务器。这意味着我们不会向尚未知道我们 IP 的机器发送请求。
7. 附加议定书
有时,硬件或配置细节允许或强制执行外部 IP 检查的方式。让我们探索一些场景。
UPnP协议主要面向家庭用户。它们可以轻松实现网络上的自动设备发现和通信。
实际上,我们可以使用upnpc 工具来列出和查询UPnP设备:
$ upnpc -s
upnpc : miniupnpc library test client, version 2.2.1.
(c) 2005-2020 Thomas Bernard.
Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
for more information.
List of UPNP devices found on the network :
desc: http://192.168.1.1:1900/rootDesc.xml
st: urn:schemas-upnp-org:device:InternetGatewayDevice:1
Found valid IGD : http://router:1900/ctl/IPConn
Local LAN ip address : 192.168.1.2
[...]
ExternalIPAddress = 169.254.6.66
[...]
在这里,我们看到了我们的路由器和外部 IP 地址,我们可以通过grep 和cut 等工具过滤掉它们:
$ echo "${UPNP_DATA}" | grep ^ExternalIPAddress | cut -c21-
与UPnP一样,网络地址转换端口映射协议 (NAT-PMP) 支持网络设备之间的信息交换。还有一个名为natpmpc 的工具,它允许我们获取该信息。