来自 Heroku 的 SSH 隧道 [英] SSH tunneling from Heroku

查看:33
本文介绍了来自 Heroku 的 SSH 隧道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm providing a service hosted on Heroku which lets users report on their own data, using their database. My customers have to connect my Heroku app to their database. Some of them are obviously afraid of letting data transit in clear over the Internet.

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

Is it possible with Heroku to open an SSH tunnel from my app (Play Framework/Java) to their machines?

注意:我知道 SSH 隧道到远程数据库来自 Heroku? 但在这个问题上,使用内置的 Heroku 数据库是可能的.

NB: I'm aware of SSH tunneling to a remote DB from Heroku? but on that question, using the built-in Heroku db was possible.

谢谢,阿德里安

推荐答案

是的,你可以.

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

Yes, you can.

Having now gone down this path: yes, it is possible to set up an SSH tunnel from heroku to an external database. [NOTE: My particular app was written in Ruby on Rails, but the solution given here should work for any language hosted on Heroku.]

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

I am running an app on Heroku. The app needs to access an external MySQL database (hosted on AWS) from which it grabs data for analysis. Access to the MySQL database is protected by ssh keys, i.e. you cannot access it with a password: you need an ssh key pair. Since Heroku starts each dyno fresh, how can you set up an SSH tunnel with the proper credentials?

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

Create a script file, say ssh_setup.sh. Put it in ${HOME}/.profile.d/ssh_setup.sh. Heroku will notice any file in ${HOME}/.profile.d and execute it when it creates your dyno. Use the script file to set up ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub and then launch ssh in tunneling mode.

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

Create a key pair and save it in ~/.ssh/heroku_id_rsa and ~/.ssh/heroku_id_rsa.pub. Use an empty passphrase (otherwise the Heroku dyno will try to prompt for it when it starts up):

$ ssh-keygen -t rsa -C "me@example.com"
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 访问外部数据库

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

2. Test ssh access to the external DB

Send your PUBLIC key (~/.ssh/heroku_id_rsa.pub) to the administrator for the external DB and ask for access using that key. After that, you should be able to type the following into a shell window on your local machine:

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

哪里

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

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

You should get a long string of debugging output that include the following:

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

恭喜.您已经在自己的机器上设置了到外部数据库的隧道.现在说服 Heroku 也这样做......

Congratulations. You've set up tunneling on your own machine to the external database. Now to convince Heroku to do the same...

目标是在 Heroku dyno 启动时将 ~/.ssh/heroku_id_rsa 和 ~/.ssh/heroku_id_rsa.pub 的内容复制到 Heroku dyno 上的相应目录,但您真的不想公开您的私人键入一个脚本文件.

The goal is to copy the contents of ~/.ssh/heroku_id_rsa and ~/.ssh/heroku_id_rsa.pub to the corresponding directories on your Heroku dyno whenever it starts up, but you REALLY don't want to expose your private key in a script file.

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

Instead, we'll use Heroku's configuration variables, which simply (and safely) sets up shell environment variables when launching the dyno.

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

在此过程中,我们还将设置一些其他可能敏感的变量:

While we're at it, we'll set up a few other potentially sensitive variables as well:

$ 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.在该目录中,创建以下内容:

4. Create version 1.0 of your script file

In your project home directory, create a directory .profile.d. In that directory, create the following:

# 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 上测试

你知道练习...

5. Push the configuration and test it on Heroku

You know the drill...

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

试一试...

$ heroku run sh

您可能会看到以下内容:

You may see something like:

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)?

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

This is a problem, since it means the dyno needs user input to continue. But we're about to fix that. What follows is a somewhat ugly hack, but it works. (If someone has a better solution, please comment!)

(从上面继续)对提示回答 yes 并让脚本运行完成.我们现在要捕获 known_hosts 文件的输出:

(Continuing from above) Answer yes to the prompt and let the script run to completion. We're now going to capture the output of the known_hosts file:

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

复制该输出并将其粘贴到预加载 known_hosts"注释下的 ssh-setup.sh 文件中,然后进行编辑,使其看起来像这样:

Copy that output and paste it into your ssh-setup.sh file under the "Preload the known_hosts" comment, and edit so it looks like this:

# 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

你知道练习...

7. Push and test v2

You know the drill...

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

试一试.幸运的话,您应该会看到如下内容:

Give it a whirl. With luck, you should see something like this:

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

8.调试

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

8. Debugging

If the tunnel isn't getting set up properly, try pre-pending a -v (verbose) argument to the SSH command in the script file:

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.它将打印大量调试输出.比我更聪明的系统管理员朋友应该能够解码该输出以告诉您问题出在哪里.

Repeat the git add ... git commit ... git push sequence and call heroku run sh. It will print a lot of debug output. A sysadmin friend with more brains than I have should be able to decode that output to tell you where the problem lies.

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

If you're running Rails, you'll need a way to access the database within your Rails app, right? Add the following to your config/database.yml file (changing the names appropriate):

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 参数:

The important thing to note is that the host is the local host (127.0.0.1) and the port (3307) must match the -L argument given to ssh in the script:

-L 3307:${REMOTE_MYSQL_HOST}:3306

总结

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

In summary

Despite what's been said elsewhere, you can tunnel out of Heroku to access a remote database. The above recipe makes a LOT of assumptions, but with some customizations it should work for your specific needs.

现在我要睡觉了...

这篇关于来自 Heroku 的 SSH 隧道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆