forked from userify/shim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shim.py
187 lines (160 loc) · 5.5 KB
/
shim.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#! /usr/bin/env python
# Userify Shim
# Copyright (c) 2012-2014 Userify
# Note: the shim can be easily installed and configured via the shim installer,
# or just installed manually after creating the creds.py file.
# base level imports
try:
import json
except:
import simplejson as json
# Standard Library Imports
import subprocess
import os
import os.path
import signal
import httplib
import sys
import datetime
import time
import traceback
import base64
import urllib
# catch stderr
from subprocess import PIPE as pipe
sys.path.append("/opt/userify")
from creds import api_id, api_key
local_download = urllib.urlretrieve
def userdel(username, permanent=False):
# removes user and renames homedir
removed_dir = "/home/deleted:" + username
home_dir = "/home/" + username
if not permanent:
if os.path.isdir(removed_dir):
qexec(["/bin/rm", "-Rf", removed_dir])
# Debian:
qexec(["/usr/bin/pkill", "--signal", "9", "-u", username])
# RHEL:
qexec(["/usr/bin/pkill", "-9", "-u", username])
qexec(["/usr/sbin/userdel", username])
qexec(["/bin/mv", home_dir, removed_dir])
else:
qexec(["/usr/sbin/userdel", "-r", username])
def useradd(name, username, preferred_shell):
removed_dir = "/home/deleted:" + username
home_dir = "/home/" + username
# restore removed home directory
if not os.path.isdir(home_dir) and os.path.isdir(removed_dir):
qexec(["/bin/mv", removed_dir, home_dir])
cmd = ["/usr/sbin/useradd", "-m"
if not os.path.isdir(home_dir) else "",
"--comment", "userify-" + name,
"-s", preferred_shell if preferred_shell else "/bin/bash",
"--user-group", username]
subprocess.call([i for i in cmd if i])
parse_passwd()
def sudoers_add(username, perm=""):
fname = "/etc/sudoers.d/" + username
text = sudoerstext(username, perm)
if perm:
if not os.path.isfile(fname) or open(fname).read() != text:
open(fname, "w").write(text)
fullchmod("0440", fname)
else:
sudoers_del(username)
def sudoers_del(username):
fname = "/etc/sudoers.d/" + username
if os.path.isfile(fname):
qexec(["/bin/rm", fname])
def sudoerstext(username, perm):
return "\n".join((
"# Generated by userify",
username + " "*10 + perm, ""))
def sshkeytext(ssh_public_key):
return "\n".join((
"# Generated by userify",
"# Paste your new key at console.userify.com.", ssh_public_key, ""))
def sshkey_add(username, ssh_public_key):
userpath = "/home/" + username
sshpath = userpath + "/.ssh/"
failsafe_mkdir(sshpath)
fname = sshpath + "authorized_keys"
text = sshkeytext(ssh_public_key)
if not os.path.isfile(fname) or open(fname).read() != text:
open(fname, "w").write(text)
fullchown(username, sshpath)
def fullchown(username, path):
qexec(["chown", "-R", username+":"+username, path])
def fullchmod(mode, path):
qexec(["chmod", "-R", mode, path])
def qexec(cmd):
print "[shim] exec: \"" + " ".join(cmd) + '"'
try: subprocess.check_call(cmd)
# ,stderr=pipe, stdout=pipe)
except:
traceback.print_exc()
pass
def failsafe_mkdir(path):
try: os.mkdir(path)
except OSError: pass
def auth(id,key):
return base64.b64encode(":".join((api_id, api_key)))
def https(method, path, data=""):
h = httplib.HTTPSConnection("configure.userify.com", timeout=30)
headers = {
"Accept": "text/plain, */json",
"Authorization": "Basic " + auth(api_id, api_key)
}
h.request(method, path, data, headers)
return h
def parse_passwd():
# returns a list of passwd lines, ordered as
# username, unused, uid, gid, comment, homedir, shell
app["passwd"] = [[i.strip() for i in l.split(":")]
for l in open("/etc/passwd").read().strip().split("\n")]
app["passwd"] = [i if len(i)>6 else i.append("") for i in app["passwd"]]
def current_usernames():
return [user[0] for user in app["passwd"]]
def current_userify_users():
"get only usernames created by userify"
return [user for user in app["passwd"] if user[4].startswith("userify-")]
def remove_user(username, permanent=False):
# completely removes user
try: userdel(username, permanent)
except: pass
try: sudoers_del(username)
except: pass
def process_users(good_users):
for username, user in good_users.iteritems():
if username not in current_usernames():
useradd(user["name"], username, user["preferred_shell"])
sshkey_add(username, user["ssh_public_key"])
sudoers_add(username, user["perm"])
for userrow in current_userify_users():
username = userrow[0]
if username not in good_users.keys():
print "[shim] removing" + username
remove_user(username)
def main():
parse_passwd()
h = https("POST", "/api/userify/configure")
response = h.getresponse()
# print response.status, response.reason
commands = json.loads(response.read())
if "error" in commands:
return
process_users(commands["users"])
return commands["shim-delay"] if "shim-delay" in commands else 1
app = {}
try:
print "[shim] start: %s" % time.ctime()
s = time.time()
time_to_wait = main()
elapsed = time.time() - s
print "[shim] elapsed: " + str(int(elapsed * 1000)/1000.0) + "s"
if elapsed < time_to_wait:
print "[shim] sleeping: %s" % (time_to_wait-elapsed)
time.sleep(time_to_wait-elapsed)
except:
time.sleep(1)
raise