forked from austinhyde/ansible-sshjail
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sshjail.py
128 lines (102 loc) · 4.48 KB
/
sshjail.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import distutils.spawn
import traceback
import os
import shutil
import subprocess
import re
import sys
from ansible import errors
from ansible.callbacks import vvv
import ansible.constants as C
from ansible.runner.connection_plugins.ssh import Connection as SSHConn
SSHJAIL_USE_JAILME = False
class Connection(object):
''' jail-over-ssh based connections '''
def get_jail_path(self):
return self.path
def get_jail_id(self):
return self.jid
def match_jail(self):
code, _, stdout, stderr = self._exec_command(' '.join(['jls', 'name', 'jid', 'path']))
lines = stdout.strip().split('\n')
lines = [ line.split() for line in lines ]
names = dict(
(re.sub(r'\W', '_', name), {
'name': name.strip(),
'jid': jid.strip(),
'path': path.strip(),
}) for name, jid, path in lines
)
if len(names) != len(lines):
vvv("WARNING: This host's jail names are not unique after underscore-substitution!")
# remove \n
jail = names.get(self.jname)
if jail:
self.jid = jail['jid']
self.path = jail['path']
self.jname = jail['name']
vvv('Matched jail: %s' % self.jname)
else:
vvv('No jail found with name: %s' % self.jname)
def get_tmp_file(self):
code, _, stdout, stderr = self._exec_command('mktemp', '', None)
return stdout.strip().split('\n')[-1]
def __init__(self, runner, host, port, user, password, private_key_file, *args, **kwargs):
self.host = host
jaildef, self.jailhost = host.split('@',1)
self.jname = re.sub(r'\W','_',jaildef)
self.runner = runner
self.has_pipelining = False
self.ssh = SSHConn(runner, self.jailhost, port, user, password, private_key_file, *args)
self.jid = None
self.path = None
self.juser = None
self.matched = False
def connect(self, port=None):
self.ssh.connect()
if not self.matched:
self.match_jail()
self.matched = True
return self
def _exec_command(self, cmd, tmp_path='', become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
# oh lordy I hate this approach, but we need to note what user we use to access the jail so put/fetch works
if become_user != None:
self.juser = become_user
return self.ssh.exec_command(cmd, tmp_path, become_user, sudoable, executable, in_data)
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
''' run a command in the jail '''
if SSHJAIL_USE_JAILME:
jcmd = ['jailme', self.jid]
else:
jcmd = ['jexec', self.jname]
if executable:
local_cmd = ' '.join([jcmd[0], jcmd[1], executable, '-c', '"%s"' % cmd])
else:
local_cmd = '%s "%s" "%s"' % (jcmd[0], jcmd[1], cmd)
vvv("JAIL (%s) %s" % (become_user, local_cmd), host=self.host)
return self._exec_command(local_cmd, tmp_path, become_user, True, executable, in_data)
def _normalize_path(self, path, prefix):
if not path.startswith(os.path.sep):
path = os.path.join(os.path.sep, path)
normpath = os.path.normpath(path)
return os.path.join(prefix, normpath[1:])
def put_file(self, in_path, out_path):
''' transfer a file from local to remote jail '''
tmp = self.get_tmp_file()
self.ssh.put_file(in_path, tmp)
out_path = self._normalize_path(out_path, self.get_jail_path())
code, _, stdout, stderr = self._exec_command(' '.join(['chmod 0777',tmp]))
if code != 0:
raise errors.AnsibleError("failed to make temp file %s world writable:\n%s\n%s" % (tmp, stdout, stderr))
code, _, stdout, stderr = self._exec_command(' '.join(['cp',tmp,out_path]), '', self.juser, True)
if code != 0:
raise errors.AnsibleError("failed to move file from %s to %s:\n%s\n%s" % (tmp, out_path, stdout, stderr))
def fetch_file(self, in_path, out_path):
''' fetch a file from remote jail to local '''
tmp = self.get_tmp_file()
in_path = self._normalize_path(in_path, self.get_jail_path())
self._exec_command(' '.join(['mv',in_path,tmp]), '', self.juser, True)
self.ssh.fetch_file(tmp, out_path)
def close(self):
''' terminate the connection; nothing to do here '''
pass