supervisord 上的 Flask 应用 运行 如何打开 SSH 隧道?

How can a Flask app running on supervisord open an SSH tunnel?

注意:我在问题中包含了 Flask,但我怀疑它与 Flask 有任何关系,并且与 supervisordsshtunnel 有关.

基本上,我正在尝试打开 SSH 隧道以连接到数据库并以编程方式执行一些命令。这总是发生在使用 threading 库打开的工人 Thread 上。这可以通过直接调用 Gunicorn 或直接 运行ning 脚本来正常工作。这是函数:

def _get_ssh_server(client):
    """Open an SSH tunnel forwarder and return it."""

    return sshtunnel.SSHTunnelForwarder(
        (os.environ['SSH_HOSTNAME'], int(os.environ['SSH_PORT'])),
        ssh_username=os.environ['SSH_USERNAME'],
        ssh_pkey=os.environ['SSH_PKEY'],
        remote_bind_address=(
            os.environ['REMOTE_BIND_ADDRESS'], 3306),
        local_bind_address=("localhost", int(
            os.environ['LOCAL_BIND_PORT']))
    )

显然那些环境变量存在并且是正确的。当我 运行 在本地直接调用 Gunicorn(甚至在远程服务器上)时,一切正常。

当我 运行 sudo service supervisord restart 并观察日志时,我看到:

ValueError: No password or public key available!

这是 supervisord 应用的命令部分 .ini

command=/usr/local/bin/pipenv run gunicorn -w 3 manage:app

研究告诉我,这与 supervisordssh-agent 的工作方式有关。在 root 和我正在使用的用户上,私钥都存在于 id_rsa 中的 ~/.ssh 中,具有正确的权限。 运行 Gunicorn 直接在两个作品上,将问题隔离到 supervisord

我可能可以使用 screen 或类似的方法让它工作,但这不是首选解决方案。任何帮助将不胜感激。

谢谢!

编辑: 我能够通过以 root 身份删除 /home/<user>/.ssh/id_rsa 文件、终止 SSH 代理并重新启动它来使其失败。我通过 运行ning ssh-add 让它再次工作。 运行 supervisord 还是不行。

如果 supervisor 使用的 SSH 代理与 root 使用的相同,为什么它不起作用?

所以,我设法自己修复了它。我不是 100% 确定为什么它以前不起作用,但我有一个替代解决方案仍然使用相同的工具。

sshtunnel 的文档指出您可以使用文件路径或 paramiko RSAKey 代替 ssh_pkey。我一直在使用 str 文件路径,所以我尝试了另一种方式。

我做的第一件事是将私钥的格式从 OpenSSH 更改为 RSA:

ssh-keygen -p -m PEM -f ~/.ssh/id_rsa

接下来,我将加载 SSH 密钥的代码块更改为:

import paramiko

# ...

def _get_ssh_server(client):
    """Open an SSH tunnel forwarder and return it."""

    return sshtunnel.SSHTunnelForwarder(
        (os.environ['SSH_HOSTNAME'], int(os.environ['SSH_PORT'])),
        ssh_username=os.environ['SSH_USERNAME'],
        ssh_pkey=paramiko.RSAKey.from_private_key_file(os.environ['SSH_PKEY']),
        remote_bind_address=(os.environ['REMOTE_BIND_ADDRESS'], 3306),
        local_bind_address=("localhost", int(os.environ['LOCAL_BIND_PORT'])),
        allow_agent=False,
    )

现在一切正常。