server.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. from flask import Flask
  2. from flask_restful import Resource, Api
  3. from flask import jsonify
  4. from flask import request
  5. from threading import Thread
  6. import docker
  7. import signal
  8. import time
  9. import os
  10. import re
  11. import sys
  12. docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
  13. app = Flask(__name__)
  14. api = Api(app)
  15. class containers_get(Resource):
  16. def get(self):
  17. containers = {}
  18. try:
  19. for container in docker_client.containers.list(all=True):
  20. containers.update({container.attrs['Id']: container.attrs})
  21. return containers
  22. except Exception as e:
  23. return jsonify(type='danger', msg=str(e))
  24. class container_get(Resource):
  25. def get(self, container_id):
  26. if container_id and container_id.isalnum():
  27. try:
  28. for container in docker_client.containers.list(all=True, filters={"id": container_id}):
  29. return container.attrs
  30. except Exception as e:
  31. return jsonify(type='danger', msg=str(e))
  32. else:
  33. return jsonify(type='danger', msg='no or invalid id defined')
  34. class container_post(Resource):
  35. def post(self, container_id, post_action):
  36. if container_id and container_id.isalnum() and post_action:
  37. if post_action == 'stop':
  38. try:
  39. for container in docker_client.containers.list(all=True, filters={"id": container_id}):
  40. container.stop()
  41. return jsonify(type='success', msg='command completed successfully')
  42. except Exception as e:
  43. return jsonify(type='danger', msg=str(e))
  44. elif post_action == 'start':
  45. try:
  46. for container in docker_client.containers.list(all=True, filters={"id": container_id}):
  47. container.start()
  48. return jsonify(type='success', msg='command completed successfully')
  49. except Exception as e:
  50. return jsonify(type='danger', msg=str(e))
  51. elif post_action == 'restart':
  52. try:
  53. for container in docker_client.containers.list(all=True, filters={"id": container_id}):
  54. container.restart()
  55. return jsonify(type='success', msg='command completed successfully')
  56. except Exception as e:
  57. return jsonify(type='danger', msg=str(e))
  58. elif post_action == 'exec':
  59. if not request.json or not 'cmd' in request.json:
  60. return jsonify(type='danger', msg='cmd is missing')
  61. if request.json['cmd'] == 'df' and request.json['dir']:
  62. try:
  63. for container in docker_client.containers.list(filters={"id": container_id}):
  64. # Should be changed to be able to validate a path
  65. directory = re.sub('[^0-9a-zA-Z/]+', '', request.json['dir'])
  66. df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H " + directory + " | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
  67. if df_return.exit_code == 0:
  68. return df_return.output.rstrip()
  69. else:
  70. return "0,0,0,0,0,0"
  71. except Exception as e:
  72. return jsonify(type='danger', msg=str(e))
  73. elif request.json['cmd'] == 'sieve_list' and request.json['username']:
  74. try:
  75. for container in docker_client.containers.list(filters={"id": container_id}):
  76. sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve list -u '" + request.json['username'].replace("'", "'\\''") + "'"], user='vmail')
  77. return sieve_return.output
  78. except Exception as e:
  79. return jsonify(type='danger', msg=str(e))
  80. elif request.json['cmd'] == 'sieve_print' and request.json['script_name'] and request.json['username']:
  81. try:
  82. for container in docker_client.containers.list(filters={"id": container_id}):
  83. return container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"], user='vmail')
  84. except Exception as e:
  85. return jsonify(type='danger', msg=str(e))
  86. elif request.json['cmd'] == 'worker_password' and request.json['raw']:
  87. try:
  88. for container in docker_client.containers.list(filters={"id": container_id}):
  89. hash = container.exec_run(["/bin/bash", "-c", "/usr/bin/rspamadm pw -e -p '" + request.json['raw'].replace("'", "'\\''") + "' 2> /dev/null"], user='_rspamd')
  90. if hash.exit_code == 0:
  91. hash = str(hash.output)
  92. f = open("/access.inc", "w")
  93. f.write('enable_password = "' + re.sub('[^0-9a-zA-Z\$]+', '', hash.rstrip()) + '";\n')
  94. f.close()
  95. container.restart()
  96. return jsonify(type='success', msg='command completed successfully')
  97. else:
  98. return jsonify(type='danger', msg='command did not complete, exit code was ' + int(hash.exit_code))
  99. except Exception as e:
  100. return jsonify(type='danger', msg=str(e))
  101. else:
  102. return jsonify(type='danger', msg='Unknown command')
  103. else:
  104. return jsonify(type='danger', msg='invalid action')
  105. else:
  106. return jsonify(type='danger', msg='invalid container id or missing action')
  107. class GracefulKiller:
  108. kill_now = False
  109. def __init__(self):
  110. signal.signal(signal.SIGINT, self.exit_gracefully)
  111. signal.signal(signal.SIGTERM, self.exit_gracefully)
  112. def exit_gracefully(self,signum, frame):
  113. self.kill_now = True
  114. def startFlaskAPI():
  115. app.run(debug=False, host='0.0.0.0', port=8080, threaded=True)
  116. api.add_resource(containers_get, '/containers/json')
  117. api.add_resource(container_get, '/containers/<string:container_id>/json')
  118. api.add_resource(container_post, '/containers/<string:container_id>/<string:post_action>')
  119. if __name__ == '__main__':
  120. api_thread = Thread(target=startFlaskAPI)
  121. api_thread.daemon = True
  122. api_thread.start()
  123. killer = GracefulKiller()
  124. while True:
  125. time.sleep(1)
  126. if killer.kill_now:
  127. break
  128. print "Stopping dockerapi-mailcow"