-
Notifications
You must be signed in to change notification settings - Fork 0
/
password_utilities.py
114 lines (104 loc) · 4.27 KB
/
password_utilities.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
"""
Utility functions for Fabric deployment scripts
Compatible with Fabric 0.9.1 through 1.2, and Python 2.5 through 2.7.
Temporary file handling works under both Windows and Unix-based OSes.
by Andrew Shearer <ashearerw@shearersoftware.com>
"""
import os
from fabric import api as fapi
from fabric.state import env
def run_prompted(command, password='', prompt='Password[^:]*: ', pty=True):
"""
Re-use Fabric's sudo prompt mechanism for other password prompts
(database, version control, etc.)
prompt gives a regular expression matching the remote prompt. Fabric
will add anchors to make it match an entire line.
If the password param is non-empty, use it. Otherwise prompt locally.
Returns the password (either default or user-entered), which the caller
can then supply as the password parameter to subsequent _run_prompted
calls involving the same system.
Caveat: The prompt displayed locally is the same as the one displayed
for sudo passwords ('Password for <username>@<host>:'), which may be
misleading. But attempting to customize it would be too hacky, even
for this function.
"""
saved_prompt, saved_password = env.sudo_prompt, env.password
env.sudo_prompt, env.password = prompt, password
try:
fapi.run(command, pty=pty)
return env.password
finally:
env.sudo_prompt, env.password = saved_prompt, saved_password
def run_script(script, runner=None, password=None, prompt=None, pty=False):
"""
Upload the given script content and either pipe it into the given command
or, if no command is given, execute it directly (in this case, its shebang
line should provide the command).
If prompt is not empty, password prompts will be recognized and answered
as described for _run_sudo.
The advantage of using this command instead of passing the statements
to "run" (or of using "run" and "echo" to pipe them to a script runner)
is in security: they won't appear on the command line, so they won't
appear momentarily in "ps" and won't be recorded in bash history, both
helpful when including passwords. Also, there's less per-statement
overhead.
"""
temp_remote_file = 'temp-fabric-script'
if runner:
command = '%s < "%s"' % (runner, temp_remote_file)
mode = 0600
else:
command = '"./%s"' % temp_remote_file
mode = 0700
try:
put_data(script, temp_remote_file, mode)
if prompt:
run_prompted(command, password=password, prompt=prompt, pty=pty)
else:
fapi.run(command, pty=pty)
finally:
fapi.run('[ ! -f "%s" ] || rm -f "%s"' % (temp_remote_file,
temp_remote_file))
def sudo_put_data(data, remote_path, uid='root', gid='root', mode=0600):
"""
Create a privileged file with data provided by a string.
The file will first be created in the current home directory, then moved
into place with sudo, because the current user may not have sufficient
permissions for the regular 'put' command to place it there directly.
"""
import tempfile
fd, name = tempfile.mkstemp()
try:
try:
os.write(fd, data)
finally:
os.close(fd)
remote_tempfile = 'temp-fabric-file'
try:
fapi.put(name, remote_tempfile, mode=0777) # make remote tempfile
sudo('mv "%(remote_tempfile)s" "%(remote_path)s"'
' && chown %(uid)s:%(gid)s "%(remote_path)s"'
' && chmod %(mode)o "%(remote_path)s"'
% {'remote_path': remote_path, 'remote_tempfile': remote_tempfile,
'uid': uid, 'gid': gid, 'mode': mode}, pty=True)
finally:
# error cleanup: remove remote tempfile if it still exists
run('[ ! -f "%(remote_tempfile)s" ]'
' || rm -f "%(remote_tempfile)s"'
% {'remote_tempfile': remote_tempfile})
finally:
os.remove(name)
def put_data(data, remote_path, mode=0644):
"""
Create a file with data provided by a string.
"""
import tempfile
fd, name = tempfile.mkstemp()
try:
try:
os.write(fd, data)
finally:
os.close(fd)
fapi.put(name, remote_path, mode=mode)
finally:
os.remove(name)