使用 python 时无法在 sudo 模式下推送提交

can't push commit in sudo mode while using python

我编写了一个使用 keyboard 库的 python 小程序 (screen.py)。该程序的本质是通过按ctrl + 1截取屏幕截图并通过按ctrl + 2将它们发送到我的github存储库。使用键盘库要求必须通过sudo启动程序。

screen.py:

import keyboard
import pyautogui
import os

screenshot_num = 0

PICS_DIR = os.path.join(os.getcwd(), "pics")
SEND_TO_GIT_SCRIPT = os.path.join(os.getcwd(), "send.sh")
os.makedirs(PICS_DIR, exist_ok=True)


def make_screenshot():
    global screenshot_num
    myScreenshot = pyautogui.screenshot()
    myScreenshot.save(os.path.join(PICS_DIR, str(screenshot_num) + '.png'))
    screenshot_num += 1


def send_to_git():
    os.system(SEND_TO_GIT_SCRIPT)


keyboard.add_hotkey('Ctrl + 1', make_screenshot)
keyboard.add_hotkey('Ctrl + 2', send_to_git)

keyboard.wait('Alt + q')

问题是当程序执行到调用 send.sh(下面的代码)时,我得到以下错误:

错误:

» sudo python3 screen.py
New Pics
 3 files changed, 0 insertions(+), 0 deletions(-)
 rewrite screen/pics/0.png (97%)
 rewrite screen/pics/1.png (97%)
 rewrite screen/pics/2.png (97%)
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

send.sh:

git add ./pics/*
git commit -m 'New Pics'
git push

我通过 ssh 连接到 github,密钥是通过 sudo ssh-keygen 创建的。我通过 ssh 连接到 github。我添加了使用和不使用 sudo 生成的密钥。

您需要以普通用户身份运行 git 命令。

不幸的是,Python 只允许您放弃当前进程的权限;如果您仍然需要 运行 一些特权代码,您将无法再切换回去。

最佳实践和通常良好的安全卫生是放弃所有不需要根权限的东西。 我试图将您的代码重构为 运行 尽可能少 root,但事实证明在这种情况下这很难。 必须将部分分割成子流程,这在这里没有多大意义。

这是一个快速而肮脏的重构,其中 运行 将 shell 子进程作为调用用户,并修复任何已创建文件的所有权以避免以 root 拥有结束由普通用户创建的文件,可能在他们的主目录中或他们应该控制文件的其他地方。

import os
import subprocess

import keyboard
import pyautogui


screenshot_num = 0

# no need for os.getcwd() here
# see 
PICS_DIR = "pics"
SEND_TO_GIT_SCRIPT = os.path.join(".", "send.sh")


def fix_owner(filename: str) -> None:
    """
    Ensure that the file is owned by the unprivileged invoking user
    """
    os.chown(filename, int(os.environ["SUDO_UID"]), int(os.environ["SUDO_GID"]))

def unprivileged_subprocess(command: str) -> CompletedProcess:
    """
    Run the shell commands as the unprivileged invoking user
    """
    return subprocess.run(
        ["su", "-", os.environ["SUDO_USER"], "-c", command],
        check=True)


os.makedirs(PICS_DIR, exist_ok=True)
fix_owner(PICS_DIR)

def make_screenshot():
    global screenshot_num
    filename = os.path.join(PICS_DIR, str(screenshot_num) + '.png')
    myScreenshot = pyautogui.screenshot(filename)
    fix_owner(filename)
    screenshot_num += 1

def send_to_git():
    unprivileged_subprocess(SEND_TO_GIT_SCRIPT)


keyboard.add_hotkey('Ctrl + 1', make_screenshot)
keyboard.add_hotkey('Ctrl + 2', send_to_git)

keyboard.wait('Alt + q')

如果没有填充预期的环境变量等,这将崩溃并显示无用的错误消息;如果脚本不是 运行 和 sudo.

,那么在尝试真正执行任何操作之前,也许添加一个显式检查以使用 user-friendly 指令退出

也许更好的通用设计是 运行 将特权代码作为持久性子进程 (subprocess.Popen),并在启动后立即放弃主脚本的​​特权。 不幸的是,我对 keyboard 或 PyAutoGUI 的了解还不够,无法尝试在此上下文中实现它。用含糊的话来说,让某些东西打开键盘设备为 root,然后以某种方式将结果传达给调用者?或者只 open 设备处于特权模式就足够了,然后放弃所有后续处理的特权。