-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathsshwrapper.py
executable file
·130 lines (114 loc) · 3.84 KB
/
sshwrapper.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
129
130
#!/usr/bin/env python3
import getpass
import os
import sys
from lib import (
awx_get_inventory_file,
awx_get_vars,
find_executable,
get_hostvars,
get_var_within,
manage_conf_file,
)
def main():
argv = list(sys.argv[1:]) # Copy
bastion_user = None
bastion_host = None
bastion_port = None
remote_user = None
remote_port = 22
default_configuration_file = "/etc/ovh/bastion/config.yml"
cmd = argv.pop()
host = argv.pop()
# check if bastion_vars are passed as env vars in the playbook
# may be usefull if the ansible controller manage many bastions
# example :
# - hosts: all
# gather_facts: false
# environment:
# BASTION_USER: "{{ bastion_user }}"
# BASTION_HOST: "{{ bastion_host }}"
# BASTION_PORT: "{{ bastion_port }}"
#
# will result as : ... '/bin/sh -c '"'"'BASTION_USER=my_bastion_user BASTION_HOST=my_bastion_host BASTION_PORT=22 /usr/bin/python3 && sleep 0'"'"''
for i in list(cmd.split(" ")):
if "bastion_user" in i.lower():
bastion_user = i.split("=")[1]
elif "bastion_host" in i.lower():
bastion_host = i.split("=")[1]
elif "bastion_port" in i.lower():
bastion_port = i.split("=")[1]
# in some cases (AWX in a non containerised environment for instance), the environment is overridden by the job
# so we are not able to get the BASTION vars
# if some vars are still undefined, try to load them from a configuration file
bastion_host, bastion_port, bastion_user = manage_conf_file(
os.environ.get("BASTION_CONF_FILE", default_configuration_file),
bastion_host,
bastion_port,
bastion_user,
)
# lookup on the inventory may take some time, depending on the source, so use it only if not defined elsewhere
# it seems like some module like template does not send env vars too...
if not bastion_host or not bastion_port or not bastion_user:
# check if running on AWX, we'll get the vars in a different way
awx_inventory_file = awx_get_inventory_file()
if os.path.exists(awx_inventory_file):
hostvar = awx_get_vars(host, awx_inventory_file)
else:
hostvar = get_hostvars(host) # dict
# manage the case where a bastion var is defined from another var
# Ex: bastion_host = {{ my_bastion_host }}
bastion_port = get_var_within(
hostvar.get("bastion_port", os.environ.get("BASTION_PORT", 22)), hostvar
)
bastion_user = get_var_within(
hostvar.get(
"bastion_user", os.environ.get("BASTION_USER", getpass.getuser())
),
hostvar,
)
bastion_host = get_var_within(
hostvar.get("bastion_host", os.environ.get("BASTION_HOST")), hostvar
)
for i, e in enumerate(argv):
if e.startswith("User="):
remote_user = e.split("=")[-1]
argv[i] = "User={}".format(bastion_user)
elif e.startswith("Port="):
remote_port = e.split("=")[-1]
argv[i] = "Port={}".format(bastion_port)
# syscall exec
args = (
[
"ssh",
"-p",
bastion_port,
"-q",
"-o",
"StrictHostKeyChecking=no",
"-l",
bastion_user,
bastion_host,
"-T",
]
+ argv
+ [
"--",
"-q",
"-T",
"--never-escape",
"--user",
remote_user,
"--port",
remote_port,
host,
"--",
cmd,
]
)
os.execv(
find_executable("ssh"), # full path mandatory
[str(e).strip() for e in args], # execv() arg 2 must contain only strings
)
if __name__ == "__main__":
main()