Ответ 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 для доступа к удаленной базе данных. Вышеупомянутый рецепт делает много предположений, но с некоторыми настройками он должен работать для ваших конкретных потребностей.
Теперь я немного посплю...