manage hostmaster@ and postmaster@ automatically, create administrator@ during setup instead

closes #94
This commit is contained in:
Joshua Tauberer 2014-07-09 19:29:46 +00:00
parent 22a010ecb9
commit 41b3df6d78
3 changed files with 85 additions and 19 deletions

View File

@ -43,10 +43,13 @@ def get_mail_aliases(env):
c.execute('SELECT source, destination FROM aliases')
return [(row[0], row[1]) for row in c.fetchall()]
def get_mail_domains(env):
def get_mail_domains(env, filter_aliases=lambda alias : True):
def get_domain(emailaddr):
return emailaddr.split('@', 1)[1]
return set([get_domain(addr) for addr in get_mail_users(env)] + [get_domain(addr1) for addr1, addr2 in get_mail_aliases(env)])
return set(
[get_domain(addr) for addr in get_mail_users(env)]
+ [get_domain(source) for source, target in get_mail_aliases(env) if filter_aliases((source, target)) ]
)
def add_mail_user(email, pw, env):
if not validate_email(email, True):
@ -91,7 +94,7 @@ def add_mail_user(email, pw, env):
shutil.copyfile(utils.CONF_DIR + "/dovecot_sieve.txt", user_mail_dir + "/.dovecot.sieve")
os.chown(user_mail_dir + "/.dovecot.sieve", maildirstat.st_uid, maildirstat.st_gid)
# Update DNS/web in case any new domains are added.
# Update things in case any new domains are added.
return kick(env, "mail user added")
def set_mail_password(email, pw, env):
@ -113,10 +116,10 @@ def remove_mail_user(email, env):
return ("That's not a user (%s)." % email, 400)
conn.commit()
# Update DNS/web in case any domains are removed.
# Update things in case any domains are removed.
return kick(env, "mail user removed")
def add_mail_alias(source, destination, env):
def add_mail_alias(source, destination, env, do_kick=True):
if not validate_email(source, False):
return ("Invalid email address.", 400)
@ -127,28 +130,84 @@ def add_mail_alias(source, destination, env):
return ("Alias already exists (%s)." % source, 400)
conn.commit()
# Update DNS/web in case any new domains are added.
return kick(env, "alias added")
if do_kick:
# Update things in case any new domains are added.
return kick(env, "alias added")
def remove_mail_alias(source, env):
def remove_mail_alias(source, env, do_kick=True):
conn, c = open_database(env, with_connection=True)
c.execute("DELETE FROM aliases WHERE source=?", (source,))
if c.rowcount != 1:
return ("That's not an alias (%s)." % source, 400)
conn.commit()
# Update DNS and nginx in case any domains are removed.
return kick(env, "alias removed")
if do_kick:
# Update things in case any domains are removed.
return kick(env, "alias removed")
def kick(env, mail_result=None):
results = []
# Inclde the current operation's result in output.
if mail_result is not None:
results.append(mail_result + "\n")
# Create hostmaster@ for the primary domain if it does not already exist.
# Default the target to administrator@ which the user is responsible for
# setting and keeping up to date.
existing_aliases = get_mail_aliases(env)
administrator = "administrator@" + env['PRIMARY_HOSTNAME']
def ensure_admin_alias_exists(source):
# Does this alias exists?
for s, t in existing_aliases:
if s == source:
return
# Doesn't exist.
add_mail_alias(source, administrator, env, do_kick=False)
results.append("added alias %s (=> %s)\n" % (source, administrator))
ensure_admin_alias_exists("hostmaster@" + env['PRIMARY_HOSTNAME'])
# Get a list of domains we serve mail for, except ones for which the only
# email on that domain is a postmaster/admin alias to the administrator.
real_mail_domains = get_mail_domains(env,
filter_aliases = lambda alias : \
(not alias[0].startswith("postmaster@") \
and not alias[0].startswith("admin@")) \
or alias[1] != administrator \
)
# Create postmaster@ and admin@ for all domains we serve mail on.
# postmaster@ is assumed to exist by our Postfix configuration. admin@
# isn't anything, but it might save the user some trouble e.g. when
# buying an SSL certificate.
for domain in real_mail_domains:
ensure_admin_alias_exists("postmaster@" + domain)
ensure_admin_alias_exists("admin@" + domain)
# Remove auto-generated hostmaster/postmaster/admin on domains we no
# longer have any other email addresses for.
for source, target in existing_aliases:
user, domain = source.split("@")
if user in ("postmaster", "admin") and domain not in real_mail_domains \
and target == administrator:
remove_mail_alias(source, env, do_kick=False)
results.append("removed alias %s (was to %s; domain no longer used for email)\n" % (source, target))
def kick(env, mail_result):
# Update DNS and nginx in case any domains are added/removed.
from dns_update import do_dns_update
results.append( do_dns_update(env) )
from web_update import do_web_update
results = [
do_dns_update(env),
mail_result + "\n",
do_web_update(env),
]
results.append( do_web_update(env) )
return "".join(s for s in results if s != "")
if __name__ == "__main__":
@ -159,3 +218,7 @@ if __name__ == "__main__":
sys.exit(0)
else:
sys.exit(1)
if len(sys.argv) > 1 and sys.argv[1] == "update":
from utils import load_environment
print(kick(load_environment()))

View File

@ -55,6 +55,7 @@ def run_domain_checks(env):
if domain == env["PRIMARY_HOSTNAME"]:
check_primary_hostname_dns(domain, env)
check_alias_exists("administrator@" + domain, env)
if domain in dns_domains:
check_dns_zone(domain, env, dns_zonefiles)

View File

@ -222,8 +222,10 @@ if [ -z "`tools/mail.py user`" ]; then
echo "Okay. I'm about to set up $EMAIL_ADDR for you."
fi
tools/mail.py user add $EMAIL_ADDR $EMAIL_PW # will ask for password if none given
tools/mail.py alias add hostmaster@$PRIMARY_HOSTNAME $EMAIL_ADDR
tools/mail.py alias add postmaster@$PRIMARY_HOSTNAME $EMAIL_ADDR
# Create the user's mail account. This will ask for a password if none was given above.
tools/mail.py user add $EMAIL_ADDR $EMAIL_PW
# Create an alias to which we'll direct all automatically-created administrative aliases.
tools/mail.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR
fi