1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

paramikoの接続設定をssh/configから読み込む

Last updated at Posted at 2018-10-23
URL
基本 https://qiita.com/cielavenir/items/9d068c49186060a47650
Pythonの.ssh/config読み出し https://qiita.com/cielavenir/items/6aa9e6dc1166ae947c6f
大きいファイルの取得 https://qiita.com/cielavenir/items/f38223c156e4aaab58ad

前説

ホスト名にエイリアスを指定すると、Perl/Rubyの場合は良きに計らってくれるが、Pythonはそうではないので、手動で設定する必要があるらしい。幸い、パーサはparamikoライブラリが用意してくれている。ただ、はまりどころが多い。

port

int()で囲う必要がある。

key_filename

certificatefileidentityfileを結合するのが普通と思うが、返すものが単一の文字列なのか文字列のリストなのかよくわからない。リストでなければリストに変換するgetlist()ラッパーを作った。

sock

proxycommandには$(command)(シェル置換)を記述できるが、paramikoはこれを解釈しない。subprocessを噛ませることでなんとかする必要があった。

まとめ

paramiko_config.py
#!/usr/bin/python
#Py2/Py3 are supported
import os,sys,subprocess,glob
import paramiko

def getlist(h,k):
    if k not in h:
        return []
    if not isinstance(h[k],list):
        return [h.pop(k)]
    return h.pop(k)

def bash_expand(s):
    return subprocess.check_output(['bash','-c','echo "%s"'%s]).decode('utf-8')

def parse_config(hostname, username=None, configfile='~/.ssh/config'):
    configfile = os.path.expanduser(configfile)
    cfg = {
        'hostname':hostname,
        'username':username if username is not None else os.environ['USER'],
        'port':22,
        'compress':False,
    }
    configfile_dir = os.path.dirname(configfile)
    configfiles = [configfile]
    configfiles_added = {configfile}
    while configfiles:
        configfile = configfiles.pop(0)
        if os.path.exists(configfile):
            print('Loading '+configfile)
            with open(configfile) as f:
                for line in f:
                    args=line.split()
                    if args and args[0].lower()=='include':
                        for incl in args[1:]:
                            if incl[0]=='~':
                                incl = os.path.expanduser(incl)
                            elif incl[0]!='/':
                                incl = os.path.join(configfile_dir,incl)
                            for e in sorted(glob.glob(incl)):
                                if e not in configfiles_added:
                                    configfiles_added.add(e)
                                    configfiles.append(e)
                f.seek(0)
                ssh_config = paramiko.SSHConfig()
                ssh_config.parse(f)
                user_config = ssh_config.lookup(hostname)
                print(user_config)
                cfg['hostname'] = user_config.pop('hostname') # hostname could be alias(?)
                cfg['username'] = user_config.pop('user',cfg['username'])
                cfg['port'] = int(user_config.pop('port',cfg['port']))
                if 'compression' in user_config:
                    v = user_config.pop('compression')
                    if v.lower()=='yes':
                        cfg['compress'] = True
                    elif v.lower()=='no':
                        cfg['compress'] = False
                if 'certificatefile' in user_config or 'identityfile' in user_config:
                    if 'key_filename' not in cfg:
                        cfg['key_filename'] = []
                    cfg['key_filename'] += [os.path.expanduser(e) for e in getlist(user_config,'certificatefile')+getlist(user_config,'identityfile')]
                if 'proxycommand' in user_config:
                    cfg['sock'] = paramiko.ProxyCommand(bash_expand(user_config.pop('proxycommand')))
    print('[paramiko config]')
    print(cfg)
    return cfg

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(**parse_config(host))
    with ssh.open_sftp() as sftp:
        with sftp.file('.ssh/authorized_keys','r') as f:
            sys.stdout.write(f.read().decode('utf-8'))

190125

これをベースにしたスクリプトが一定の条件下で動かなかったので、怒りに任せて(笑)Includeを自前実装してしまいました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?