heroku - 来自Heroku的SSH隧道

标签 heroku ssh ssh-tunnel

我正在提供一项托管在Heroku上的服务,该服务使用户可以使用自己的数据库报告自己的数据。我的客户必须将我的Heroku应用程序连接到他们的数据库。他们中的一些人显然害怕让数据通过Internet清晰地传输。

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

注意:我知道SSH tunneling to a remote DB from Heroku?,但是在这个问题上,可以使用内置的Heroku db。

谢谢,
阿德里安

最佳答案

是的你可以。

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

问题陈述

我正在Heroku上运行一个应用程序。该应用程序需要访问外部MySQL数据库(托管在AWS上),并从中获取数据进行分析。使用ssh密钥保护对MySQL数据库的访问,即您无法使用密码访问它:您需要一个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.

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}

哪里
  • $ {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.设置配置变量

    目的是在启动时将〜/ .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=
    

    复制该输出并将其粘贴到“预加载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 ...
    

    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。它将打印很多调试输出。拥有比我更多的头脑的sysadmin friend 应该能够解码该输出以告诉您问题出在哪里。

    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隧道中访问远程数据库。上面的配方有很多假设,但是通过一些自定义,它应该可以满足您的特定需求。

    现在我去睡觉...

    关于heroku - 来自Heroku的SSH隧道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21575582/

    相关文章:

    kubernetes - 如何通过 ssh 隧道使用 kubectl 通过我的 kubernetes API 访问我的内部 ELB?

    docker - 如何连接到在 docker 容器上运行的远程 Jupyter Notebook?

    python - 用于 Python 的轻量级 NLP 框架

    postgresql - 导出 Heroku Postgres 数据库,但排除表

    heroku 抛出错误,但没有告诉我错误位于 : Invalid numeric literal at line 31, 第 7 列中的哪个文件

    python - 带有 Cisco ASA 的 paramiko

    heroku - 部署heroku-buildpack-nodejs和heroku-buildpack-ruby-minimal时出现"Push rejected, failed to compile Multipack app"

    windows - Git SSH 无法连接到自定义端口

    linux - Ubuntu中监控远程登录机器的流量(通过ssh本地端口转发)

    ssh - 我想简化 SSH 隧道来做一个 MySQL 转储和 SCP 文件到本地机器