【问题标题】:How to handle "sudo ufw" failing in a subshell when the parent shell is closed?当父 shell 关闭时,如何处理子 shell 中的“sudo ufw”失败?
【发布时间】:2022-08-18 11:17:17
【问题描述】:

我正在编写一个 shell 脚本,它将在后台运行以控制 Syncthing 并管理与 Syncthing 相关的 UFW 防火墙。

这里是简化的形式:

#!/bin/bash

sync () {
    # Open the ports which Syncthing communicates on.
    sudo ufw allow syncthing &> /dev/null
    # Start Syncthing, and block script execution here until Syncthing is closed.
    syncthing &> /dev/null
    # Close the ports which Syncthing communicates on once it is closed.
    sudo ufw delete allow syncthing &> /dev/null
}

# Get sudo before the sync function is backgrounded.
sudo -v
# Run the sync function in the background.
sync &

当运行它的终端保持打开状态时,此脚本将按预期工作。

如果运行它的终端在 Syncthing 运行时关闭,则防火墙中的端口不会在 Syncthing 关闭时关闭。

有没有办法让这个脚本正常运行——在 Syncthing 关闭后关闭防火墙中的端口——当它启动的终端在 Syncthing 关闭之前关闭?

这是一个脚本,您可以使用它来试验这种行为。它不需要安装 Syncthing,它会输出到syslog

#!/bin/bash

test_function () {
    echo \'-- Opening port\' | logger -t TEST
    sudo ufw allow 80 | logger -t TEST
    echo \'-- Close the terminal you started this script from in the next 10 seconds\' | logger -t TEST
    sleep 10
    echo \'-- Closing port\' | logger -t TEST
    sudo ufw delete allow 80 | logger -t TEST
}

sudo -v
test_function &

    标签: bash shell sudo ufw


    【解决方案1】:

    我猜您使用sudo -v 创建的sudo 缓存与终端会话相关联,并在您注销时立即消失。

    简单的解决方法是使用sudo 运行整个命令。

    #!/bin/sh
    
    sync () {
        ufw allow syncthing
        su "$SUDO_USER" -c syncthing
        ufw delete allow syncthing
    }
    
    test "$SUDO_USER" && test -w / || {
      echo "${0##*/}: run this script using sudo" >&2
      exit 126
    }
    
    sync >/dev/null 2>&1 &
    

    我还重构了对调用者的重定向;例如,这应该希望可以更轻松地将其更改为将诊断从ufwsyncthing 写入日志文件。

    su 命令以调用用户身份运行syncthing;我不熟悉它的功能,所以如果它需要登录会话或访问您的桌面环境,例如,这可能是不够的。显示一个 GUI(如果你不介意将它作为root 运行,那就有点过分了)。

    只有重定向使用 Bash 语法,所以用可移植的 sh 语法替换那些允许我将 shebang 更改为 #!/bin/sh;在许多系统上,这应该允许此脚本消耗更少的资源。

    或者,更新您的sudoers 权限,以允许您在无密码的情况下运行这些特定的ufw 命令。如果你有/etc/sudoers.d,你可以在那里创建一个新文件来授予自己这些权限。

    you ALL=(root) NOPASSWD: /usr/bin/ufw allow syncthing
    you ALL=(root) NOPASSWD: /usr/bin/ufw delete allow syncthing
    

    (显然,您将you 替换为您的实际帐户名,并可能检查我只是猜测的ufw 的路径)。

    【讨论】:

    • 对我来说重要的是我可以在没有 sudo 的情况下调用我的脚本,因为 sync() 函数最终将包含在一个更大的脚本中,我不想用 sudo 调用它。如果我从您的脚本中删除测试,并将其中的最后一行替换为sudo bash -c "$(declare -f sync); sync &> /dev/null &",我认为这就是我想要的。
    • 您不能在 sh 脚本中使用 &>;我通常发现避免这种特殊的 Bashism 更清楚(它是从 Csh 借来的事实已经让很多人相信这是一个坏主意)。
    • 需要交互式 sudo 似乎可以制作脚本较少的对于集成到更大的脚本很有用。
    • 我注意到的一件事是,现在当 Syncthing 打开其 Web GUI 时,它使用的是 Chromium,而不是我的用户(和 root 用户)默认的 Firefox。我猜是因为我们正在使用 sudo 运行 sync() 函数并在其中摆弄用户,所以我们丢失了一些设置,但我不确定哪些设置或来自哪里。我该怎么说呢?
    • 如果该过程需要 GUI,那么您确实需要在桌面会话中运行它才能使这些东西正常工作。也许将它连接到您的桌面注销操作中?
    【解决方案2】:

    我使用one of triplee's suggestions 为我想要运行的ufw 命令启用无密码sudo 来处理这个问题。

    为此,我在/etc/sudoers.d/ 中创建了一个名为ufw 的文件,其中包含以下内容:

    %sudo ALL=(root) NOPASSWD:/usr/sbin/ufw allow syncthing
    %sudo ALL=(root) NOPASSWD:/usr/sbin/ufw delete allow syncthing
    

    然后使用无密码 sudo,我的脚本变成:

    #!/bin/bash
    
    sync () {
        sudo ufw allow syncthing
        syncthing
        sudo ufw delete allow syncthing
    }
    
    sync &> /dev/null &
    

    现在,即使运行它的终端在syncthing 运行时关闭,ufw delete 命令仍会在syncthing 关闭时触发。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-27
      • 1970-01-01
      • 2013-09-19
      • 2020-08-03
      • 1970-01-01
      • 2023-03-30
      • 2019-07-31
      相关资源
      最近更新 更多