从Heroku SSH隧道 [英] SSH tunneling from Heroku

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

问题描述

我在Heroku上提供一项服务,让用户使用他们的数据库报告他们自己的数据。我的客户必须将我的Heroku应用程序连接到他们的数据库。



Heroku有可能从我的应用程序(Play Framework / Java)中打开SSH隧道,到他们的机器?



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



谢谢您,
Adrien

解决方案

是的,您可以。



现在走下了这条路:是的, 可以设置从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中的任何文件,并在创建您的动态时执行它。使用脚本文件设置〜/ .ssh / id_rsa和〜/ .ssh / id_rsa.pub,然后以隧道模式启动ssh。



完整配方< h2>

1。生成用于访问外部数据库的密钥对



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

  $ ssh-keygen  - t rsa -Cme@example.com
生成公钥/私钥rsa密钥对。
输入保存密钥的文件(/home/.ssh/id_rsa):/home/.ssh/heroku_id_rsa
输入密码(空密码):
再次输入相同的密码:
您的身份证已保存在/home/.ssh/heroku_id_rsa中。
您的公钥已保存在/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:认证成功(publickey)。 
    ...
    debug1:分支到后台
    debug1:进入交互式会话。

    恭喜。你已经在你自己的机器上建立了隧道到外部数据库。现在说服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 =<您的REMOTE_MYSQL_HOST从上面的值> 
    $ heroku config:设置TUNNEL_USER =<您上面的TUNNEL_USER的值>
    $ heroku config:设置TUNNEL_SITE =<您从上方TUNNEL_SITE的值>



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



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

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

    #!/ bin / bash
    echo $ 0:创建公钥和私钥文件

    #创建.ssh目录
    mkdir -p $ {HOME} /。ssh
    chmod 700 $ {HOME} /。ssh

    #从环境变量中创建公钥和私钥文件。
    回声$ {HEROKU_PUBLIC_KEY}> $ {HOME} /。ssh / heroku_id_rsa.pub
    chmod 644 $ {HOME} / .ssh / heroku_id_rsa.pub

    #注意使用双引号,以保留换行符
    回显$ {HEROKU_PRIVATE_KEY}> $ {HOME} / .ssh / heroku_id_rsa
    chmod 600 $ {HOME} / .ssh / heroku_id_rsa

    #预载known_hosts文件(参见下面的版本2)

    #启动SSH隧道(如果尚未运行)
    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];那么
    echo $ 0:隧道已经在$ {PID}上运行
    其他
    echo $ 0启动隧道
    $ SSH_CMD
    fi



    5。推送配置并在Heroku上进行测试



    您知道钻取...

      $ git add。 
    $ git commit -m'Heroku dyno启动时启动ssh'
    $ git push heroku master

    给它一个漩涡......

      $ heroku run sh 
      


    >运行`sh`连接到终端...运行1926
    bash:创建公钥和私钥文件
    bash:启动隧道
    主机example.com的真实性(11.22。 33.44)'不能建立。
    ECDSA密钥指纹是1f:aa:bb:cc:dd:ee:ff:11:22:33:44:55:66:77:88:99。
    您确定要继续连接(是/否)吗?

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

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



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

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

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

     #预先载入known_hosts文件(参见下面的版本2)
    echo'| 1 | longstringofstuff = ecdsa-sha2-nistp256 more stuff =
    | 1 | morestuff = ecdsa-sha2-nistp256 yetmorestuff ='> $ {HOME} / .ssh / known_hosts

    #启动SSH隧道(如果尚未运行的话)
    ... etc ...



    7。推和测试v2



    您知道钻取...

      $ git add。 
    $ git commit -m'预装known_hosts文件以避免提示'
    $ git push heroku master

    给它一个旋转。幸运的是,你应该看到如下所示的内容:

      $ heroku run sh 
    运行连接到终端的`sh`。 .. up,run.1926
    bash:创建公钥和私钥文件
    bash:启动隧道



    8。调试



    如果隧道未正确设置,请尝试在脚本文件中对SSH命令执行pre-pending -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:
    适配器:mysql2
    数据库:mysql_legacy
    用户名:<%= ENV ['LEGACY_DB_USERNAME'] || 'root'%>
    密码:<%= ENV ['LEGACY_DB_PASSWORD'] || ''%>
    主机:127.0.0.1
    端口:3307

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

      -L 3307:$ {REMOTE_MYSQL_HOST}:3306 



    总结



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



    现在我会去睡觉...

    >

    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.

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

    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.

    Thank you, Adrien

    解决方案

    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.]

    Statement of the problem

    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?

    Short Answer

    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.

    The Full Recipe

    1. Generate keypair for access to the external DB

    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. 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}
    

    where

    • ${REMOTE_MYSQL_HOST} is the address of the remote database. In our case, it is something like long_complicated_string.us-west-2.rds.amazonaws.com
    • ${TUNNEL_USER} is the user account on the site that accesses the database
    • ${TUNNEL_SITE} is the address of the machine that accesses the database

    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.
    

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

    3. Set up configuration variables

    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.

    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. 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. 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
    

    Give it a whirl...

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

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

    6. Create version 2.0 of your script file

    (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=
    

    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. 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. 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}"
    

    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.

    9. (Rails only): Configuring the DB

    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
    

    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
    

    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.

    Now I'll go get some sleep...

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

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