From: Ralf Jung Date: Sun, 20 May 2018 22:00:28 +0000 (+0200) Subject: move newmail script to dovecot tasks; add changepw script; fix quota-warning script X-Git-Url: https://git.ralfj.de/ansible.git/commitdiff_plain/df93ad2b2188c394c421743afc7a7645a2486202?ds=inline;hp=d8b2db0f1004d3496e6e64aafef506eb4f7e373a move newmail script to dovecot tasks; add changepw script; fix quota-warning script --- diff --git a/host_vars/template.yml b/host_vars/template.yml index dbc47ef..d5e5971 100644 --- a/host_vars/template.yml +++ b/host_vars/template.yml @@ -56,6 +56,8 @@ postfix: quota: general: 1G trash: +10M + # optional: Where to generate a CGI script that users can use to change their password + changepw_cgi: /srv/mail.example.org/cgi/changepw # optional: Hostnames and SHA1 certificate hashes that are allowed to relay email via this host. relay_client_cert_whitelist: - hostname: other.example.org diff --git a/roles/email/tasks/dovecot.yml b/roles/email/tasks/dovecot.yml index f4929eb..aa59821 100644 --- a/roles/email/tasks/dovecot.yml +++ b/roles/email/tasks/dovecot.yml @@ -40,3 +40,21 @@ dest: /etc/dovecot/quota-warning.sh src: templates/dovecot/quota-warning.sh mode: +x +# scripts +- name: create newmail dir + file: path=/root/newmail state=directory +- name: install newmail script + copy: + dest: /root/newmail/newmail + src: files/newmail/newmail + mode: u=rwx,g=rx,o=rx +- name: install newmail templates + copy: + dest: /root/newmail/templates.py + src: files/newmail/templates.py +- name: install changepw script + when: postfix.dovecot.changepw_cgi is defined + template: + dest: "{{postfix.dovecot.changepw_cgi}}" + src: templates/changepw + mode: u=rwx,g=rx,o= diff --git a/roles/email/tasks/postfix.yml b/roles/email/tasks/postfix.yml index fb8f703..67e459d 100644 --- a/roles/email/tasks/postfix.yml +++ b/roles/email/tasks/postfix.yml @@ -85,17 +85,3 @@ dest: /etc/cron.daily/check-for-local-mail src: files/check-for-local-mail mode: u=rwx,g=rx,o=rx -# tools -- block: - - name: create newmail dir - file: path=/root/newmail state=directory - - name: install newmail script - copy: - dest: /root/newmail/newmail - src: files/newmail/newmail - mode: u=rwx,g=rx,o=rx - - name: install newmail templates - copy: - dest: /root/newmail/templates.py - src: files/newmail/templates.py - when: postfix.dovecot is defined diff --git a/roles/email/templates/changepw b/roles/email/templates/changepw new file mode 100644 index 0000000..96eef60 --- /dev/null +++ b/roles/email/templates/changepw @@ -0,0 +1,102 @@ +#!/usr/bin/env python2 +from __future__ import print_function +import os, cgi, MySQLdb, subprocess, time + +# settings +DB_NAME = 'vmail' +DB_USER = 'vmail' +DB_PW = '{{postfix.dovecot.mysql_password}}' + +# DB stuff +db = MySQLdb.connect(user=DB_USER, passwd=DB_PW, db=DB_NAME, charset='utf8') + +def db_execute(query, args): + cursor = db.cursor(MySQLdb.cursors.DictCursor) + if len(args) == 1 and isinstance(args[0], dict): args = args[0] # einzelnes dict weiterreichen + cursor.execute(query, args) + return cursor + +def db_fetch_one_row(query, *args): + cursor = db_execute(query, args) + result = cursor.fetchone() + if cursor.fetchone() is not None: + raise Exception("Found more than one result, only one was expected") + cursor.close() + return result + +def db_run(query, *args): + cursor = db_execute(query, args) + cursor.close() + +def get_user(name): + return db_fetch_one_row('SELECT * FROM users WHERE username=%s', name) + +def change_user_pw(name, hash): + db_run('UPDATE users SET password=%s WHERE username=%s', hash, name) + +def change_user_active(name, active): + db_run('UPDATE users SET active=%s WHERE username=%s', int(active), name) + +# interaction with dbadm pw +def compare_pw(hash, plain): + try: + subprocess.check_output(["doveadm", "pw", "-t", hash, "-p", plain]) + return True + except subprocess.CalledProcessError: + return False + +def gen_hash(plain): + return subprocess.check_output(["doveadm", "pw", "-s", "SHA512-CRYPT", "-r", str(64*1024), "-p", plain]).decode('utf-8').strip() + +# the core action +def act(user, oldpw, newpw1, newpw2): + if user is None and oldpw is None and newpw1 is None and newpw2 is None: + return + if user is None or oldpw is None or newpw1 is None or newpw2 is None: + return "Error: You have to fill all the fields." + curdata = get_user(user) + if curdata is None: + return "Error: User not found." + if len(newpw1) < 8: + return "Error: Password must be at least 8 characters long." + if newpw1 != newpw2: + return "Error: New passwords do not match." + # slow down brute-force attacks + time.sleep(2.5) + # now go on + if not compare_pw(curdata['password'], oldpw): + return "Error: Old PW is wrong." + new_hash = gen_hash(newpw1) + change_user_pw(user, new_hash) + # potentially enable this user + if curdata['active'] < 0: + change_user_active(user, -curdata['active']) + return "Password successfully changed." + +# print headers +print("Content-Type: text/html") +print() + +# print document header +print("") +print("") +print(" ") +print(" User PW change") +print(" ") +print("") + +# do stuff +form = cgi.FieldStorage() +msg = act(form.getfirst('user'), form.getfirst('oldpw'), form.getfirst('newpw1'), form.getfirst('newpw2')) +if msg is not None: + print("

{}

".format(msg)) + +# print form +print("
") +print(" ") +print(" ") +print(" ") +print(" ") +print(" ") +print("
Username:
Old Password:
New Password:
New Password (confirmation):
") +print("") diff --git a/roles/email/templates/dovecot/quota-warning.sh b/roles/email/templates/dovecot/quota-warning.sh index 00c09f8..f834d1c 100644 --- a/roles/email/templates/dovecot/quota-warning.sh +++ b/roles/email/templates/dovecot/quota-warning.sh @@ -2,7 +2,7 @@ set -e PERCENT=$1 -FROM="{{postfix.dovecot.postmaster}}" +FROM="{{postfix.postmaster}}" msg="From: $FROM To: $USER