防止 SSH 连接开启时机器进入睡眠状态

防止 SSH 连接开启时机器进入睡眠状态

我试图通过在桌面不活动时将其切换到挂起模式来节省电量。但许多桌面也由其所有者通过 SSH 访问。部署了一个 wakeonlan 解决方案以使所有者能够打开机器,但问题是即使 SSH 连接已打开,机器也会在 10 分钟后再次自动挂起。

我想要做的是将活动的 SSH 会话包含在“活动”定义中。

问题是,可以通过设置 polkit 规则来实现吗?可以通过放置一个在实际暂停之前运行的脚本来实现,如果发现 SSH 会话,则中止该脚本吗?我需要一种干净合法的方式来做到这一点。如果不是这样,那么黑客方法也是受欢迎的。

当前简单的黑客解决方案:编辑/usr/sbin/pm-suspend

#check for SSH sessions, and prevent suspending:
if [ "$(who | grep -cv "(:")" -gt 0 ]; then
    echo "SSH session(s) are on. Not suspending."
    exit 1
fi

这达到了目的。但我不知道更新何时会覆盖文件/usr/sbin/pm-suspend。我也不知道这将如何与其他暂停实现(如 tuxonice)配合使用。

答案1

直到 Ubuntu 14.10 (基于 Upstart)

看一下pm-action(8)并在“FILES”部分中搜索/etc/pm/sleep.d。如果其中一个脚本返回非零退出状态,则会阻止暂停。

更新说明以提高清晰度:

  1. 因此创建一个文件/etc/pm/sleep.d/05_ssh_keepawake

  2. 放一个舍邦#!/bin/sh)以及本文件中问题中提到的代码。

  3. 为其设置执行权限:

    chmod +x /etc/pm/sleep.d/05_ssh_keepawake
    

从 Ubuntu 15.04 开始(基于 systemd)

systemd不使用pm-实用程序来管理其电源状态挂钩,但有自己的基础设施来实现相同的目的。睡眠抑制器检查器不再在睡眠时执行,但必须设置通过抑制行动睡眠(见1)。

因此,你必须在 SSH 会话登录和注销中添加命令,使用 systemd 注册睡眠抑制器(例如通过systemd-inhibit(1)) 随后释放了抑制剂。如果有人知道如何挂接 SSH 登录和注销,我欢迎发表评论或进行编辑,以便我们能够制定相关的步骤和命令。

以下部分正在进行中 – 仅当您知道自己在做什么时才使用它!

您可能能够编写一个 systemd 单元/etc/systemd/system/ssh-inhibit-sleep.service,使其自身成为sleep.target使用该RequiredBy选项的依赖项。如果您的新单元失败(其调用的进程的退出状态为非零),它将执行sleep.target后续的睡眠操作。

[Unit]
Description=Check for running SSH sessions and, if any, inhibit sleep
Before=sleep.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c '! who | grep -qv "\(:0\)"'

[Install]
RequiredBy=sleep.target

与往常一样,您需要激活 systemd 单元才能使它们生效:

sudo systemctl enable ssh-inhibit-sleep.service

欲了解更多信息,请参阅systemd.unit(5)systemd.service(5)

答案2

结合两个相关答案(这里这里),这里有一个/etc/pam_session.sh在 Ubuntu 19.10 上有效的方法(至少):

#!/bin/sh
#
# This script runs when an ssh session opens/closes, and masks/unmasks
# systemd sleep and hibernate targets, respectively.
#
# Inspired by: https://unix.stackexchange.com/a/136552/84197 and
#              https://askubuntu.com/a/954943/388360

num_ssh=$(netstat -nt | awk '$4 ~ /:22$/ && $6 == "ESTABLISHED"' | wc -l)

case "$PAM_TYPE" in
    open_session)
        if [ "${num_ssh}" -gt 1 ]; then
            exit
        fi
        command=mask
        ;;

    close_session)
        if [ "${num_ssh}" -ne 0 ]; then
            exit
        fi
        command=unmask
        ;;

    *)
        exit
esac

logger "${command}ing sleep and suspend targets (num_ssh=${num_ssh})"
sudo systemctl ${command} sleep.target suspend.target

确保添加以下行:

session     optional    pam_exec.so quiet /etc/pam_session.sh

添加到/etc/pam.d/sshd部分中session,并使/etc/pam_session.sh脚本可执行:

chmod +x /etc/pam_session.sh

请注意,该/etc/pam_session.sh脚本似乎在用户登录或注销时运行,因此用户需要sudo访问 mask/unmask systemd 目标。

答案3

在现代systemd系统,您还可以使用ssh.套接字服务systemd-抑制. 而不是像往常一样ssh 服务独立,你可以让systemd管理传入连接。现在每个打开的会话都由ssh.套接字创建基于以下项的实例单元[电子邮件保护],因此只需创建一个直接绑定到该服务即可。这还有额外的好处,即跟踪和记录每个 ssh 连接systemd。然后我们可以使用此标识符在阻止睡眠时提供更多有用的信息。此方法还可以防止降级systemd与其他一些例子一样,有一个失败的单元。

首先你需要检查以确保启用ssh.套接字如果您尚未使用它。

systemctl is-active ssh.socket

应该说积极的否则您将需要启用它:

sudo systemctl disable ssh
sudo systemctl stop ssh
sudo systemctl enable ssh.socket
sudo systemctl start ssh.socket

然后你只需要创建/etc/systemd/系统/[电子邮件保护]

[Unit]
Description=ssh no sleep
BindsTo=ssh@%i.service

[Service]
ExecStart=/usr/bin/systemd-inhibit --mode block --what sleep --who "ssh session "%I --why "session still active" /usr/bin/sleep infinity

[Install]
[email protected]

并使用以下命令启用它:

sudo systemctl enable ssh-no-sleep@

为了[电子邮件保护]要触发别名(用 @ 表示),您必须确保您的 ssh.socket 服务具有[插座]环境接受=是,否则 no sleep 服务将永远不会为每个新连接触发。您可以通过运行以下命令来测试 systemd 默认设置:

sudo systemctl show --no-pager --property Accept ssh.socket

如果这返回,你需要将其覆盖为是的通过创建一个 conf 文件/etc/systemd/system/ssh.socket.d/accept.conf和:

[Socket]
Accept=yes

如果您的系统缺少[电子邮件保护]或者ssh.套接字,下面是来自 Debian Bullseye 的一个示例,您可以参考它:

/etc/systemd/系统/[电子邮件保护]

[Unit]
Description=OpenBSD Secure Shell server per-connection daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=auditd.service

[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=-/usr/sbin/sshd -i $SSHD_OPTS
StandardInput=socket
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755

/etc/systemd/system/ssh.socket

[Unit]
Description=OpenBSD Secure Shell server socket
Before=sockets.target
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run

[Socket]
ListenStream=22
Accept=no

[Install]
WantedBy=sockets.target

保存后,运行systemctl 守护进程重新加载systemctl 重启 ssh.socket,您可以重新运行之前的 show 命令来确认接受=是

现在,当有人尝试暂停时,他们将收到所有阻塞 ssh 会话的有用消息

sudo systemctl suspend

然后将返回类似以下内容:

Operation inhibited by "ssh session 1/127.0.0.1:22/127.0.0.1:12345" (PID 12345 "systemd-inhibit", user root), reason is "session still active".
Operation inhibited by "ssh session 2/2001:db8:1::1:22/2001:db8:2::1:12345" (PID 54321 "systemd-inhibit", user root), reason is "session still active".
Please retry operation after closing inhibitors and logging out other users.
Alternatively, ignore inhibitors and users with 'systemctl suspend -i'.

额外的好处ssh.套接字您还可以使用:

systemctl status ssh.socket

返回活跃用户和系统统计信息:

* ssh.socket - OpenBSD Secure Shell server socket
     Loaded: loaded (/lib/systemd/system/ssh.socket; enabled; vendor preset: enabled)
     Active: active (listening) since Mon 2021-12-20 14:46:05 EST; 1h 6min ago
   Triggers: * ssh@2-2001:db8:1::1:22-2001:db8:2::1:12345.service
             * [email protected]:22-127.0.0.1:12345.service
     Listen: [::]:22 (Stream)
   Accepted: 2; Connected: 2;
      Tasks: 0 (limit: 18775)
     Memory: 16.0K
        CPU: 2ms
     CGroup: /system.slice/ssh.socket

答案4

我自己的解决方案是将以下内容添加到.bashrc/ .zshrc/...

if [ "$SSH_CLIENT" ] &&
     ! pstree -ps $$ |
       grep -q -- '-systemd-inhibit(' >/dev/null; then

  echo "Inhibit automatic standby"
  exec /usr/bin/systemd-inhibit \
    --what=idle --why='Interactive SSH Session' -- \
    "$SHELL" "$@"

fi

它通过/使用在远程路由/包装 shell 来工作systemd-inhibit(并且还确保调用链中只有一个systemd-inhibit,因此在进入子 shell 时不会再次调用它)。

显然,它仅适用于带有 shell 的 SSH 会话。

相关内容