跳板机管理平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

812 lines
25 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #!/usr/bin/env python3
  2. # -*- coding: UTF-8 -*-
  3. import argparse
  4. import sys
  5. import os
  6. import time
  7. import logging
  8. from threading import Timer
  9. import subprocess
  10. import sqlite3
  11. import json
  12. import urllib.parse as urlparse
  13. from urllib.parse import urlencode
  14. import hashlib
  15. import requests
  16. from flask import request, Flask, redirect, session, render_template, g
  17. from flask_cors import CORS
  18. # 启动flask
  19. app = Flask(__name__,
  20. static_folder="/home/daniel/vue-admin/dist/static",
  21. template_folder="/home/daniel/vue-admin/dist")
  22. app.config['SECRET_KEY'] = os.urandom(24)
  23. CORS(app)
  24. # 命令行参数
  25. gCmdArgs = {}
  26. # 数据库文件
  27. gSqlite3File = "/usr/local/jumpserver/jumpserver.db"
  28. # 默认ssh管理账号
  29. gDefaultSSHAdmin = "ec2-user"
  30. # 默认初始密码
  31. gDefaultInitPassword = "cynking"
  32. # sql语句
  33. gUsersTableSql = "create table if not exists users(id integer primary key autoincrement, name varchar(128) not null UNIQUE, sudo integer not null default 0, isdelete integer not null default 0, desc varchar(128), date timestamp not null default (datetime('now','localtime')))"
  34. gHostsTableSql = "create table if not exists hosts(id integer primary key autoincrement, name varchar(128) not null UNIQUE, ip varchar(32) not null, port integer not null default 22, isdelete integer not null default 0, desc varchar(128), date timestamp not null default (datetime('now','localtime')))"
  35. gHostUserSql = "create table if not exists hostuser(id integer primary key autoincrement, hostname varchar(128) not null, username varchar(128) not null, sudo integer not null default 0, isdelete integer not null default 0, date timestamp not null default (datetime('now','localtime')))"
  36. # 添加跳板机用户脚本
  37. gManagerUserShellFile = "manager_user.sh"
  38. # sso应用信息
  39. SSO_APPID = 18
  40. SSO_APPTOKEN = "b813d5b7e4449177cd844bfb00ba8108"
  41. SSO_URL = URL = "http://sso.cynking.cn/"
  42. SSO_AUTH_URL = "http://sso.cynking.cn/?do=login.s_login&appid=%d" % SSO_APPID
  43. # 服务主页地址
  44. gUrl = "http://192.168.1.44:8080/"
  45. # sso管理
  46. gSsoManager = {}
  47. # sso定时器执行周期
  48. SSO_TIMER_PERIOD = 5 * 60
  49. # sso过期时间
  50. SSO_EXPIRE_TIMEOUT = 24 * 60 * 60
  51. def init():
  52. global gSqlite3File
  53. jumpDb = os.environ.get("JUMPDB")
  54. if jumpDb is not None:
  55. gSqlite3File = jumpDb
  56. else:
  57. print("ERROR: Hadn't set environ var 'JUMPDB' for jump database! mod 755")
  58. exit(1)
  59. # 解析命令行
  60. def parse_cmd():
  61. global gCmdArgs
  62. # 显示默认值
  63. default_value_fmt = " (default: %(default)s)"
  64. # 参数
  65. parser = argparse.ArgumentParser()
  66. parser.add_argument("-host", type=str, help="bind host"+default_value_fmt, default="0.0.0.0")
  67. parser.add_argument("-port", type=int, help="bind port"+default_value_fmt, default=8080)
  68. parser.add_argument("-debug", type=bool, help="enable debug"+default_value_fmt, default=False)
  69. # 解析
  70. gCmdArgs = parser.parse_args()
  71. logging.debug(gCmdArgs)
  72. # 简单校验
  73. if len(gCmdArgs.host) == 0 or gCmdArgs.port <= 0:
  74. parser.print_help()
  75. sys.exit(1)
  76. # 主页url
  77. global gUrl
  78. gUrl = "http://%s:%d/" % (gCmdArgs.host, gCmdArgs.port)
  79. # 只有执行结果
  80. def exec_command(cmd):
  81. return subprocess.call(cmd, shell=True)
  82. # 执行结果 和 输出
  83. def exec_command_output(cmd):
  84. return subprocess.getstatusoutput(cmd)
  85. # 同步目标机sh文件
  86. def sync_remote_control_file(host, port):
  87. status, output = exec_command_output(
  88. "scp -P %d manager_user.sh %s@%s:.manager_user.sh" % (port, gDefaultSSHAdmin, host))
  89. if status != 0:
  90. logging.error("sync_remote_control_file error %s", output)
  91. return -1
  92. return 0
  93. # 重定向
  94. def redirect_sso():
  95. return '<script language="JavaScript">self.location="' + SSO_AUTH_URL + '";</script>'
  96. # 计算md5
  97. def make_md5(data):
  98. md = hashlib.md5()
  99. md.update(data.encode('utf-8'))
  100. return md.hexdigest()
  101. # 获取用户信息
  102. def getUserInfo(params):
  103. params["do"] = "user.getInfo"
  104. params["appid"] = SSO_APPID
  105. params["_time"] = int(time.time())
  106. sort_params = sorted(params.items())
  107. params = {}
  108. for value in sort_params:
  109. params[value[0]] = value[1]
  110. params["sig"] = make_md5(urlencode(params) + SSO_APPTOKEN) # 签名
  111. url = SSO_URL + "cmsapi.php"
  112. resp = requests.get(url, params)
  113. return json.loads(resp.text)
  114. def checkCookie(request):
  115. # 获取http参数
  116. if request.method == "GET":
  117. sso_uid = 0
  118. _sso_uid = request.args.get('sso_uid')
  119. if _sso_uid != None:
  120. sso_uid = int(_sso_uid)
  121. sso_token = request.args.get('sso_token')
  122. elif request.method == "POST":
  123. sso_uid = 0
  124. _sso_uid = request.form.get("sso_uid")
  125. if _sso_uid != None:
  126. sso_uid = int(_sso_uid)
  127. sso_token = request.form.get("sso_token")
  128. # 获取sso信息有就保存
  129. if sso_uid != None and sso_token != None and sso_uid != 0 and len(sso_token) > 0:
  130. session["sso_uid"] = sso_uid
  131. session["sso_token"] = sso_token
  132. # 从session中获取sso信息
  133. ck_uid = session.get("sso_uid")
  134. ck_token = session.get("sso_token")
  135. if ck_uid == None or ck_token == None or ck_uid == 0 or len(ck_token) == 0:
  136. return False
  137. # sso信息未过期则不需要验证
  138. sso_info = gSsoManager.get(ck_uid)
  139. if sso_info != None:
  140. if sso_info["sso_token"] == ck_token:
  141. return True
  142. return False
  143. # # 丢弃
  144. # # 每次请求都验证用户信息太慢了
  145. # params = {"sso_uid": ck_uid, "sso_token": ck_token}
  146. # rets = getUserInfo(params)
  147. # if rets == None:
  148. # return False
  149. # if rets["ret"] == -200:
  150. # logging.error(u"不信任的主机,请添加白名单")
  151. # return False
  152. # if rets["ret"] != 1:
  153. # return False
  154. # rets_data = rets.get("data")
  155. # if rets_data == None:
  156. # return False
  157. # rets_data_uid = rets_data.get("uid")
  158. # if rets_data_uid == None:
  159. # return False
  160. # return True
  161. # 处理请求前回调
  162. @app.before_request
  163. def before_request():
  164. g.isconnect_db = False
  165. if request.path != "/sso" and (not checkCookie(request)):
  166. return redirect_sso()
  167. # 连接db并标志
  168. g.db = connect_db()
  169. g.isconnect_db = True
  170. # 处理请求后回调
  171. @app.after_request
  172. def after_request(response):
  173. if g.isconnect_db:
  174. g.db.close()
  175. # g会被释放掉
  176. return response
  177. # sso回调接口
  178. @app.route('/sso', methods=['GET', 'POST'])
  179. def sso():
  180. return do_sso(request)
  181. # 处理sso回调
  182. def do_sso(request):
  183. if request.method == "GET":
  184. sso_uid = int(request.args.get('sso_uid'))
  185. sso_token = request.args.get('sso_token')
  186. elif request.method == "POST":
  187. sso_uid = int(request.form.get("sso_uid"))
  188. sso_token = request.form.get("sso_token")
  189. # 记录sso信息
  190. gSsoManager[sso_uid] = {
  191. "sso_uid": sso_uid, "sso_token": sso_token, "update_time": int(time.time())}
  192. # 取出参数
  193. params = urlparse.urlparse(request.url).query
  194. return redirect(gUrl + "?" + params)
  195. # 登录
  196. @app.route('/login', methods=['GET', 'POST'])
  197. def login():
  198. return do_login(request)
  199. # 主页
  200. @app.route('/', methods=['GET', 'POST'])
  201. def index():
  202. return render_template("index.html")
  203. # 控制
  204. @app.route('/jump/hosts', methods=['GET', 'POST'])
  205. def hostlist():
  206. return do_hostlist(request)
  207. @app.route('/jump/users', methods=['GET', 'POST'])
  208. def userlist():
  209. return do_userlist(request)
  210. # user动态路由
  211. @app.route('/jump/user/<op>', methods=['GET', 'POST'])
  212. def user_op(op):
  213. if op == "add":
  214. return do_add_user(request)
  215. elif op == "del":
  216. return do_del_user(request)
  217. elif op == "modify":
  218. return do_modify_user(request)
  219. elif op == "hosts":
  220. return do_userhostlist(request)
  221. # host动态路由
  222. @app.route('/jump/host/<op>', methods=['GET', 'POST'])
  223. def host_op(op):
  224. if op == "add":
  225. return do_add_host(request)
  226. elif op == "del":
  227. return do_del_host(request)
  228. elif op == "adduser":
  229. return do_host_adduser(request)
  230. elif op == "deluser":
  231. return do_host_deluser(request)
  232. elif op == "modifyuser":
  233. return do_host_modifyuser(request)
  234. elif op == "users":
  235. return do_hostuserlist(request)
  236. @app.route('/jump/hostuser', methods=['GET', 'POST'])
  237. def hostuser():
  238. return do_hostuserall(request)
  239. def do_login(request):
  240. if request.method == "GET":
  241. username = request.args.get('username')
  242. password = request.args.get('password')
  243. elif request.method == "POST":
  244. username = request.form.get("username") or "admin"
  245. password = request.form.get("password") or "123456"
  246. logging.info("login username:%s password:%s", username, password)
  247. resp = {}
  248. resp["msg"] = "ok"
  249. resp["code"] = 200
  250. resp["user"] = username
  251. resp["sessionkey"] = "cookie-666"
  252. return json.dumps(resp)
  253. # get主机列表
  254. def do_hostlist(request):
  255. hosts = g.db.execute("select id,name,ip,port,desc,date from hosts where isdelete=0;").fetchall()
  256. resp = []
  257. for host in hosts:
  258. res = {}
  259. res["id"] = host[0]
  260. res["name"] = host[1]
  261. res["ip"] = host[2]
  262. res["port"] = host[3]
  263. res["desc"] = host[4]
  264. res["ctime"] = host[5]
  265. resp.append(res)
  266. return json.dumps(resp)
  267. # 获取用户列表
  268. def do_userlist(request):
  269. users = g.db.execute("select id,name,sudo,desc,date from users where isdelete=0;").fetchall()
  270. resp = []
  271. for user in users:
  272. res = {}
  273. res["id"] = user[0]
  274. res["name"] = user[1]
  275. res["sudo"] = user[2]
  276. res["desc"] = user[3]
  277. res["ctime"] = user[4]
  278. resp.append(res)
  279. return json.dumps(resp)
  280. # 添加用户
  281. def do_add_user(request):
  282. if request.method == "GET":
  283. name = request.args.get('name')
  284. desc = request.args.get('desc')
  285. elif request.method == "POST":
  286. name = request.form["name"]
  287. desc = request.form["desc"]
  288. ret = g.db.execute("select count(1) from users where name='%s'" % name).fetchone()
  289. if (len(ret) > 0 and ret[0]) >= 1:
  290. return "user %s exists" % name
  291. status, output = exec_command_output("sudo sh %s add %s \"%s\"" % (
  292. gManagerUserShellFile, name, gDefaultInitPassword))
  293. if status != 0:
  294. logging.error("output=%s", output)
  295. return "error %s" % output
  296. # 新增用户 sql
  297. g.db.execute("insert into users(name,desc) values('%s',\"%s\")" %
  298. (name, desc))
  299. g.db.commit()
  300. return "ok"
  301. # 删除用户
  302. def do_del_user(request):
  303. if request.method == "GET":
  304. username = request.args.get('name')
  305. elif request.method == "POST":
  306. username = request.form["name"]
  307. else:
  308. return "invalid request for del user"
  309. status, output = exec_command_output(
  310. "sudo sh %s del %s" % (gManagerUserShellFile, username))
  311. if status != 0:
  312. logging.error("output=%s", output)
  313. return "error %s" % output
  314. g.db.execute("delete from users where name='%s'" % username)
  315. g.db.execute("delete from hostuser where username='%s'" % username)
  316. g.db.commit()
  317. return "del user %s ok" % username
  318. # 修改用户
  319. def do_modify_user(request):
  320. if request.method == "GET":
  321. username = request.args.get('username')
  322. password = request.args.get('password') or ""
  323. sudo = int(request.args.get('sudo'))
  324. desc = request.args.get('desc')
  325. elif request.method == "POST":
  326. username = request.form.get("username")
  327. password = request.form.get("password") or ""
  328. sudo = int(request.form.get("sudo"))
  329. desc = request.form.get("desc")
  330. else:
  331. return "invalid request for user"
  332. # check param
  333. if sudo != 0 and sudo != 1:
  334. return "invalid request sudo:%d param" % sudo
  335. # 检查
  336. users = g.db.execute("select sudo,desc from users where name='%s'" % username).fetchall()
  337. if len(users) == 0:
  338. logging.error("user(%s) not exitst", username)
  339. return "user(%s) not exitst" % username
  340. user = users[0]
  341. user_sudo = user[0]
  342. user_desc = user[1]
  343. change = False # 是否需要修改
  344. opParam = "sudo" # 操作类型 sudo / unsudo
  345. if sudo == 0:
  346. opParam = "unsudo"
  347. if sudo != user_sudo:
  348. change = True
  349. # 处理sudo权限
  350. status, output = exec_command_output(
  351. "sudo sh manager_user.sh %s %s" % (opParam, username))
  352. if status != 0:
  353. logging.error("%s user user(%s) failed! => output=%s", opParam, username, output)
  354. return "error: %s user user(%s) failed! => output=%s" % (opParam, username, output)
  355. if len(password) > 0:
  356. # 修改用户密码
  357. opParam = "passwd"
  358. status, output = exec_command_output(
  359. "sudo sh manager_user.sh %s %s" % (opParam, username))
  360. if status != 0:
  361. logging.error("%s user user(%s) failed! => output=%s", opParam, username, output)
  362. return "error: %s user user(%s) failed! => output=%s" % (opParam, username, output)
  363. if desc != user_desc:
  364. change = True
  365. # 记录在数据库中
  366. if change:
  367. logging.debug("update users set sudo=%d,desc=\"%s\" where name='%s'", sudo, desc, username)
  368. g.db.execute("update users set sudo=%d,desc=\"%s\" where name='%s'" % (sudo, desc, username))
  369. g.db.commit()
  370. logging.info("modify user:%s successful [output: %s]", username, output)
  371. return "modify user:%s successful [output: %s]" % (username, output)
  372. # 添加主机
  373. def do_add_host(request):
  374. if request.method == "GET":
  375. name = request.args.get('name')
  376. ip = request.args.get('host')
  377. port = int(request.args.get('port'))
  378. desc = request.args.get('desc')
  379. elif request.method == "POST":
  380. name = request.form["name"]
  381. ip = request.form["host"]
  382. port = int(request.form["port"])
  383. desc = request.form["desc"]
  384. ret = g.db.execute(
  385. "select count(1) from hosts where name='%s' or ip='%s'" % (name, ip)).fetchone()
  386. if (len(ret) > 0 and ret[0]) >= 1:
  387. return "alias name(%s) or ip(%s) is exists" % (name, ip)
  388. # 新增用户 sql
  389. g.db.execute("insert into hosts(name,ip,port,desc) values('%s','%s',%d,'%s')" % (
  390. name, ip, port, desc))
  391. g.db.execute("insert into hostuser(hostname,username,sudo) values('%s','%s','%d')" % (
  392. name, gDefaultSSHAdmin, 1))
  393. g.db.commit()
  394. return "add host %s:%s ok" % (name, ip)
  395. # 删除主机
  396. def do_del_host(request):
  397. if request.method == "GET":
  398. hostname = request.args.get('name')
  399. ip = request.args.get('host')
  400. elif request.method == "POST":
  401. hostname = request.form["name"]
  402. ip = request.form["host"]
  403. else:
  404. return "invalid request for del host"
  405. g.db.execute("delete from hosts where name='%s' and ip='%s'" %
  406. (hostname, ip))
  407. g.db.execute("delete from hostuser where hostname='%s'" % hostname)
  408. g.db.commit()
  409. return "delete host %s:%s ok" % (hostname, ip)
  410. # 主机上添加用户
  411. def do_host_adduser(request):
  412. if request.method == "GET":
  413. hostname = request.args.get('hostname')
  414. username = request.args.get('username')
  415. elif request.method == "POST":
  416. hostname = request.form["hostname"]
  417. username = request.form["username"]
  418. else:
  419. return "invalid request for add user to host"
  420. # 检查
  421. ret = g.db.execute("select count(1) from hostuser where hostname='%s' and username='%s'" % (
  422. hostname, username)).fetchone()
  423. if (len(ret) > 0 and ret[0]) >= 1:
  424. logging.error("user(%s) exitst on host(%s)", username, hostname)
  425. return "user(%s) exitst on host(%s)" % (username, hostname)
  426. # 检查
  427. hostips = g.db.execute("select ip,port from hosts where name='%s'" % (hostname)).fetchone()
  428. if hostips == None:
  429. logging.error("host(%s) not exitst on hosts", hostname)
  430. return "host(%s) not exitst on hosts" % hostname
  431. hostip = hostips[0]
  432. hostport = int(hostips[1])
  433. # 同步控制文件
  434. sync_remote_control_file(hostip, hostport)
  435. # 获取公钥
  436. status, publicSshRsaKey = exec_command_output(
  437. "sudo cat /home/%s/.ssh/id_rsa.pub" % username)
  438. if status != 0:
  439. logging.error("cat user(%s) id_rsa.pub failed!", username)
  440. return "error %s" % publicSshRsaKey
  441. logging.debug("ssh %s@%s -p%d sudo sh .manager_user.sh add %s %s \"\"%s\"\"", gDefaultSSHAdmin, hostip, hostport, username, gDefaultInitPassword, publicSshRsaKey)
  442. # 主机上新建用户
  443. status, output = exec_command_output("ssh %s@%s -p%d sudo sh .manager_user.sh add %s %s \"\"%s\"\"" % (
  444. gDefaultSSHAdmin, hostip, hostport, username, gDefaultInitPassword, publicSshRsaKey))
  445. if status != 0:
  446. logging.error("remote add user host(%s) user(%s) failed! => output=%s", hostname, username, output)
  447. return "error: remote add user host(%s) user(%s) failed! => output=%s" % (hostname, username, output)
  448. # 记录在数据库中
  449. g.db.execute("insert into hostuser(hostname,username) values('%s','%s')" % (
  450. hostname, username)).fetchone()
  451. g.db.commit()
  452. logging.info("host remote =>> add user:%s to host:%s successful [output: %s]", username, hostname, output)
  453. return "host remote =>> add user:%s to host:%s successful" % (username, hostname)
  454. # 主机上删除用户
  455. def do_host_deluser(request):
  456. if request.method == "GET":
  457. hostname = request.args.get('hostname')
  458. username = request.args.get('username')
  459. elif request.method == "POST":
  460. hostname = request.form["hostname"]
  461. username = request.form["username"]
  462. else:
  463. return "invalid request for add user to host"
  464. # 检查
  465. ret = g.db.execute("select count(1) from hostuser where hostname='%s' and username='%s'" % (
  466. hostname, username)).fetchone()
  467. if (len(ret) > 0 and ret[0]) == 0:
  468. logging.error("user(%s) not exitst on host(%s)", username, hostname)
  469. return "user(%s) not exitst on host(%s)" % (username, hostname)
  470. # 检查
  471. hostips = g.db.execute("select ip,port from hosts where name='%s'" % (hostname)).fetchone()
  472. if hostips == None:
  473. logging.error("host(%s) not exitst on hosts", hostname)
  474. return "host(%s) not exitst on hosts" % hostname
  475. hostip = hostips[0]
  476. hostport = int(hostips[1])
  477. # 同步控制文件
  478. sync_remote_control_file(hostip, hostport)
  479. # 主机上新建用户
  480. status, output = exec_command_output(
  481. "ssh %s@%s -p%d sudo sh .manager_user.sh del %s" % (gDefaultSSHAdmin, hostip, hostport, username))
  482. if status != 0:
  483. logging.error("remote del user host(%s) user(%s) failed! => output=%s", hostname, username, output)
  484. return "error: remote del user host(%s) user(%s) failed! => output=%s" % (hostname, username, output)
  485. # 记录在数据库中
  486. g.db.execute("delete from hostuser where hostname='%s' and username='%s'" % (
  487. hostname, username))
  488. g.db.commit()
  489. logging.info("host remote =>> del user:%s from host:%s successful [output: %s]", username, hostname, output)
  490. return "host remote =>> del user:%s from host:%s successful" % (username, hostname)
  491. # 主机上修改用户信息
  492. def do_host_modifyuser(request):
  493. if request.method == "GET":
  494. hostname = request.args.get('hostname')
  495. username = request.args.get('username')
  496. password = request.args.get('password') or ""
  497. sudo = int(request.args.get('sudo'))
  498. desc = request.args.get('desc')
  499. elif request.method == "POST":
  500. hostname = request.form.get("hostname")
  501. username = request.form["username"]
  502. password = request.form.get("password") or ""
  503. sudo = int(request.form.get("username"))
  504. desc = request.form.get("desc")
  505. else:
  506. return "invalid request for add user to host"
  507. # 检查
  508. hostusers = g.db.execute("select sudo,desc from hostuser where hostname='%s' and username='%s' and isdelete=0" % (
  509. hostname, username)).fetchone()
  510. if len(hostusers) > 0 and hostusers[0] != None:
  511. logging.error("user(%s) not exitst on host(%s)", username, hostname)
  512. return "user(%s) not exitst on host(%s)" % (username, hostname)
  513. hostuser = hostusers[0]
  514. user_sudo = hostuser[0]
  515. user_desc = hostuser[1]
  516. change = False
  517. opParam = "sudo"
  518. if sudo == 0:
  519. opParam = "unsudo"
  520. # 检查
  521. hostips = g.db.execute("select ip,port from hosts where name='%s'" % (hostname)).fetchone()
  522. if hostips == None:
  523. logging.error("host(%s) not exitst on hosts", hostname)
  524. return "host(%s) not exitst on hosts" % hostname
  525. hostip = hostips[0]
  526. hostport = int(hostips[1])
  527. if sudo != user_sudo:
  528. change = True
  529. # 主机上修改sudo
  530. status, output = exec_command_output(
  531. "ssh %s@%s -p%d sudo sh .manager_user.sh %s %s" % (gDefaultSSHAdmin, hostip, hostport, opParam, username))
  532. if status != 0:
  533. logging.error("remote %s user host(%s) user(%s) failed! => output=%s", opParam, hostname, username, output)
  534. return "error: remote %s user host(%s) user(%s) failed! => output=%s" % (
  535. opParam, hostname, username, output)
  536. if len(password) > 0:
  537. # 主机上修改密码
  538. opParam = "passwd"
  539. status, output = exec_command_output(
  540. "ssh %s@%s -p%d sudo sh .manager_user.sh %s %s %s" % (
  541. gDefaultSSHAdmin, hostip, hostport, opParam, username, password))
  542. if status != 0:
  543. logging.error("remote %s user host(%s) user(%s) failed! => output=%s", opParam, hostname, username, output)
  544. return "error: remote %s user host(%s) user(%s) failed! => output=%s" % (
  545. opParam, hostname, username, output)
  546. if desc != user_desc:
  547. change = True
  548. if change:
  549. # 记录在数据库中
  550. g.db.execute("update hostuser set sudo=%d,desc=%s where hostname='%s' and username='%s'" % (
  551. sudo, desc, hostname, username))
  552. g.db.commit()
  553. logging.info("host remote =>> modify user:%s from host:%s successful [output: %s]", username, hostname, output)
  554. return "host remote =>> modify user:%s from host:%s successful [output: %s]" % (username, hostname, output)
  555. # 获取用户所有的主机列表
  556. def do_userhostlist(request):
  557. if request.method == "GET":
  558. username = request.args.get('username')
  559. elif request.method == "POST":
  560. username = request.form["username"]
  561. else:
  562. return "invalid request for getting user host list"
  563. hosts = g.db.execute(
  564. "select id,name,ip,port,desc,date from hosts where isdelete=0 and name in (select hostname from hostuser where username='%s')" % username).fetchall()
  565. resp = []
  566. for host in hosts:
  567. res = {}
  568. res["id"] = host[0]
  569. res["name"] = host[1]
  570. res["ip"] = host[2]
  571. res["port"] = host[3]
  572. res["desc"] = host[4]
  573. res["ctime"] = host[5]
  574. resp.append(res)
  575. return json.dumps(resp)
  576. # 获取主机上的用户列表
  577. def do_hostuserlist(request):
  578. if request.method == "GET":
  579. hostname = request.args.get('hostname')
  580. elif request.method == "POST":
  581. hostname = request.form["hostname"]
  582. else:
  583. return "invalid request for getting host user list"
  584. users = g.db.execute(
  585. "select id,username,sudo,date from hostuser where isdelete=0 and hostname='%s'" % hostname).fetchall()
  586. resp = []
  587. for user in users:
  588. res = {}
  589. res["id"] = user[0]
  590. res["username"] = user[1]
  591. res["sudo"] = user[2]
  592. res["ctime"] = user[3]
  593. resp.append(res)
  594. return json.dumps(resp)
  595. # 获取所有的用户主机列表
  596. def do_hostuserall(request):
  597. users = g.db.execute(
  598. "select id,hostname,username,sudo,date from hostuser where isdelete=0").fetchall()
  599. resp = []
  600. for user in users:
  601. res = {}
  602. res["id"] = user[0]
  603. res["hostname"] = user[1]
  604. res["username"] = user[2]
  605. res["sudo"] = user[3]
  606. res["ctime"] = user[4]
  607. resp.append(res)
  608. return json.dumps(resp)
  609. # 连接数据库
  610. def connect_db():
  611. return sqlite3.connect(gSqlite3File)
  612. # 初始化表
  613. def init_db():
  614. # 连接数据库
  615. conn = connect_db()
  616. if conn == None:
  617. sys.exit(1)
  618. # 初始化用户表
  619. conn.execute(gUsersTableSql)
  620. # 初始化主机表
  621. conn.execute(gHostsTableSql)
  622. # 创建主机用户表
  623. conn.execute(gHostUserSql)
  624. # sso本地管理
  625. def sso_timeout():
  626. # 当前时间戳
  627. nowtime = int(time.time())
  628. # 需求删除的key
  629. delkeys = []
  630. for sso_uid, sso_info in gSsoManager.items():
  631. if nowtime - sso_info["update_time"] >= SSO_EXPIRE_TIMEOUT:
  632. delkeys.append(sso_uid)
  633. # 删除key
  634. for key in delkeys:
  635. del gSsoManager[key]
  636. # 循环定时器
  637. Timer(SSO_TIMER_PERIOD, sso_timeout).start()
  638. def init_log():
  639. # 设置日志
  640. LOG_FORMAT = "[%(asctime)s %(levelname)s %(filename)s:%(funcName)s:%(lineno)d] => %(message)s"
  641. logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)
  642. def main():
  643. # 初始化检测
  644. init()
  645. # 初始化日志
  646. init_log()
  647. # 解析命令行
  648. parse_cmd()
  649. # 初始化db
  650. init_db()
  651. # 启动sso定时器 秒
  652. Timer(SSO_TIMER_PERIOD, sso_timeout).start()
  653. # 启动HTTP服务
  654. app.run(
  655. host=gCmdArgs.host,
  656. port=gCmdArgs.port,
  657. debug=gCmdArgs.debug
  658. )
  659. # 入口
  660. if __name__ == '__main__':
  661. main()