Add configuration wizard (#158)
* add --interactive option refs #133 * delete uneeded template as config is a dict now * minor changes after code review
This commit is contained in:
committed by
Antoine Nguyen
parent
45c777683c
commit
19ac9350d7
@@ -1,103 +0,0 @@
|
||||
[general]
|
||||
# %(domain)s is the value specified when launching the installer
|
||||
hostname = mail.%(domain)s
|
||||
|
||||
[certificate]
|
||||
generate = true
|
||||
# Choose between self-signed or letsencrypt
|
||||
type = self-signed
|
||||
|
||||
[letsencrypt]
|
||||
email = admin@example.com
|
||||
|
||||
[database]
|
||||
# Select database engine : postgres or mysql
|
||||
engine = postgres
|
||||
#engine = mysql
|
||||
host = 127.0.0.1
|
||||
install = true
|
||||
|
||||
[postgres]
|
||||
user = postgres
|
||||
password =
|
||||
|
||||
[mysql]
|
||||
user = root
|
||||
password = $mysql_password
|
||||
charset = utf8
|
||||
collation = utf8_general_ci
|
||||
|
||||
[modoboa]
|
||||
user = modoboa
|
||||
home_dir = /srv/modoboa
|
||||
venv_path = %(home_dir)s/env
|
||||
instance_path = %(home_dir)s/instance
|
||||
timezone = Europe/Paris
|
||||
dbname = modoboa
|
||||
dbuser = modoboa
|
||||
dbpassword = $modoboa_password
|
||||
# Extensions to install
|
||||
# also available: modoboa-radicale modoboa-dmarc modoboa-imap-migration
|
||||
extensions = modoboa-amavis modoboa-pdfcredentials modoboa-postfix-autoreply modoboa-sievefilters modoboa-stats modoboa-webmail modoboa-contacts
|
||||
|
||||
# Deploy Modoboa and enable development mode
|
||||
devmode = false
|
||||
|
||||
[automx]
|
||||
enabled = true
|
||||
user = automx
|
||||
config_dir = /etc
|
||||
home_dir = /srv/automx
|
||||
venv_path = %(home_dir)s/env
|
||||
instance_path = %(home_dir)s/instance
|
||||
|
||||
[amavis]
|
||||
enabled = true
|
||||
user = amavis
|
||||
max_servers = 1
|
||||
|
||||
dbname = amavis
|
||||
dbuser = amavis
|
||||
dbpassword = $amavis_password
|
||||
|
||||
[clamav]
|
||||
enabled = true
|
||||
user = clamav
|
||||
|
||||
[dovecot]
|
||||
enabled = true
|
||||
config_dir = /etc/dovecot
|
||||
user = vmail
|
||||
home_dir = /srv/vmail
|
||||
mailboxes_owner = vmail
|
||||
# Enable extra procotols (in addition to imap and lmtp)
|
||||
# Example: pop3
|
||||
extra_protocols =
|
||||
# Replace localhost with your domain
|
||||
postmaster_address = postmaster@localhost
|
||||
|
||||
[nginx]
|
||||
enabled = true
|
||||
config_dir = /etc/nginx
|
||||
|
||||
[razor]
|
||||
enabled = true
|
||||
config_dir = /etc/razor
|
||||
|
||||
[postfix]
|
||||
enabled = true
|
||||
config_dir = /etc/postfix
|
||||
message_size_limit = 11534336
|
||||
|
||||
[spamassassin]
|
||||
enabled = true
|
||||
config_dir = /etc/mail/spamassassin
|
||||
|
||||
dbname = spamassassin
|
||||
dbuser = spamassassin
|
||||
dbpassword = $sa_password
|
||||
|
||||
[uwsgi]
|
||||
enabled = true
|
||||
config_dir = /etc/uwsgi
|
||||
nb_processes = 2
|
||||
362
modoboa_installer/config_dict_template.py
Normal file
362
modoboa_installer/config_dict_template.py
Normal file
@@ -0,0 +1,362 @@
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
def make_password(length=16):
|
||||
"""Create a random password."""
|
||||
return "".join(
|
||||
random.SystemRandom().choice(
|
||||
string.ascii_letters + string.digits) for _ in range(length))
|
||||
|
||||
|
||||
# Validators should return a tuple bool, error message
|
||||
def is_email(user_input):
|
||||
"""Return True in input is a valid email"""
|
||||
return "@" in user_input, "Please enter a valid email"
|
||||
|
||||
|
||||
ConfigDictTemplate = [
|
||||
{
|
||||
"name": "general",
|
||||
"values": [
|
||||
{
|
||||
"option": "hostname",
|
||||
"default": "mail.%(domain)s",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "certificate",
|
||||
"values": [
|
||||
{
|
||||
"option": "generate",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "type",
|
||||
"default": "self-signed",
|
||||
"customizable": True,
|
||||
"question": "Please choose your certificate type",
|
||||
"values": ["self-signed", "letsencrypt"],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "letsencrypt",
|
||||
"if": "certificate.type=letsencrypt",
|
||||
"values": [
|
||||
{
|
||||
"option": "email",
|
||||
"default": "admin@example.com",
|
||||
"question": (
|
||||
"Please enter the mail you wish to use for "
|
||||
"letsencrypt"),
|
||||
"customizable": True,
|
||||
"validators": [is_email]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "database",
|
||||
"values": [
|
||||
{
|
||||
"option": "engine",
|
||||
"default": "postgres",
|
||||
"customizable": True,
|
||||
"question": "Please choose your database engine",
|
||||
"values": ["postgres", "mysql"],
|
||||
},
|
||||
{
|
||||
"option": "host",
|
||||
"default": "127.0.0.1",
|
||||
},
|
||||
{
|
||||
"option": "install",
|
||||
"default": "true",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "postgres",
|
||||
"if": "database.engine=postgres",
|
||||
"values": [
|
||||
{
|
||||
"option": "user",
|
||||
"default": "postgres",
|
||||
},
|
||||
{
|
||||
"option": "password",
|
||||
"default": "",
|
||||
"customizable": True,
|
||||
"question": "Please enter postgres password",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mysql",
|
||||
"if": "database.engine=mysql",
|
||||
"values": [
|
||||
{
|
||||
"option": "user",
|
||||
"default": "root",
|
||||
},
|
||||
{
|
||||
"option": "password",
|
||||
"default": make_password,
|
||||
"customizable": True,
|
||||
"question": "Please enter mysql root password"
|
||||
},
|
||||
{
|
||||
"option": "charset",
|
||||
"default": "utf8",
|
||||
},
|
||||
{
|
||||
"option": "collation",
|
||||
"default": "utf8_general_ci",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "modoboa",
|
||||
"values": [
|
||||
{
|
||||
"option": "user",
|
||||
"default": "modoboa",
|
||||
},
|
||||
{
|
||||
"option": "home_dir",
|
||||
"default": "/srv/modoboa",
|
||||
},
|
||||
{
|
||||
"option": "venv_path",
|
||||
"default": "%(home_dir)s/instance",
|
||||
},
|
||||
{
|
||||
"option": "instance_path",
|
||||
"default": "%(home_dir)s/env",
|
||||
},
|
||||
{
|
||||
"option": "timezone",
|
||||
"default": "Europe/Paris",
|
||||
},
|
||||
{
|
||||
"option": "dbname",
|
||||
"default": "modoboa",
|
||||
},
|
||||
{
|
||||
"option": "dbuser",
|
||||
"default": "modoboa",
|
||||
},
|
||||
{
|
||||
"option": "dbpassword",
|
||||
"default": make_password,
|
||||
"customizable": True,
|
||||
"question": "Please enter Modoboa db password",
|
||||
},
|
||||
{
|
||||
"option": "extensions",
|
||||
"default": (
|
||||
"modoboa-amavis modoboa-pdfcredentials "
|
||||
"modoboa-postfix-autoreply modoboa-sievefilters "
|
||||
"modoboa-stats modoboa-webmail modoboa-contacts"),
|
||||
},
|
||||
{
|
||||
"option": "devmod",
|
||||
"default": "false",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "automx",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "user",
|
||||
"default": "automx",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc",
|
||||
},
|
||||
{
|
||||
"option": "home_dir",
|
||||
"default": "/srv/automx",
|
||||
},
|
||||
{
|
||||
"option": "venv_path",
|
||||
"default": "%(home_dir)s/env",
|
||||
},
|
||||
{
|
||||
"option": "instance_path",
|
||||
"default": "%(home_dir)s/instance",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "amavis",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "user",
|
||||
"default": "amavis",
|
||||
},
|
||||
{
|
||||
"option": "max_servers",
|
||||
"default": "1",
|
||||
},
|
||||
{
|
||||
"option": "dbname",
|
||||
"default": "amavis",
|
||||
},
|
||||
{
|
||||
"option": "dbuser",
|
||||
"default": "amavis",
|
||||
},
|
||||
{
|
||||
"option": "dbpassword",
|
||||
"default": make_password,
|
||||
"customizable": True,
|
||||
"question": "Please enter amavis db password"
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "clamav",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "user",
|
||||
"default": "clamav",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dovecot",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc/dovecot",
|
||||
},
|
||||
{
|
||||
"option": "user",
|
||||
"default": "vmail",
|
||||
},
|
||||
{
|
||||
"option": "home_dir",
|
||||
"default": "/srv/vmail",
|
||||
},
|
||||
{
|
||||
"option": "mailboxes_owner",
|
||||
"default": "vmail",
|
||||
},
|
||||
{
|
||||
"option": "extra_protocols",
|
||||
"default": "",
|
||||
},
|
||||
{
|
||||
"option": "postmaster_address",
|
||||
"default": "postmaster@%(domain)s",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "nginx",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc/nginx",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "razor",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc/razor",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "postfix",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc/postfix",
|
||||
},
|
||||
{
|
||||
"option": "message_size_limit",
|
||||
"default": "11534336",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spamassassin",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc/mail/spamassassin",
|
||||
},
|
||||
{
|
||||
"option": "dbname",
|
||||
"default": "spamassassin",
|
||||
},
|
||||
{
|
||||
"option": "dbuser",
|
||||
"default": "spamassassin",
|
||||
},
|
||||
{
|
||||
"option": "dbpassword",
|
||||
"default": make_password,
|
||||
"customizable": True,
|
||||
"question": "Please enter spamassassin db password"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "uwsgi",
|
||||
"values": [
|
||||
{
|
||||
"option": "enabled",
|
||||
"default": "true",
|
||||
},
|
||||
{
|
||||
"option": "config_dir",
|
||||
"default": "/etc/uwsgi",
|
||||
},
|
||||
{
|
||||
"option": "nb_processes",
|
||||
"default": "2",
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
@@ -10,6 +10,12 @@ import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
|
||||
from . import config_dict_template
|
||||
|
||||
|
||||
ENV = {}
|
||||
@@ -139,23 +145,14 @@ def copy_from_template(template, dest, context):
|
||||
fp.write(ConfigFileTemplate(buf).substitute(context))
|
||||
|
||||
|
||||
def check_config_file(dest):
|
||||
def check_config_file(dest, interactive=False):
|
||||
"""Create a new installer config file if needed."""
|
||||
if os.path.exists(dest):
|
||||
return
|
||||
printcolor(
|
||||
"Configuration file {} not found, creating new one."
|
||||
.format(dest), YELLOW)
|
||||
with open("installer.cfg.template") as fp:
|
||||
buf = fp.read()
|
||||
context = {
|
||||
"mysql_password": make_password(),
|
||||
"modoboa_password": make_password(),
|
||||
"amavis_password": make_password(),
|
||||
"sa_password": make_password()
|
||||
}
|
||||
with open(dest, "w") as fp:
|
||||
fp.write(string.Template(buf).substitute(context))
|
||||
gen_config(dest, interactive)
|
||||
|
||||
|
||||
def has_colours(stream):
|
||||
@@ -222,3 +219,67 @@ def random_key(l=16):
|
||||
key = "".join(random.sample(population * l, l))
|
||||
if len(key) == l:
|
||||
return key
|
||||
|
||||
|
||||
def validate(value, config_entry):
|
||||
if value is None:
|
||||
return False
|
||||
if "values" not in config_entry and "validators" not in config_entry:
|
||||
return True
|
||||
if "values" in config_entry:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
return False
|
||||
return value >= 0 and value < len(config_entry["values"])
|
||||
if "validators" in config_entry:
|
||||
for validator in config_entry["validators"]:
|
||||
valide, message = validator(value)
|
||||
if not valide:
|
||||
printcolor(message, MAGENTA)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_entry_value(entry, interactive):
|
||||
if callable(entry["default"]):
|
||||
default_value = entry["default"]()
|
||||
else:
|
||||
default_value = entry["default"]
|
||||
user_value = None
|
||||
if entry.get("customizable") and interactive:
|
||||
while (user_value != '' and not validate(user_value, entry)):
|
||||
print(entry.get("question"))
|
||||
if entry.get("values"):
|
||||
print("Please choose from the list")
|
||||
values = entry.get("values")
|
||||
for index, value in enumerate(values):
|
||||
print("{} {}".format(index, value))
|
||||
print("default is <{}>".format(default_value))
|
||||
user_value = user_input("->")
|
||||
|
||||
if entry.get("values") and user_value != '':
|
||||
user_value = values[int(user_value)]
|
||||
return user_value if user_value else default_value
|
||||
|
||||
|
||||
def gen_config(dest, interactive=False):
|
||||
"""Create config file from dict template"""
|
||||
tpl_dict = config_dict_template.ConfigDictTemplate
|
||||
config = configparser.ConfigParser()
|
||||
# only ask about options we need, else still generate default
|
||||
for section in tpl_dict:
|
||||
if "if" in section:
|
||||
config_key, value = section.get("if").split("=")
|
||||
section_name, option = config_key.split(".")
|
||||
interactive_section = (
|
||||
config.get(section_name, option) == value and interactive)
|
||||
else:
|
||||
interactive_section = interactive
|
||||
config.add_section(section["name"])
|
||||
for config_entry in section["values"]:
|
||||
value = get_entry_value(config_entry, interactive_section)
|
||||
config.set(section["name"], config_entry["option"], value)
|
||||
|
||||
with open(dest, "w") as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
5
run.py
5
run.py
@@ -31,6 +31,9 @@ def main():
|
||||
parser.add_argument(
|
||||
"--stop-after-configfile-check", action="store_true", default=False,
|
||||
help="Check configuration, generate it if needed and exit")
|
||||
parser.add_argument(
|
||||
"--interactive", action="store_true", default=False,
|
||||
help="Generate configuration file with user interaction")
|
||||
parser.add_argument("domain", type=str,
|
||||
help="The main domain of your future mail server")
|
||||
args = parser.parse_args()
|
||||
@@ -38,7 +41,7 @@ def main():
|
||||
if args.debug:
|
||||
utils.ENV["debug"] = True
|
||||
utils.printcolor("Welcome to Modoboa installer!", utils.GREEN)
|
||||
utils.check_config_file(args.configfile)
|
||||
utils.check_config_file(args.configfile, args.interactive)
|
||||
if args.stop_after_configfile_check:
|
||||
return
|
||||
config = configparser.SafeConfigParser()
|
||||
|
||||
Reference in New Issue
Block a user