2021年6月25日金曜日

macOS で paramiko を使ってみた

macOS で paramiko を使ってみた

概要

Python の paramiko を使っていろいろな ssh 接続をしてみました 便利だけどケースによってはハマる点も多いと思います

環境

  • macOS 11.4
  • Python 3.8.3
  • paramiko

インストール

  • pipenv install paramiko scp

普通に ssh する

connect メソッドを使って各種 ssh の基本パラメータを指定するだけです

コネクションを貼るので with を使うのをオススメします

import paramiko

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.100.10', port=22, username='user1', password='xxxxxxxxxx')
    stdin, stdout, stderr = ssh.exec_command('hostname')
    stdin.close()
    print(stdout.read())

トラブルシューティング

paramiko.ssh_exception.SSHException: Server ‘192.168.100.10’ not found in known_hosts

以下で回避できます

ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

AttributeError: ‘NoneType’ object has no attribute ‘time’

以下で回避できます

stdin.close()

もしくはコマンド実行後に 5 秒ほど wait を入れましょう 参考: https://github.com/paramiko/paramiko/issues/1617

鍵を使ってログインする

paramiko.RSAKey を使います 鍵へのパスはフルパスを指定しましょう 相対パスやカレントパスを使うとうまく鍵ファイルを読み込めない場合があります パスフレーズがある場合は一緒に指定します

import paramiko

rsa_key = paramiko.RSAKey.from_private_key_file("/path/to/key/privkey.pem", "xxxxxxxxx")

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.100.10', port=22, username='root', pkey=rsa_key)
    stdin, stdout, stderr = ssh.exec_command('hostname')
    stdin.close()
    print(stdout.read())

scp する

scp モジュールと組み合わせることで使えます paramiko オンリーでは scp は使えません https://github.com/paramiko/paramiko/issues/150

以下はファイルをローカルからサーバにアップロードする方法になります 逆にダウンロードする場合は scp.get を使います

import paramiko
import scp

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.100.10', port=22, username='user1', password='xxxxxxxxxx')
    with scp.SCPClient(ssh.get_transport()) as scp:
       scp.put('hoge.txt', '/tmp/hoge.txt')

鍵を使って scp する

鍵認証+scp を組み合わせるだけです

import paramiko
import scp

rsa_key = paramiko.RSAKey.from_private_key_file("/path/to/key/privkey.pem", "xxxxxxxxx")

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.100.10', port=22, username='root', pkey=rsa_key)
    with scp.SCPClient(ssh.get_transport()) as scp:
       scp.put('hoge.txt', '/tmp/hoge.txt')

プロキシを使う

これがかなり曲者です ProxyCommand と組み合わせて使うのですが %h や %p などのテンプレート変数を paramiko は展開してくれません なので ssh コマンドで使っている ProxyCommand をそのまま使うと

paramiko.ssh_exception.ProxyCommandFailure: ProxyCommand(“nc -X connect -x 192.168.100.20:3128 %h %p”) returned nonzero exit status: Broken pipe」

と言ったエラーが発生します なお以下のサンプルは squid で ssh をプロキシした場合の ProxyCommand の設定になります

192.168.100.20 が squid プロキシで 192.168.100.10 が接続したい ssh サーバになります

import paramiko
with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.100.10',
                port=22,
                username='user1',
                password='xxxxxxxxx',
                sock=paramiko.ProxyCommand("nc -X connect -x 192.168.100.20:3128 192.168.100.10 22"))
    stdin, stdout, stderr = ssh.exec_command('hostname')
    stdin.close()
    print(stdout.read())

なお謎が解けなかったのは ProxyCommand で多段 ssh プロキシを使っている場合でプロキシも ssh でかつ鍵認証の場合に ProxyCommand で指定している ssh サーバの鍵のパスフレーズを指定する方法がわかりませんでした

もしかするとその場合はプロキシサーバにパスフレーズなしの鍵を登録しろってことなのかもしれません

最後に

多段プロキシで paramiko を使う場合はハマりどころがかなり多いと印象です 可能であれば多段プロキシを使わないケースで paramiko は使ったほうが良いかもしれません

参考サイト

0 件のコメント:

コメントを投稿