systemd:在网络真正启动后(用于WoL目的),在启动时启动服务。

19

我在工作中有一台电脑,有时我会在家里启动它以便访问,但是当它启动并从我们的DHCP服务器获取另一个IP地址时,我该如何访问它呢?

情况和我的“工作流程”如下:

  • 我从家里的电脑连接到办公室的VPN
  • ssh到办公室局域网中的一台专用服务器(它有一个固定的IP地址)
  • 在那台服务器上,我调用一个脚本来广播一个带有我的办公室电脑MAC地址的WoL数据包
  • 我的办公室电脑启动(确实如此,肯定没错!)

现在理论上我应该能够通过SSH连接到我的办公室电脑,只要我知道它的IP地址。 有时它保持不变,有时它会改变。 为了规避这个问题,我有以下想法:

  • 在启动后,我的办公室电脑会通过SSH连接到办公室服务器,并将自己的IP地址写入服务器上的某个文本文件中
  • 我可以通过检查服务器上的那个文件(因为它有固定的IP地址)找到我的办公室电脑的IP地址,然后可以从家里的电脑通过SSH连接到我的办公室电脑
所有的电脑都运行Linux;家里用的是Ubuntu 14.04,办公室服务器上用的是SLES,办公室个人电脑上用的是OpenSUSE 13.1。这些都不是问题。
为了让这一切正常运行,我只需要在办公室个人电脑上有一个脚本,在网络启动并运行时自动执行。
我的脚本(publish_ip.sh)如下所示:
# get own IP address:
ip=$(ip addr show | awk '$1=="inet" && $2 !~ /127\.0\.0\.1/ { split($2, a, "/"); print a[1]}');

# SSH to the office server (10.64.5.84) and write own IP address to a file there:
ssh -x -e none 10.64.5.84 "echo $(date) $ip >> office_pc_address.txt"

为了在启动时运行此脚本,我创建了一个systemd服务文件,名为publish-ip.service,用于我的办公电脑。
[Unit]
Description=publishes own IP address
Wants=network.target
After=network.target

[Service]
Type=oneshot
ExecStartPre=/usr/bin/sleep 30
ExecStart=/home/perldog/bin/publish_ip.sh
User=perldog

[Install]
WantedBy=multi-user.target

但这就是我在办公室电脑上经常遇到的情况:
linux-tz7m:/usr/lib/systemd/system # systemctl status publish-ip.service
publish-ip.service - publishes own IP address
   Loaded: loaded (/usr/lib/systemd/system/publish-ip.service; enabled)
   Active: failed (Result: exit-code) since Mon 2016-02-29 12:17:34 CET; 4 days ago
  Process: 1688 ExecStart=/home/perldog/bin/publish_ip.sh (code=exited, status=255)
  Process: 1016 ExecStartPre=/usr/bin/sleep 30 (code=exited, status=0/SUCCESS)
 Main PID: 1688 (code=exited, status=255)

Feb 29 12:17:34 linux-tz7m publish_ip.sh[1688]: ssh: connect to host 10.64.5.84 port 22: Network is unreachable

显然,我的服务开始并调用我的脚本,但是脚本中的SSH命令失败,显示“网络不可达”。
我尝试了在我的服务文件中的所有设置,以便它只在网络连接正常后运行,但是我无法解决这个问题。我尝试了“Wants=network.target”,“After=network.target”,“WantedBy=multi-user.target”,甚至插入了“ExecStartPre=/usr/bin/sleep 30”。但是都没有起作用。每次当我的脚本被调用并尝试SSH连接到办公室服务器时,我总是得到“网络不可达”的错误。
问题:我在我的服务文件中需要哪些设置,才能确保它只在办公室服务器可通过SSH连接时运行?
注意:当我在办公室,我的办公电脑正常运行时,我的脚本和服务都能正常工作,即“systemctl start publish-ip.service”可以正常运行,没有任何错误。

1
一个相关的解决方案,https://stackoverflow.com/a/43192575/1451311。将 network.target 更改为 network-online.target 解决了我的问题。 - mgilbert
4个回答

23
我尝试了所有这些目标,它们在DHCP获取IP地址之前都已经达成了。真是让人费解。
  • network-online.target
  • remote-fs.target
  • nfs-client.target
  • dbus.service
启用这两个是有效的。
systemctl enable systemd-networkd.service systemd-networkd-wait-online.service

然后设置
[Unit]
After=systemd-networkd-wait-online.service
Wants=systemd-networkd-wait-online.service

现在它在DHCP获取到IP地址之后开始运行(在我的情况下是一个挂载点,但也可能是您的服务)。
(在debian9/stretch上)

你会在哪里添加你发布的这两行代码? 你要怎么做才能让systemd-networkd-wait-online.service在启动时自动运行?这两行代码就足够了吗? - sancho.s ReinstateMonicaCellio
1
我记不清了,而且我已经辞职了,但我想我只是把它放在.service或.mount文件中。有关更多信息,请访问我的其他帖子链接。 - Peter V. Mørch

22

你可以通过将ping循环作为ExecStartPre脚本来延迟工作的启动:

[Service]
   ExecStartPre=/bin/sh -c 'until ping -c1 google.com; do sleep 1; done;'

在这里找到


5
我尝试了这里提供的所有解决方案,尽管看起来有些不太正规,但这才是有效的方法。谢谢。 - David Chan
3
如果网络已经连接,until ... 将延迟1秒执行。反转逻辑:/bin/sh -c 'while ! ping -c1 1.1.1.1; do sleep 1; done'。此外,如果您的 ping 支持,建议添加 -W.5 的超时设置。 - xebeche

3

这不是一个完整的解决方案,但是我设置了一个SSH隧道,一旦计算机启动,它就会重新启动,每5秒钟重新启动一次,以防止在网络仍然下降时被杀死; 如果您可以使用固定IP系统作为堡垒主机,您可以将其与WOL配对使用:

[Unit]
Description=Keep open a reverse tunnel to my home server
After=network.target

[Service]
ExecStart=/usr/bin/ssh -NT hometunnel
RestartSec=5
Restart=always

[Install]
WantedBy=multi-user.target

root的.ssh/config文件中相关部分:

Host                    hometunnel
HostName                <redacted>
IdentityFile    /root/.ssh/<redacted>
User                    <redacted>
RemoteForward   <redacted> localhost:22
ExitOnForwardFailure yes
ServerAliveInterval 60
ServerAliveCountMax 5

1
看起来也可以尝试 ExecStartPre=/bin/sh -c 'until ping -c1 google.com; do sleep 1; done;'; 来源:https://github.com/coreos/bugs/issues/1966#issuecomment-301825679 - Iiridayn

0

曾经有一个叫做 arpwatch 的工具,它记录了所有网络设备的 IP 地址和 MAC 地址,因此您可以简单地 grep 您计算机的 MAC 地址并获取 IP。

还有一个叫做 arping 的工具,你可以通过输出 grep MAC 地址。

另一个选项是在您的计算机上保持随机端口开放,并使用 nmap 扫描整个网络以获得该特定端口,或者扫描整个网络以查找处于活动状态的主机并 grep 您的 MAC 地址...

nmap -sP 192.168.1.0/24

谢谢,但是我的办公服务器上没有安装arpwatch、arping或nmap。:-( 我已经搜索了“如何从MAC地址获取IP地址”,但没有结果。这就是我想到自己的方法的原因。我承认这个方法非常复杂。 - PerlDuck
@PerlDog,ARP是将第三层(IP)地址解析为第二层(MAC地址),根据您的评论,这似乎与您想要的相反。有一个地方可以查找MAC地址到IP地址的对应关系,那就是DHCP服务器,假设主机使用DHCP分配其IP地址。 - Ron Maupin
@RonMaupin 谢谢,但我尝试了所有这些 ping -b 10.64.255.255; arp -na | grep $MAC 的建议。它们都没有起作用,我很确定我没有访问我们网络中 DHCP 服务器的“查询”权限(我不是 root 用户)。所以我想到了这个 publish-ip 脚本,但它也不起作用 :-( 因为它在网络启动之前运行。 - PerlDuck
1
@PerlDog,IP和以太网从未设计用于局域网发现。您可以获取一个具有所需功能的源(DHCP服务器)。许多主机现在都有防火墙,可以并经常会阻止ping和其他可能用于局域网发现的东西。连接到有线以太网交换机的具有软件防火墙的静态分配主机几乎在局域网上不可见。您可以编写一个应用程序,在局域网上发送每个可能的IP地址的ARP请求,但这可能是非常大量的地址,并且需要很长时间。 - Ron Maupin
@RonMaupin 当然。为了避免这种情况,我的脚本会启动。如果你喜欢的话,它有点像穷人版的DynDNS,即将IP发布到一个众所周知的位置。我认为我应该重新表述我的问题(或者发布一个新的问题),以便更强调_systemd/network up_问题。我写了所有这些东西,因为我想避免出现XY问题 - PerlDuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接