Update server.py

This commit is contained in:
Christian Burmeister 2019-05-20 21:30:40 +02:00 committed by GitHub
parent 07d15cf035
commit 9afa2730ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,10 +1,11 @@
#!/usr/bin/env python3
from flask import Flask from flask import Flask
from flask_restful import Resource, Api from flask_restful import Resource, Api
from flask import jsonify from flask import jsonify
from flask import Response from flask import Response
from flask import request from flask import request
from threading import Thread from threading import Thread
from OpenSSL import crypto
import docker import docker
import uuid import uuid
import signal import signal
@ -14,6 +15,8 @@ import re
import sys import sys
import ssl import ssl
import socket import socket
import subprocess
import traceback
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto') docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
app = Flask(__name__) app = Flask(__name__)
@ -43,160 +46,181 @@ class container_get(Resource):
class container_post(Resource): class container_post(Resource):
def post(self, container_id, post_action): def post(self, container_id, post_action):
if container_id and container_id.isalnum() and post_action: if container_id and container_id.isalnum() and post_action:
if post_action == 'stop':
try: try:
"""Dispatch container_post api call"""
if post_action == 'exec':
if not request.json or not 'cmd' in request.json:
return jsonify(type='danger', msg='cmd is missing')
if not request.json or not 'task' in request.json:
return jsonify(type='danger', msg='task is missing')
api_call_method_name = '__'.join(['container_post', str(post_action), str(request.json['cmd']), str(request.json['task']) ])
else:
api_call_method_name = '__'.join(['container_post', str(post_action) ])
api_call_method = getattr(self, api_call_method_name, lambda container_id: jsonify(type='danger', msg='container_post - unknown api call'))
print("api call: %s, container_id: %s" % (api_call_method_name, container_id))
return api_call_method(container_id)
except Exception as e:
print("error - container_post: %s" % str(e))
return jsonify(type='danger', msg=str(e))
else:
return jsonify(type='danger', msg='invalid container id or missing action')
# api call: container_post - post_action: stop
def container_post__stop(self, container_id):
for container in docker_client.containers.list(all=True, filters={"id": container_id}): for container in docker_client.containers.list(all=True, filters={"id": container_id}):
container.stop() container.stop()
return jsonify(type='success', msg='command completed successfully') return jsonify(type='success', msg='command completed successfully')
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif post_action == 'start':
try: # api call: container_post - post_action: start
def container_post__start(self, container_id):
for container in docker_client.containers.list(all=True, filters={"id": container_id}): for container in docker_client.containers.list(all=True, filters={"id": container_id}):
container.start() container.start()
return jsonify(type='success', msg='command completed successfully') return jsonify(type='success', msg='command completed successfully')
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif post_action == 'restart':
try: # api call: container_post - post_action: restart
def container_post__restart(self, container_id):
for container in docker_client.containers.list(all=True, filters={"id": container_id}): for container in docker_client.containers.list(all=True, filters={"id": container_id}):
container.restart() container.restart()
return jsonify(type='success', msg='command completed successfully') return jsonify(type='success', msg='command completed successfully')
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif post_action == 'top':
try: # api call: container_post - post_action: top
def container_post__top(self, container_id):
for container in docker_client.containers.list(all=True, filters={"id": container_id}): for container in docker_client.containers.list(all=True, filters={"id": container_id}):
return jsonify(type='success', msg=container.top()) return jsonify(type='success', msg=container.top())
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif post_action == 'stats':
try: # api call: container_post - post_action: stats
def container_post__stats(self, container_id):
for container in docker_client.containers.list(all=True, filters={"id": container_id}): for container in docker_client.containers.list(all=True, filters={"id": container_id}):
return jsonify(type='success', msg=container.stats(decode=True, stream=False)) for stat in container.stats(decode=True, stream=True):
except Exception as e: return jsonify(type='success', msg=stat )
return jsonify(type='danger', msg=str(e))
elif post_action == 'exec':
if not request.json or not 'cmd' in request.json: # api call: container_post - post_action: exec - cmd: mailq - task: delete
return jsonify(type='danger', msg='cmd is missing') def container_post__exec__mailq__delete(self, container_id):
if request.json['cmd'] == 'mailq':
if 'items' in request.json: if 'items' in request.json:
r = re.compile("^[0-9a-fA-F]+$") r = re.compile("^[0-9a-fA-F]+$")
filtered_qids = filter(r.match, request.json['items']) filtered_qids = filter(r.match, request.json['items'])
if filtered_qids: if filtered_qids:
if request.json['task'] == 'delete':
flagged_qids = ['-d %s' % i for i in filtered_qids] flagged_qids = ['-d %s' % i for i in filtered_qids]
sanitized_string = str(' '.join(flagged_qids)); sanitized_string = str(' '.join(flagged_qids));
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string]) postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
return exec_run_handler('generic', postsuper_r) return exec_run_handler('generic', postsuper_r)
except Exception as e:
return jsonify(type='danger', msg=str(e))
if request.json['task'] == 'hold': # api call: container_post - post_action: exec - cmd: mailq - task: hold
def container_post__exec__mailq__hold(self, container_id):
if 'items' in request.json:
r = re.compile("^[0-9a-fA-F]+$")
filtered_qids = filter(r.match, request.json['items'])
if filtered_qids:
flagged_qids = ['-h %s' % i for i in filtered_qids] flagged_qids = ['-h %s' % i for i in filtered_qids]
sanitized_string = str(' '.join(flagged_qids)); sanitized_string = str(' '.join(flagged_qids));
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string]) postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
return exec_run_handler('generic', postsuper_r) return exec_run_handler('generic', postsuper_r)
except Exception as e:
return jsonify(type='danger', msg=str(e))
if request.json['task'] == 'unhold': # api call: container_post - post_action: exec - cmd: mailq - task: unhold
def container_post__exec__mailq__unhold(self, container_id):
if 'items' in request.json:
r = re.compile("^[0-9a-fA-F]+$")
filtered_qids = filter(r.match, request.json['items'])
if filtered_qids:
flagged_qids = ['-H %s' % i for i in filtered_qids] flagged_qids = ['-H %s' % i for i in filtered_qids]
sanitized_string = str(' '.join(flagged_qids)); sanitized_string = str(' '.join(flagged_qids));
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string]) postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
return exec_run_handler('generic', postsuper_r) return exec_run_handler('generic', postsuper_r)
except Exception as e:
return jsonify(type='danger', msg=str(e))
if request.json['task'] == 'deliver': # api call: container_post - post_action: exec - cmd: mailq - task: deliver
def container_post__exec__mailq__deliver(self, container_id):
if 'items' in request.json:
r = re.compile("^[0-9a-fA-F]+$")
filtered_qids = filter(r.match, request.json['items'])
if filtered_qids:
flagged_qids = ['-i %s' % i for i in filtered_qids] flagged_qids = ['-i %s' % i for i in filtered_qids]
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
for i in flagged_qids: for i in flagged_qids:
postqueue_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix') postqueue_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
# todo: check each exit code # todo: check each exit code
return jsonify(type='success', msg=str("Scheduled immediate delivery")) return jsonify(type='success', msg=str("Scheduled immediate delivery"))
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['task'] == 'list': # api call: container_post - post_action: exec - cmd: mailq - task: list
try: def container_post__exec__mailq__list(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
mailq_return = container.exec_run(["/usr/sbin/postqueue", "-j"], user='postfix') mailq_return = container.exec_run(["/usr/sbin/postqueue", "-j"], user='postfix')
return exec_run_handler('utf8_text_only', mailq_return) return exec_run_handler('utf8_text_only', mailq_return)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['task'] == 'flush': # api call: container_post - post_action: exec - cmd: mailq - task: flush
try: def container_post__exec__mailq__flush(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
postqueue_r = container.exec_run(["/usr/sbin/postqueue", "-f"], user='postfix') postqueue_r = container.exec_run(["/usr/sbin/postqueue", "-f"], user='postfix')
return exec_run_handler('generic', postqueue_r) return exec_run_handler('generic', postqueue_r)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['task'] == 'super_delete': # api call: container_post - post_action: exec - cmd: mailq - task: super_delete
try: def container_post__exec__mailq__super_delete(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
postsuper_r = container.exec_run(["/usr/sbin/postsuper", "-d", "ALL"]) postsuper_r = container.exec_run(["/usr/sbin/postsuper", "-d", "ALL"])
return exec_run_handler('generic', postsuper_r) return exec_run_handler('generic', postsuper_r)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['cmd'] == 'system':
if request.json['task'] == 'fts_rescan': # api call: container_post - post_action: exec - cmd: system - task: fts_rescan
def container_post__exec__system__fts_rescan(self, container_id):
if 'username' in request.json: if 'username' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm fts rescan -u '" + request.json['username'].replace("'", "'\\''") + "'"], user='vmail') rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm fts rescan -u '" + request.json['username'].replace("'", "'\\''") + "'"], user='vmail')
if rescan_return.exit_code == 0: if rescan_return.exit_code == 0:
return jsonify(type='success', msg='fts_rescan: rescan triggered') return jsonify(type='success', msg='fts_rescan: rescan triggered')
else: else:
return jsonify(type='warning', msg='fts_rescan error') return jsonify(type='warning', msg='fts_rescan error')
except Exception as e:
return jsonify(type='danger', msg=str(e))
if 'all' in request.json: if 'all' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm fts rescan -A"], user='vmail') rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm fts rescan -A"], user='vmail')
if rescan_return.exit_code == 0: if rescan_return.exit_code == 0:
return jsonify(type='success', msg='fts_rescan: rescan triggered') return jsonify(type='success', msg='fts_rescan: rescan triggered')
else: else:
return jsonify(type='warning', msg='fts_rescan error') return jsonify(type='warning', msg='fts_rescan error')
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['task'] == 'df': # api call: container_post - post_action: exec - cmd: system - task: df
def container_post__exec__system__df(self, container_id):
if 'dir' in request.json: if 'dir' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H '" + request.json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody') df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H '" + request.json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
if df_return.exit_code == 0: if df_return.exit_code == 0:
return df_return.output.rstrip() return df_return.output.decode('utf-8').rstrip()
else: else:
return "0,0,0,0,0,0" return "0,0,0,0,0,0"
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['task'] == 'mysql_upgrade': # api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
try: def container_post__exec__system__mysql_upgrade(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
sql_shell = container.exec_run(["/bin/bash"], stdin=True, socket=True, user='mysql') cmd = "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"
upgrade_cmd = "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n" cmd_response = exec_cmd_container(container, cmd, user='mysql')
sql_socket = sql_shell.output;
try :
sql_socket.sendall(upgrade_cmd.encode('utf-8'))
sql_socket.shutdown(socket.SHUT_WR)
except socket.error:
return jsonify(type='danger', msg=str('socket error'))
worker_response = recv_socket_data(sql_socket)
matched = False matched = False
for line in worker_response.split("\n"): for line in cmd_response.split("\n"):
if 'is already upgraded to' in line: if 'is already upgraded to' in line:
matched = True matched = True
if matched: if matched:
@ -204,122 +228,88 @@ class container_post(Resource):
else: else:
container.restart() container.restart()
return jsonify(type='warning', msg='mysql_upgrade: upgrade was applied') return jsonify(type='warning', msg='mysql_upgrade: upgrade was applied')
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['cmd'] == 'reload':
if request.json['task'] == 'dovecot': # api call: container_post - post_action: exec - cmd: reload - task: dovecot
try: def container_post__exec__reload__dovecot(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/local/sbin/dovecot reload"]) reload_return = container.exec_run(["/bin/bash", "-c", "/usr/local/sbin/dovecot reload"])
return exec_run_handler('generic', reload_return) return exec_run_handler('generic', reload_return)
except Exception as e:
return jsonify(type='danger', msg=str(e))
if request.json['task'] == 'postfix': # api call: container_post - post_action: exec - cmd: reload - task: postfix
try: def container_post__exec__reload__postfix(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postfix reload"]) reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
return exec_run_handler('generic', reload_return) return exec_run_handler('generic', reload_return)
except Exception as e:
return jsonify(type='danger', msg=str(e))
if request.json['task'] == 'nginx': # api call: container_post - post_action: exec - cmd: reload - task: nginx
try: def container_post__exec__reload__nginx(self, container_id):
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
reload_return = container.exec_run(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"]) reload_return = container.exec_run(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
return exec_run_handler('generic', reload_return) return exec_run_handler('generic', reload_return)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['cmd'] == 'sieve':
if request.json['task'] == 'list': # api call: container_post - post_action: exec - cmd: sieve - task: list
def container_post__exec__sieve__list(self, container_id):
if 'username' in request.json: if 'username' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve list -u '" + request.json['username'].replace("'", "'\\''") + "'"]) sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve list -u '" + request.json['username'].replace("'", "'\\''") + "'"])
return exec_run_handler('utf8_text_only', sieve_return) return exec_run_handler('utf8_text_only', sieve_return)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['task'] == 'print':
if 'username' in request.json and 'script_name' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}):
sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"])
return exec_run_handler('utf8_text_only', sieve_return)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['cmd'] == 'maildir':
if request.json['task'] == 'cleanup': # api call: container_post - post_action: exec - cmd: sieve - task: print
def container_post__exec__sieve__print(self, container_id):
if 'username' in request.json and 'script_name' in request.json:
for container in docker_client.containers.list(filters={"id": container_id}):
cmd = ["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"]
sieve_return = container.exec_run(cmd)
return exec_run_handler('utf8_text_only', sieve_return)
# api call: container_post - post_action: exec - cmd: maildir - task: cleanup
def container_post_exec__maildir__cleanup(self, container_id):
if 'maildir' in request.json: if 'maildir' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
sane_name = re.sub(r'\W+', '', request.json['maildir']) sane_name = re.sub(r'\W+', '', request.json['maildir'])
maildir_cleanup = container.exec_run(["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request.json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request.json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"], user='vmail') cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request.json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request.json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
maildir_cleanup = container.exec_run(cmd, user='vmail')
return exec_run_handler('generic', maildir_cleanup) return exec_run_handler('generic', maildir_cleanup)
except Exception as e:
return jsonify(type='danger', msg=str(e))
elif request.json['cmd'] == 'rspamd':
if request.json['task'] == 'worker_password':
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
def container_post__exec__rspamd__worker_password(self, container_id):
if 'raw' in request.json: if 'raw' in request.json:
try:
for container in docker_client.containers.list(filters={"id": container_id}): for container in docker_client.containers.list(filters={"id": container_id}):
worker_shell = container.exec_run(["/bin/bash"], stdin=True, socket=True, user='_rspamd') cmd = "/usr/bin/rspamadm pw -e -p '" + request.json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
worker_cmd = "/usr/bin/rspamadm pw -e -p '" + request.json['raw'].replace("'", "'\\''") + "' 2> /dev/null\n" cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
worker_socket = worker_shell.output;
try :
worker_socket.sendall(worker_cmd.encode('utf-8'))
worker_socket.shutdown(socket.SHUT_WR)
except socket.error:
return jsonify(type='danger', msg=str('socket error'))
worker_response = recv_socket_data(worker_socket)
matched = False matched = False
for line in worker_response.split("\n"): for line in cmd_response.split("\n"):
if '$2$' in line: if '$2$' in line:
matched = True
hash = line.strip() hash = line.strip()
hash_out = re.search('\$2\$.+$', hash).group(0) hash_out = re.search('\$2\$.+$', hash).group(0)
f = open("/access.inc", "w") rspamd_passphrase_hash = re.sub('[^0-9a-zA-Z\$]+', '', hash_out.rstrip())
f.write('enable_password = "' + re.sub('[^0-9a-zA-Z\$]+', '', hash_out.rstrip()) + '";\n')
f.close() rspamd_password_filename = "/etc/rspamd/override.d/worker-controller-password.inc"
cmd = '''/bin/echo 'enable_password = "%s";' > %s && cat %s''' % (rspamd_passphrase_hash, rspamd_password_filename, rspamd_password_filename)
cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
if rspamd_passphrase_hash.startswith("$2$") and rspamd_passphrase_hash in cmd_response:
container.restart() container.restart()
matched = True
if matched: if matched:
return jsonify(type='success', msg='command completed successfully') return jsonify(type='success', msg='command completed successfully')
else: else:
return jsonify(type='danger', msg='command did not complete') return jsonify(type='danger', msg='command did not complete')
except Exception as e:
return jsonify(type='danger', msg=str(e))
else:
return jsonify(type='danger', msg='Unknown command')
else: def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
return jsonify(type='danger', msg='invalid action')
else: def recv_socket_data(c_socket, timeout):
return jsonify(type='danger', msg='invalid container id or missing action')
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self, signum, frame):
self.kill_now = True
def startFlaskAPI():
create_self_signed_cert()
try:
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.check_hostname = False
ctx.load_cert_chain(certfile='/cert.pem', keyfile='/key.pem')
except:
print "Cannot initialize TLS, retrying in 5s..."
time.sleep(5)
app.run(debug=False, host='0.0.0.0', port=443, threaded=True, ssl_context=ctx)
def recv_socket_data(c_socket, timeout=10):
c_socket.setblocking(0) c_socket.setblocking(0)
total_data=[]; total_data=[];
data=''; data='';
@ -332,7 +322,7 @@ def recv_socket_data(c_socket, timeout=10):
try: try:
data = c_socket.recv(8192) data = c_socket.recv(8192)
if data: if data:
total_data.append(data) total_data.append(data.decode('utf-8'))
#change the beginning time for measurement #change the beginning time for measurement
begin=time.time() begin=time.time()
else: else:
@ -343,46 +333,56 @@ def recv_socket_data(c_socket, timeout=10):
pass pass
return ''.join(total_data) return ''.join(total_data)
try :
socket = container.exec_run([shell_cmd], stdin=True, socket=True, user=user).output._sock
if not cmd.endswith("\n"):
cmd = cmd + "\n"
socket.send(cmd.encode('utf-8'))
data = recv_socket_data(socket, timeout)
socket.close()
return data
except Exception as e:
print("error - exec_cmd_container: %s" % str(e))
traceback.print_exc(file=sys.stdout)
def exec_run_handler(type, output): def exec_run_handler(type, output):
if type == 'generic': if type == 'generic':
if output.exit_code == 0: if output.exit_code == 0:
return jsonify(type='success', msg='command completed successfully') return jsonify(type='success', msg='command completed successfully')
else: else:
return jsonify(type='danger', msg='command failed: ' + output.output) return jsonify(type='danger', msg='command failed: ' + output.output.decode('utf-8'))
if type == 'utf8_text_only': if type == 'utf8_text_only':
r = Response(response=output.output, status=200, mimetype="text/plain") r = Response(response=output.output.decode('utf-8'), status=200, mimetype="text/plain")
r.headers["Content-Type"] = "text/plain; charset=utf-8" r.headers["Content-Type"] = "text/plain; charset=utf-8"
return r return r
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self, signum, frame):
self.kill_now = True
def create_self_signed_cert(): def create_self_signed_cert():
success = False process = subprocess.Popen(
while not success: "openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout /app/dockerapi_key.pem -out /app/dockerapi_cert.pem -subj /CN=dockerapi/O=mailcow -addext subjectAltName=DNS:dockerapi".split(),
stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=False
)
process.wait()
def startFlaskAPI():
create_self_signed_cert()
try: try:
pkey = crypto.PKey() ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
pkey.generate_key(crypto.TYPE_RSA, 2048) ctx.check_hostname = False
cert = crypto.X509() ctx.load_cert_chain(certfile='/app/dockerapi_cert.pem', keyfile='/app/dockerapi_key.pem')
cert.get_subject().O = "mailcow"
cert.get_subject().CN = "dockerapi"
cert.set_serial_number(int(uuid.uuid4()))
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10*365*24*60*60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(pkey)
cert.sign(pkey, 'sha512')
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
pkey = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)
with os.fdopen(os.open('/cert.pem', os.O_WRONLY | os.O_CREAT, 0o644), 'w') as handle:
handle.write(cert)
with os.fdopen(os.open('/key.pem', os.O_WRONLY | os.O_CREAT, 0o600), 'w') as handle:
handle.write(pkey)
success = True
except: except:
time.sleep(1) print ("Cannot initialize TLS, retrying in 5s...")
try: time.sleep(5)
os.remove('/cert.pem') app.run(debug=False, host='0.0.0.0', port=443, threaded=True, ssl_context=ctx)
os.remove('/key.pem')
except OSError:
pass
api.add_resource(containers_get, '/containers/json') api.add_resource(containers_get, '/containers/json')
api.add_resource(container_get, '/containers/<string:container_id>/json') api.add_resource(container_get, '/containers/<string:container_id>/json')
@ -397,5 +397,4 @@ if __name__ == '__main__':
time.sleep(1) time.sleep(1)
if killer.kill_now: if killer.kill_now:
break break
print "Stopping dockerapi-mailcow" print ("Stopping dockerapi-mailcow")