Pythonのparamikoライブラリを使用して、リモートサーバーへSSH接続を行う方法について学びました。
その際に学習した内容について、自分用にアウトプットを行いたいと思います。
概要
今回は、「Pythonのparamikoライブラリを使ってSSH接続を行い、dirコマンドを実行して接続を切る」というシンプルな実装を通して、paramikoライブラリの使い方を学んだ形となっています。
また、以下のような前提のもとで行われています。
前提
- 接続先のリモートサーバーは、Docker Composeで立ち上げたコンテナ(イメージはこちらを使用)である
- 公開鍵認証でDE25519鍵を生成しており、それを使用して接続を行う
- 秘密鍵ファイルは
pathlibの相対パスで読み込み - ポート番号やSSHユーザーネームは
.envファイルから読み込み(セキュリティの観点から)
ソースコード
import paramiko
from pathlib import Path
import os
from dotenv import load_dotenv
def main():
# 秘密鍵ファイルパス
ROOT_DIR = Path(__file__).resolve().parent.parent
key_path = Path(ROOT_DIR, "***", "***")
# .envファイルの読み込み
load_dotenv()
# SSHユーザーネームとポート番号
ssh_port = int(os.getenv("SSH_PORT", 22))
ssh_user = os.getenv("SSH_USER")
# 鍵を読み込む
key = paramiko.Ed25519Key.from_private_key_file(key_path)
# 接続の実行
client = create_ssh_client("localhost", ssh_port, ssh_user, key)
# コマンドの実行
result = execute_command(client)
print(result)
# 終わったら閉じる
client.close()
# SSHClientを使った接続
def create_ssh_client(hostname, port, username, key):
client = paramiko.SSHClient()
# 初回接続時にホストキーを自動追加する設定
# 学習用に、自動的に信頼する`AutoAddPolicy`を使う
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 接続実行
client.connect(
hostname=hostname,
port=port,
username=username,
pkey=key
)
return client
def execute_command(client):
# コマンドを実行
# stdin:標準入力、stdout:標準出力、stderr:標準エラー出力
stdin, stdout, stderr = client.exec_command("dir")
# stdoutから中身を読み取って文字列にする
result = stdout.read().decode('utf-8')
return result
if __name__=="__main__":
main()
ポイント
おさえておきたいポイントはいくつかあります。
接続に必要な秘密鍵の読み込み
key = paramiko.Ed25519Key.from_private_key_file(key_path)
こちらの関数に、秘密鍵が書いてあるファイルのパスを渡すことによってparamikoに秘密鍵を読み込ませています。
接続の実行
client = paramiko.SSHClient()
# 初回接続時にホストキーを自動追加する設定
# 学習用に、自動的に信頼する`AutoAddPolicy`を使う
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 接続実行
client.connect(
hostname=hostname,
port=port,
username=username,
pkey=key
)
paramiko.SSHClientインスタンスを作成し、connectでSSH接続が行われています。
なお、今回は学習用として、「初めて接続するサーバの情報を、確認なしで自動的に信頼して保存する」仕様であるAutoAddPolicyをset_missing_host_key_policyメソッドに渡しています。
しかし、この運用は本番環境ではセキュリティの観点からは非推奨です。この辺りは別途本番用の仕様について学習する必要があるでしょう。
コマンドの実行
SSH接続したリモートサーバーでのコマンドの実行は、client.exec_commandメソッドによって行われました。
# stdin:標準入力、stdout:標準出力、stderr:標準エラー出力
stdin, stdout, stderr = client.exec_command("dir")
# stdoutから中身を読み取って文字列にする
result = stdout.read().decode('utf-8')
このメソッドの出力であるstdin、stdout、stderrは、それぞれ
-
stdin: 標準入力 -
stdout: 標準出力 -
stderr: 標準エラー
を表しています。
今回は、result = stdout.read().decode('utf-8')として、標準出力をデコードして人間が読める形にして、result変数に代入しました。
接続を閉じる
client.close()
接続を行った後は、必ずこのコードを実行することで閉じる処理を入れるべきでしょう。
その他
今回は、dirという単純なコマンドを実行するだけでしたが、もちろん、実践の場面ではもっと複雑な命令を与えることになるでしょう。
その場合は、linuxやMacのコマンドを記すことができる.shファイルに、命令文をまとめて書いておいて、それをparamikoに渡して実行することもできるようです。
まとめ
paramikoを使って、SSH接続を実現することができました。
この機能を、開発中のAPIに導入できるようにしていきたいです。