Heroku 的 SSH 隧道

2024-02-23

我提供了一个在 Heroku 上托管的服务,它允许用户使用他们的数据库报告他们自己的数据。我的客户必须将我的 Heroku 应用程序连接到他们的数据库。他们中的一些人显然害怕让数据在互联网上清晰传输。

Heroku 是否可以打开从我的应用程序(Play Framework/Java)到他们的机器的 SSH 隧道?

注意:我知道从 Heroku 到远程数据库的 SSH 隧道? https://stackoverflow.com/questions/11917883/ssh-tunneling-to-a-remote-db-from-heroku但在这个问题上,使用内置的 Heroku 数据库是可能的。

谢谢你, 阿德里安


是的你可以。

现在已经走上了这条路:是的,它is可以设置从 heroku 到外部数据库的 SSH 隧道。 [注意:我的特定应用程序是用 Ruby on Rails 编写的,但此处给出的解决方案应该适用于 Heroku 上托管的任何语言。]

问题陈述

我正在 Heroku 上运行一个应用程序。该应用程序需要访问外部 MySQL 数据库(托管在 AWS 上),从中获取数据进行分析。对 MySQL 数据库的访问受 ssh 密钥保护,即您无法使用密码访问它:您需要 ssh 密钥对。由于 Heroku 重新启动每个 dyno,如何使用正确的凭据设置 SSH 隧道?

简答

创建一个脚本文件,例如 ssh_setup.sh。将其放入 ${HOME}/.profile.d/ssh_setup.sh 中。 Heroku 会注意到 ${HOME}/.profile.d 中的任何文件,并在创建 dyno 时执行它。使用脚本文件设置 ~/.ssh/id_rsa 和 ~/.ssh/id_rsa.pub,然后以隧道模式启动 ssh。

完整的食谱

1. 生成访问外部DB的密钥对

创建密钥对并将其保存在 ~/.ssh/heroku_id_rsa 和 ~/.ssh/heroku_id_rsa.pub 中。使用空密码(否则 Heroku dyno 将在启动时尝试提示输入):

$ ssh-keygen -t rsa -C "[email protected] /cdn-cgi/l/email-protection"
Generating public/private rsa key pair.
Enter file in which to save the key (/home/.ssh/id_rsa): /home/.ssh/heroku_id_rsa
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/.ssh/heroku_id_rsa.
Your public key has been saved in /home/.ssh/heroku_id_rsa.pub.

2.测试ssh访问外部DB

将您的公钥 (~/.ssh/heroku_id_rsa.pub) 发送给外部数据库的管理员,并请求使用该密钥进行访问。之后,您应该能够在本地计算机上的 shell 窗口中输入以下内容:

$ ssh -v -i ~/.ssh/heroku_id_rsa -N -L 3307:${REMOTE_MYSQL_HOST}:3306 ${TUNNEL_USER}@${TUNNEL_SITE}

where

  • ${REMOTE_MYSQL_HOST} 是远程数据库的地址。在我们的例子中,它类似于 long_complicated_string.us-west-2.rds.amazonaws.com
  • ${TUNNEL_USER} 是访问数据库的站点上的用户帐户
  • ${TUNNEL_SITE}是访问数据库的机器地址

您应该得到一长串调试输出,其中包括以下内容:

debug1: Authentication succeeded (publickey).
...
debug1: forking to background
debug1: Entering interactive session.

恭喜。您已在自己的计算机上设置了通往外部数据库的隧道。现在说服 Heroku 也这样做......

3. 设置配置变量

目标是在 Heroku dyno 启动时将 ~/.ssh/heroku_id_rsa 和 ~/.ssh/heroku_id_rsa.pub 的内容复制到 Heroku dyno 上的相应目录,但你真的不想在脚本文件。

相反,我们将使用 Heroku 的配置变量,它在启动 dyno 时简单(且安全)地设置 shell 环境变量。

$ heroku config:set HEROKU_PRIVATE_KEY=`cat ~/.ssh/heroku_rsa_id`
$ heroku config:set HEROKU_PUBLIC_KEY=`cat ~/.ssh/heroku_rsa_id.pub`

当我们这样做时,我们还将设置一些其他潜在的敏感变量:

$ heroku config:set REMOTE_MYSQL_HOST=<your value of REMOTE_MYSQL_HOST from above>
$ heroku config:set TUNNEL_USER=<your value of TUNNEL_USER from above>
$ heroku config:set TUNNEL_SITE=<your value of TUNNEL_SITE from above>

4. 创建脚本文件的 1.0 版本

在项目主目录中,创建一个目录 .profile.d。在该目录中,创建以下内容:

# file: .profile.d/ssh-setup.sh

#!/bin/bash
echo $0: creating public and private key files

# Create the .ssh directory
mkdir -p ${HOME}/.ssh
chmod 700 ${HOME}/.ssh

# Create the public and private key files from the environment variables.
echo "${HEROKU_PUBLIC_KEY}" > ${HOME}/.ssh/heroku_id_rsa.pub
chmod 644 ${HOME}/.ssh/heroku_id_rsa.pub

# Note use of double quotes, required to preserve newlines
echo "${HEROKU_PRIVATE_KEY}" > ${HOME}/.ssh/heroku_id_rsa
chmod 600 ${HOME}/.ssh/heroku_id_rsa

# Preload the known_hosts file  (see "version 2" below)

# Start the SSH tunnel if not already running
SSH_CMD="ssh -f -i ${HOME}/.ssh/heroku_id_rsa -N -L 3307:${REMOTE_MYSQL_HOST}:3306 ${REMOTE_USER}@${REMOTE_SITE}"

PID=`pgrep -f "${SSH_CMD}"`
if [ $PID ] ; then
    echo $0: tunnel already running on ${PID}
else
    echo $0 launching tunnel
    $SSH_CMD
fi

5.推送配置并在Heroku上测试

你知道该怎么做...

$ git add .
$ git commit -m 'launching ssh when Heroku dyno starts up'
$ git push heroku master

试一试...

$ heroku run sh

您可能会看到类似以下内容:

Running `sh` attached to terminal... up, run.1926
bash: creating public and private key files
bash: launching tunnel
The authenticity of host 'example.com (11.22.33.44)' can't be established.
ECDSA key fingerprint is 1f:aa:bb:cc:dd:ee:ff:11:22:33:44:55:66:77:88:99.
Are you sure you want to continue connecting (yes/no)?

这是一个问题,因为这意味着测功机需要用户输入才能继续。但我们即将解决这个问题。接下来是一个有点丑陋的黑客,但它有效。 (如果有人有更好的解决方案,请评论!)

6. 创建脚本文件的 2.0 版本

(接上文)回答yes到提示符并让脚本运行完成。我们现在要捕获known_hosts 文件的输出:

heroku $ cat ~/.ssh/known_hosts
|1|longstringofstuff= ecdsa-sha2-nistp256 more stuff=
|1|morestuff= ecdsa-sha2-nistp256 yetmorestuff=

复制该输出并将其粘贴到“Preload theknown_hosts”注释下的 ssh-setup.sh 文件中,然后进行编辑,使其如下所示:

# Preload the known_hosts file  (see "version 2" below)
echo '|1|longstringofstuff= ecdsa-sha2-nistp256 more stuff=
|1|morestuff= ecdsa-sha2-nistp256 yetmorestuff=' > ${HOME}/.ssh/known_hosts

# Start the SSH tunnel if not already running
... etc ...

7. 推送并测试v2

你知道该怎么做...

$ git add .
$ git commit -m 'preload known_hosts file to avoid prompt'
$ git push heroku master

试一试。幸运的话,您应该会看到类似这样的内容:

$ heroku run sh
Running `sh` attached to terminal... up, run.1926
bash: creating public and private key files
bash: launching tunnel

8. 调试

如果隧道未正确设置,请尝试在脚本文件中的 SSH 命令前添加 -v(详细)参数:

SSH_CMD="ssh -v -f -i ${HOME}/.ssh/heroku_id_rsa -N -L ${LOCAL_PORT}:${REMOTE_MYSQL_HOST}:${MYSQL_PORT} ${REMOTE_USER}@${REMOTE_SITE}"

重复git add ... git commit ... git push序列和调用heroku run sh。它将打印大量调试输出。一个比我更有头脑的系统管理员朋友应该能够解码该输出,告诉你问题出在哪里。

9.(仅限Rails):配置数据库

如果您正在运行 Rails,您将需要一种方法来访问 Rails 应用程序中的数据库,对吧?将以下内容添加到您的config/database.yml文件(适当更改名称):

mysql_legacy:
  adapter: mysql2
  database: mysql_legacy
  username: <%= ENV['LEGACY_DB_USERNAME'] || 'root' %>
  password: <%= ENV['LEGACY_DB_PASSWORD'] || '' %>
  host: 127.0.0.1
  port: 3307

需要注意的重要一点是,主机是本地主机(127.0.0.1),端口(3307)必须与脚本中给 ssh 的 -L 参数匹配:

-L 3307:${REMOTE_MYSQL_HOST}:3306

总之

尽管在其他地方已经说过,您可以从 Heroku 中通过隧道访问远程数据库。上面的配方做了很多假设,但通过一些自定义,它应该可以满足您的特定需求。

现在我要去睡觉了...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Heroku 的 SSH 隧道 的相关文章

随机推荐