Подтвердить что ты не робот

SSH-туннелирование от Heroku

Я предоставляю услугу, размещенную на Heroku, которая позволяет пользователям сообщать о своих собственных данных, используя свою базу данных. Мои клиенты должны подключить мое приложение Heroku к своей базе данных. Некоторые из них, очевидно, боятся разрешить передачу данных через Интернет.

Можно ли с Heroku открыть SSH-туннель из моего приложения (Play Framework/Java) на свои компьютеры?

NB: Я знаю SSH-туннелирование с удаленной БД из Heroku?, но по этому вопросу возможно использование встроенного Heroku db.

Спасибо, Adrien

4b9b3361

Ответ 1

Да, вы можете.

Спустившись по этому пути: да, можно настроить туннель SSH от heroku во внешнюю базу данных. [ПРИМЕЧАНИЕ. Мое приложение было написано в Ruby on Rails, но данное решение должно работать на любом языке, размещенном на Heroku.]

Заявление о проблеме

Я запускаю приложение на Heroku. Приложение должно получить доступ к внешней базе данных MySQL (размещенной на AWS), из которой он захватывает данные для анализа. Доступ к базе данных MySQL защищен ключами ssh, т.е. Вы не можете получить к нему доступ с паролем: вам нужна пара ключей ssh. Поскольку Heroku запускает каждую новую версию dyno, как вы можете настроить туннель SSH с соответствующими учетными данными?

Краткий ответ

Создайте файл script, скажем ssh_setup.sh. Поместите его в ${HOME}/. Profile.d/ssh_setup.sh. Heroku заметит любой файл в ${HOME}/. Profile.d и выполнит его, когда он создаст ваш dyno. Используйте файл script для настройки ~/.ssh/id_rsa и ~/.ssh/id_rsa.pub, а затем запустите ssh в режиме туннелирования.

Полный рецепт

1. Создание пары ключей для доступа к внешней БД

Создайте пару ключей и сохраните их в ~/.ssh/heroku_id_rsa и ~/.ssh/heroku_id_rsa.pub. Используйте пустую кодовую фразу (в противном случае динамик Heroku попытается запросить ее при запуске):

$ ssh-keygen -t rsa -C "[email protected]"
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) администратору для внешней БД и попросите доступ с помощью этого ключа. После этого вы сможете ввести следующее в окно оболочки на локальном компьютере:

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

Поздравления. Вы настроили туннелирование на своей машине во внешнюю базу данных. Теперь, чтобы убедить Героку сделать то же самое...

3. Настройка переменных конфигурации

Цель состоит в том, чтобы скопировать содержимое ~/.ssh/heroku_id_rsa и ~/.ssh/heroku_id_rsa.pub в соответствующие каталоги на вашем процессоре Heroku при каждом запуске, но вы ДЕЙСТВИТЕЛЬНО не хотите раскрывать свои личные в файле script.

Вместо этого мы будем использовать переменные конфигурации Heroku, которые просто (и безопасно) устанавливают переменные среды оболочки при запуске dyno.

$ 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 вашего файла script

В домашнем каталоге проекта создайте каталог .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)?

Это проблема, так как это означает, что dyno требует ввода пользователем для продолжения. Но мы собираемся это исправить. Ниже следует несколько уродливый взлом, но он работает. (Если у кого-то есть лучшее решение, прокомментируйте!)

6. Создайте версию 2.0 вашего script файла

(Продолжение сверху) Ответьте yes на приглашение и допустите script до завершения. Теперь мы собираемся записать вывод файла 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" и отредактируйте его таким образом:

# 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. Отладка

Если туннель не настроен правильно, попробуйте предварительно найти аргумент -v (verbose) для команды SSH в файле script:

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): настройка 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

Важно отметить, что хост - это локальный хост (127.0.0.1), а порт (3307) должен соответствовать аргументу -L, указанному в ssh в script:

-L 3307:${REMOTE_MYSQL_HOST}:3306

В заключение

Несмотря на то, что было сказано в другом месте, вы можете туннелировать из Heroku для доступа к удаленной базе данных. Вышеупомянутый рецепт делает много предположений, но с некоторыми настройками он должен работать для ваших конкретных потребностей.

Теперь я немного посплю...