来自Heroku的SSH隧道

Adr*_*ien 26 ssh heroku ssh-tunnel

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

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

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

谢谢,阿德里安

fea*_*ool 62

是的你可以.

现在已经走了这条路:是的,这可以从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.生成用于访问外部数据库的密钥对

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

$ 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.
Run Code Online (Sandbox Code Playgroud)

2.测试对外部数据库的ssh访问

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

$ ssh -v -i ~/.ssh/heroku_id_rsa -N -L 3307:${REMOTE_MYSQL_HOST}:3306 ${TUNNEL_USER}@${TUNNEL_SITE}
Run Code Online (Sandbox Code Playgroud)

哪里

  • $ {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.
Run Code Online (Sandbox Code Playgroud)

恭喜.您已在自己的计算机上设置隧道到外部数据库.现在说服Heroku做同样的事......

3.设置配置变量

目标是在启动时将〜/ .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`
Run Code Online (Sandbox Code Playgroud)

在我们处理它的同时,我们还会设置一些其他可能敏感的变量:

$ 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>
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

5.按下配置并在Heroku上进行测试

你知道该怎么做...

$ git add .
$ git commit -m 'launching ssh when Heroku dyno starts up'
$ git push heroku master
Run Code Online (Sandbox Code Playgroud)

给它一个旋转......

$ heroku run sh
Run Code Online (Sandbox Code Playgroud)

你可能会看到类似的东西:

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)?
Run Code Online (Sandbox Code Playgroud)

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

6.创建脚本文件的2.0版

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

heroku $ cat ~/.ssh/known_hosts
|1|longstringofstuff= ecdsa-sha2-nistp256 more stuff=
|1|morestuff= ecdsa-sha2-nistp256 yetmorestuff=
Run Code Online (Sandbox Code Playgroud)

复制该输出并将其粘贴到"预加载known_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 ...
Run Code Online (Sandbox Code Playgroud)

7.推送并测试v2

你知道该怎么做...

$ git add .
$ git commit -m 'preload known_hosts file to avoid prompt'
$ git push heroku master
Run Code Online (Sandbox Code Playgroud)

给它一个旋转.幸运的话,你应该看到这样的事情:

$ heroku run sh
Running `sh` attached to terminal... up, run.1926
bash: creating public and private key files
bash: launching tunnel
Run Code Online (Sandbox Code Playgroud)

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}"
Run Code Online (Sandbox Code Playgroud)

重复git add ... git commit ... git push序列并致电heroku run sh.它会打印很多调试输出.一个拥有比我更多大脑的系统管理员朋友应该能够解码该输出以告诉你问题所在.

9.(仅限Rails):配置DB

如果您正在运行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
Run Code Online (Sandbox Code Playgroud)

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

-L 3307:${REMOTE_MYSQL_HOST}:3306
Run Code Online (Sandbox Code Playgroud)

综上所述

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

现在我要睡个好觉......

  • 这很好,谢谢!两个可能的改进:1)对mysql使用ssh隧道的一个问题是它们经常掉线,使用[autossh](http://linux.die.net/man/1/autossh)会自动重新连接.已经有一个[heroku build pack](https://github.com/kollegorna/heroku-buildpack-autossh).2)您可以使用`ssh -o StrictHostKeyChecking = no`解决已知的主机问题 (5认同)
  • 很好的答案,你可以通过使用ssh-keyscan避免用户交互,并将结果放在known_hosts文件中,如`ssh-keyscan -t rsa <REMOTE_MYSQL_HOST >>〜/ .ssh/known_hosts` (2认同)