
원격 서버로의 SSH 터널
|CircleCI| |AppVeyor| |readthedocs| |coveralls| |version|
|pyversions| |license|
저자: Pahaz_
저장소: https://github.com/pahaz/sshtunnel/
https://github.com/jmagnusson/bgtunnel 에서 영감을 받았으며, Windows에서는 작동하지 않습니다.
참고: https://github.com/paramiko/paramiko/blob/master/demos/forward.py
paramiko_sshtunnel_은 PyPI에 있으므로 다음을 실행하세요:
::
pip install sshtunnel
or ::
easy_install sshtunnel
or ::
conda install -c conda-forge sshtunnel
소스에서 설치하려면 저장소 <https://github.com/pahaz/sshtunnel>_를 클론하고 다음을 실행하세요::
python setup.py install
테스트를 실행하려면 먼저 tox <https://testrun.org/tox/latest/>_가 필요하며 다음을 실행하세요::
python setup.py test
sshtunnel이 유용한 일반적인 시나리오 중 하나는 아래 그림에 나와 있습니다. 사용자는 SSH 포트(보통 22번 포트)만 접근 가능한 원격 서버의 포트(예: 8080)에 연결해야 할 수 있습니다. ::
----------------------------------------------------------------------
|
-------------+ | +----------+
LOCAL | | | REMOTE | :22 SSH
CLIENT | <== SSH ========> | SERVER | :8080 web service
-------------+ | +----------+
|
FIREWALL (only port 22 is open)
----------------------------------------------------------------------
Fig1: 방화벽으로 차단된 서비스에 SSH 터널을 통해 연결하는 방법.
SSH 서버가 허용하는 경우, 외부(LOCAL CLIENT의 관점)에서 직접 보이지 않는 개인 서버(REMOTE SERVER의 관점에서)에 도달하는 것도 가능합니다. ::
----------------------------------------------------------------------
|
-------------+ | +----------+ +---------
LOCAL | | | REMOTE | | PRIVATE
CLIENT | <== SSH ========> | SERVER | <== local ==> | SERVER
-------------+ | +----------+ +---------
|
FIREWALL (only port 443 is open)
----------------------------------------------------------------------
Fig2: SSH 터널을 통해 PRIVATE SERVER에 연결하는 방법.
API는 터널을 초기화하고 시작하거나 with 컨텍스트를 사용할 수 있으며, 이를 통해 터널의 시작 및 중지를 처리합니다.
위 Fig1에 해당하는 코드는 다음과 같습니다. 원격 서버 주소가 pahaz.urfuclub.ru이고, 비밀번호 인증을 사용하며, 로컬 바인드 포트는 무작위로 할당됩니다.
.. code-block:: python
from sshtunnel import SSHTunnelForwarder
server = SSHTunnelForwarder(
'alfa.8iq.dev',
ssh_username="pahaz",
ssh_password="secret",
remote_bind_address=('127.0.0.1', 8080)
)
server.start()
print(server.local_bind_port) # show assigned local port
# work with `SECRET SERVICE` through `server.local_bind_port`.
server.stop()
직접 접근할 수 없는 개인 서버로의 포트 포워딩 예제입니다. 비밀번호로 보호된 개인 키 인증을 가정하고, 원격 서버의 SSH 서비스는 443번 포트에서 수신 대기 중이며 해당 포트는 방화벽에서 열려 있습니다(Fig2).
.. code-block:: python
import paramiko
import sshtunnel
with sshtunnel.open_tunnel(
(REMOTE_SERVER_IP, 443),
ssh_username="",
ssh_pkey="/var/ssh/rsa_key",
ssh_private_key_password="secret",
remote_bind_address=(PRIVATE_SERVER_IP, 22),
local_bind_address=('0.0.0.0', 10022)
) as tunnel:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('127.0.0.1', 10022)
# do some operations with client session
client.close()
print('FINISH!')
Vagrant MySQL 로컬 포트에 대한 포트 포워딩 예제입니다:
.. code-block:: python
from sshtunnel import open_tunnel
from time import sleep
with open_tunnel(
('localhost', 2222),
ssh_username="vagrant",
ssh_password="vagrant",
remote_bind_address=('127.0.0.1', 3306)
) as server:
print(server.local_bind_port)
while True:
# press Ctrl-C for stopping
sleep(1)
print('FINISH!')
또는 CLI를 간단히 사용할 수도 있습니다:
.. code-block:: console
(bash)$ python -m sshtunnel -U vagrant -P vagrant -L :3306 -R 127.0.0.1:3306 -p 2222 localhost
두 개의 터널을 통해 점프하여 SSH 세션을 여는 예제입니다. SSH 트랜스포트와 터널은 데몬화되어, 종료 시 연결이 중단될 때까지 기다리지 않습니다.
.. code-block:: python
import sshtunnel
from paramiko import SSHClient
with sshtunnel.open_tunnel(
ssh_address_or_host=('GW1_ip', 20022),
remote_bind_address=('GW2_ip', 22),
) as tunnel1:
print('Connection to tunnel1 (GW1_ip:GW1_port) OK...')
with sshtunnel.open_tunnel(
ssh_address_or_host=('localhost', tunnel1.local_bind_port),
remote_bind_address=('target_ip', 22),
ssh_username='GW2_user',
ssh_password='GW2_pwd',
) as tunnel2:
print('Connection to tunnel2 (GW2_ip:GW2_port) OK...')
with SSHClient() as ssh:
ssh.connect('localhost',
port=tunnel2.local_bind_port,
username='target_user',
password='target_pwd',
)
ssh.exec_command(...)
::
$ sshtunnel --help
usage: sshtunnel [-h] [-U SSH_USERNAME] [-p SSH_PORT] [-P SSH_PASSWORD] -R
IP:PORT [IP:PORT ...] [-L [IP:PORT ...]] [-k SSH_HOST_KEY]
[-K KEY_FILE] [-S KEY_PASSWORD] [-t] [-v] [-V] [-x IP:PORT]
[-c SSH_CONFIG_FILE] [-z] [-n] [-d [FOLDER ...]]
ssh_address
Pure python ssh tunnel utils
Version 0.4.0
positional arguments:
ssh_address SSH server IP address (GW for SSH tunnels)
set with "-- ssh_address" if immediately after -R or -L
options:
-h, --help show this help message and exit
-U SSH_USERNAME, --username SSH_USERNAME
SSH server account username
-p SSH_PORT, --server_port SSH_PORT
SSH server TCP port (default: 22)
-P SSH_PASSWORD, --password SSH_PASSWORD
SSH server account password
-R IP:PORT [IP:PORT ...], --remote_bind_address IP:PORT [IP:PORT ...]
Remote bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n
Equivalent to ssh -Lxxxx:IP_ADDRESS:PORT
If port is omitted, defaults to 22.
Example: -R 10.10.10.10: 10.10.10.10:5900
-L [IP:PORT ...], --local_bind_address [IP:PORT ...]
Local bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n
Elements may also be valid UNIX socket domains:
/tmp/foo.sock /tmp/bar.sock ... /tmp/baz.sock
Equivalent to ssh -LPORT:xxxxxxxxx:xxxx, being the local IP address optional.
By default it will listen in all interfaces (0.0.0.0) and choose a random port.
Example: -L :40000
-k SSH_HOST_KEY, --ssh_host_key SSH_HOST_KEY
Gateway's host key
-K KEY_FILE, --private_key_file KEY_FILE
RSA/DSS/ECDSA private key file
-S KEY_PASSWORD, --private_key_password KEY_PASSWORD
RSA/DSS/ECDSA private key password
-t, --threaded Allow concurrent connections to each tunnel
-v, --verbose Increase output verbosity (default: ERROR)
-V, --version Show version number and quit
-x IP:PORT, --proxy IP:PORT
IP and port of SSH proxy to destination
-c SSH_CONFIG_FILE, --config SSH_CONFIG_FILE
SSH configuration file, defaults to ~/.ssh/config
-z, --compress Request server for compression over SSH transport
-n, --noagent Disable looking for keys from an SSH agent
-d [FOLDER ...], --host_pkey_directories [FOLDER ...]
List of directories where SSH pkeys (in the format `id_*`) may be found
.. _Pahaz: https://github.com/pahaz
.. _sshtunnel: https://pypi.python.org/pypi/sshtunnel
.. paramiko: http://www.paramiko.org/
.. |CircleCI| image:: https://circleci.com/gh/pahaz/sshtunnel.svg?style=svg
:target: https://circleci.com/gh/pahaz/sshtunnel
.. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/oxg1vx2ycmnw3xr9?svg=true&passingText=Windows%20-%20OK&failingText=Windows%20-%20Fail
:target: https://ci.appveyor.com/project/pahaz/sshtunnel
.. |readthedocs| image:: https://readthedocs.org/projects/sshtunnel/badge/?version=latest
:target: http://sshtunnel.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. |coveralls| image:: https://coveralls.io/repos/github/pahaz/sshtunnel/badge.svg?branch=master
:target: https://coveralls.io/github/pahaz/sshtunnel?branch=master
.. |pyversions| image:: https://img.shields.io/pypi/pyversions/sshtunnel.svg
.. |version| image:: https://img.shields.io/pypi/v/sshtunnel.svg
:target: sshtunnel
.. |license| image:: https://img.shields.io/pypi/l/sshtunnel.svg
:target: https://github.com/pahaz/sshtunnel/blob/master/LICENSE