예제 #1
0
파일: tools.py 프로젝트: trogeat/yunohost
def tools_adminpw(new_password, check_strength=True):
    """
    Change admin password

    Keyword argument:
        new_password

    """
    from yunohost.user import _hash_user_password
    from yunohost.utils.password import assert_password_is_strong_enough
    import spwd

    if check_strength:
        assert_password_is_strong_enough("admin", new_password)

    # UNIX seems to not like password longer than 127 chars ...
    # e.g. SSH login gets broken (or even 'su admin' when entering the password)
    if len(new_password) >= 127:
        raise YunohostValidationError("admin_password_too_long")

    new_hash = _hash_user_password(new_password)

    from yunohost.utils.ldap import _get_ldap_interface

    ldap = _get_ldap_interface()

    try:
        ldap.update(
            "cn=admin",
            {
                "userPassword": [new_hash],
            },
        )
    except Exception:
        logger.error("unable to change admin password")
        raise YunohostError("admin_password_change_failed")
    else:
        # Write as root password
        try:
            hash_root = spwd.getspnam("root").sp_pwd

            with open("/etc/shadow", "r") as before_file:
                before = before_file.read()

            with open("/etc/shadow", "w") as after_file:
                after_file.write(
                    before.replace("root:" + hash_root,
                                   "root:" + new_hash.replace("{CRYPT}", "")))
        # An IOError may be thrown if for some reason we can't read/write /etc/passwd
        # A KeyError could also be thrown if 'root' is not in /etc/passwd in the first place (for example because no password defined ?)
        # (c.f. the line about getspnam)
        except (IOError, KeyError):
            logger.warning(m18n.n("root_password_desynchronized"))
            return

        logger.info(m18n.n("root_password_replaced_by_admin_password"))
        logger.success(m18n.n("admin_password_changed"))
예제 #2
0
def user_create(operation_logger,
                username,
                firstname,
                lastname,
                domain,
                password,
                mailbox_quota="0",
                mail=None):

    from yunohost.domain import domain_list, _get_maindomain
    from yunohost.hook import hook_callback
    from yunohost.utils.password import assert_password_is_strong_enough
    from yunohost.utils.ldap import _get_ldap_interface

    # Ensure sufficiently complex password
    assert_password_is_strong_enough("user", password)

    if mail is not None:
        logger.warning(
            "Packagers ! Using --mail in 'yunohost user create' is deprecated ... please use --domain instead."
        )
        domain = mail.split("@")[-1]

    # Validate domain used for email address/xmpp account
    if domain is None:
        if msettings.get('interface') == 'api':
            raise YunohostError('Invalide usage, specify domain argument')
        else:
            # On affiche les differents domaines possibles
            msignals.display(m18n.n('domains_available'))
            for domain in domain_list()['domains']:
                msignals.display("- {}".format(domain))

            maindomain = _get_maindomain()
            domain = msignals.prompt(
                m18n.n('ask_user_domain') + ' (default: %s)' % maindomain)
            if not domain:
                domain = maindomain

    # Check that the domain exists
    if domain not in domain_list()['domains']:
        raise YunohostError('domain_name_unknown', domain=domain)

    mail = username + '@' + domain
    ldap = _get_ldap_interface()

    if username in user_list()["users"]:
        raise YunohostError("user_already_exists", user=username)

    # Validate uniqueness of username and mail in LDAP
    try:
        ldap.validate_uniqueness({
            'uid': username,
            'mail': mail,
            'cn': username
        })
    except Exception as e:
        raise YunohostError('user_creation_failed', user=username, error=e)

    # Validate uniqueness of username in system users
    all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
    if username in all_existing_usernames:
        raise YunohostError('system_username_exists')

    main_domain = _get_maindomain()
    aliases = [
        'root@' + main_domain,
        'admin@' + main_domain,
        'webmaster@' + main_domain,
        'postmaster@' + main_domain,
        'abuse@' + main_domain,
    ]

    if mail in aliases:
        raise YunohostError('mail_unavailable')

    operation_logger.start()

    # Get random UID/GID
    all_uid = {str(x.pw_uid) for x in pwd.getpwall()}
    all_gid = {str(x.gr_gid) for x in grp.getgrall()}

    uid_guid_found = False
    while not uid_guid_found:
        # LXC uid number is limited to 65536 by default
        uid = str(random.randint(200, 65000))
        uid_guid_found = uid not in all_uid and uid not in all_gid

    # Adapt values for LDAP
    fullname = '%s %s' % (firstname, lastname)

    attr_dict = {
        'objectClass':
        ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh'],
        'givenName': [firstname],
        'sn': [lastname],
        'displayName': [fullname],
        'cn': [fullname],
        'uid': [username],
        'mail':
        mail,  # NOTE: this one seems to be already a list
        'maildrop': [username],
        'mailuserquota': [mailbox_quota],
        'userPassword': [_hash_user_password(password)],
        'gidNumber': [uid],
        'uidNumber': [uid],
        'homeDirectory': ['/home/' + username],
        'loginShell': ['/bin/false']
    }

    # If it is the first user, add some aliases
    if not ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'):
        attr_dict['mail'] = [attr_dict['mail']] + aliases

    try:
        ldap.add('uid=%s,ou=users' % username, attr_dict)
    except Exception as e:
        raise YunohostError('user_creation_failed', user=username, error=e)

    # Invalidate passwd and group to take user and group creation into account
    subprocess.call(['nscd', '-i', 'passwd'])
    subprocess.call(['nscd', '-i', 'group'])

    try:
        # Attempt to create user home folder
        subprocess.check_call(["mkhomedir_helper", username])
    except subprocess.CalledProcessError:
        if not os.path.isdir('/home/{0}'.format(username)):
            logger.warning(m18n.n('user_home_creation_failed'), exc_info=1)

    # Create group for user and add to group 'all_users'
    user_group_create(groupname=username,
                      gid=uid,
                      primary_group=True,
                      sync_perm=False)
    user_group_update(groupname='all_users',
                      add=username,
                      force=True,
                      sync_perm=True)

    # Trigger post_user_create hooks
    env_dict = {
        "YNH_USER_USERNAME": username,
        "YNH_USER_MAIL": mail,
        "YNH_USER_PASSWORD": password,
        "YNH_USER_FIRSTNAME": firstname,
        "YNH_USER_LASTNAME": lastname
    }

    hook_callback('post_user_create', args=[username, mail], env=env_dict)

    # TODO: Send a welcome mail to user
    logger.success(m18n.n('user_created'))

    return {'fullname': fullname, 'username': username, 'mail': mail}
예제 #3
0
def user_update(operation_logger,
                username,
                firstname=None,
                lastname=None,
                mail=None,
                change_password=None,
                add_mailforward=None,
                remove_mailforward=None,
                add_mailalias=None,
                remove_mailalias=None,
                mailbox_quota=None):
    """
    Update user informations

    Keyword argument:
        lastname
        mail
        firstname
        add_mailalias -- Mail aliases to add
        remove_mailforward -- Mailforward addresses to remove
        username -- Username of user to update
        add_mailforward -- Mailforward addresses to add
        change_password -- New password to set
        remove_mailalias -- Mail aliases to remove

    """
    from yunohost.domain import domain_list, _get_maindomain
    from yunohost.app import app_ssowatconf
    from yunohost.utils.password import assert_password_is_strong_enough
    from yunohost.utils.ldap import _get_ldap_interface
    from yunohost.hook import hook_callback

    domains = domain_list()['domains']

    # Populate user informations
    ldap = _get_ldap_interface()
    attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
    result = ldap.search(base='ou=users,dc=yunohost,dc=org',
                         filter='uid=' + username,
                         attrs=attrs_to_fetch)
    if not result:
        raise YunohostError('user_unknown', user=username)
    user = result[0]
    env_dict = {"YNH_USER_USERNAME": username}

    # Get modifications from arguments
    new_attr_dict = {}
    if firstname:
        new_attr_dict['givenName'] = [firstname]  # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict['displayName'] = [
            firstname + ' ' + user['sn'][0]
        ]
        env_dict["YNH_USER_FIRSTNAME"] = firstname

    if lastname:
        new_attr_dict['sn'] = [lastname]  # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict['displayName'] = [
            user['givenName'][0] + ' ' + lastname
        ]
        env_dict["YNH_USER_LASTNAME"] = lastname

    if lastname and firstname:
        new_attr_dict['cn'] = new_attr_dict['displayName'] = [
            firstname + ' ' + lastname
        ]

    # change_password is None if user_update is not called to change the password
    if change_password is not None:
        # when in the cli interface if the option to change the password is called
        # without a specified value, change_password will be set to the const 0.
        # In this case we prompt for the new password.
        if msettings.get('interface') == 'cli' and not change_password:
            change_password = msignals.prompt(m18n.n("ask_password"), True,
                                              True)
        # Ensure sufficiently complex password
        assert_password_is_strong_enough("user", change_password)

        new_attr_dict['userPassword'] = [_hash_user_password(change_password)]
        env_dict["YNH_USER_PASSWORD"] = change_password

    if mail:
        main_domain = _get_maindomain()
        aliases = [
            'root@' + main_domain,
            'admin@' + main_domain,
            'webmaster@' + main_domain,
            'postmaster@' + main_domain,
        ]
        try:
            ldap.validate_uniqueness({'mail': mail})
        except Exception as e:
            raise YunohostError('user_update_failed', user=username, error=e)
        if mail[mail.find('@') + 1:] not in domains:
            raise YunohostError('mail_domain_unknown',
                                domain=mail[mail.find('@') + 1:])
        if mail in aliases:
            raise YunohostError('mail_unavailable')

        del user['mail'][0]
        new_attr_dict['mail'] = [mail] + user['mail']

    if add_mailalias:
        if not isinstance(add_mailalias, list):
            add_mailalias = [add_mailalias]
        for mail in add_mailalias:
            try:
                ldap.validate_uniqueness({'mail': mail})
            except Exception as e:
                raise YunohostError('user_update_failed',
                                    user=username,
                                    error=e)
            if mail[mail.find('@') + 1:] not in domains:
                raise YunohostError('mail_domain_unknown',
                                    domain=mail[mail.find('@') + 1:])
            user['mail'].append(mail)
        new_attr_dict['mail'] = user['mail']

    if remove_mailalias:
        if not isinstance(remove_mailalias, list):
            remove_mailalias = [remove_mailalias]
        for mail in remove_mailalias:
            if len(user['mail']) > 1 and mail in user['mail'][1:]:
                user['mail'].remove(mail)
            else:
                raise YunohostError('mail_alias_remove_failed', mail=mail)
        new_attr_dict['mail'] = user['mail']

    if 'mail' in new_attr_dict:
        env_dict["YNH_USER_MAILS"] = ','.join(new_attr_dict['mail'])

    if add_mailforward:
        if not isinstance(add_mailforward, list):
            add_mailforward = [add_mailforward]
        for mail in add_mailforward:
            if mail in user['maildrop'][1:]:
                continue
            user['maildrop'].append(mail)
        new_attr_dict['maildrop'] = user['maildrop']

    if remove_mailforward:
        if not isinstance(remove_mailforward, list):
            remove_mailforward = [remove_mailforward]
        for mail in remove_mailforward:
            if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
                user['maildrop'].remove(mail)
            else:
                raise YunohostError('mail_forward_remove_failed', mail=mail)
        new_attr_dict['maildrop'] = user['maildrop']

    if 'maildrop' in new_attr_dict:
        env_dict["YNH_USER_MAILFORWARDS"] = ','.join(new_attr_dict['maildrop'])

    if mailbox_quota is not None:
        new_attr_dict['mailuserquota'] = [mailbox_quota]
        env_dict["YNH_USER_MAILQUOTA"] = mailbox_quota

    operation_logger.start()

    try:
        ldap.update('uid=%s,ou=users' % username, new_attr_dict)
    except Exception as e:
        raise YunohostError('user_update_failed', user=username, error=e)

    # Trigger post_user_update hooks
    hook_callback('post_user_update', env=env_dict)

    logger.success(m18n.n('user_updated'))
    app_ssowatconf()
    return user_info(username)
예제 #4
0
파일: tools.py 프로젝트: trogeat/yunohost
def tools_postinstall(
    operation_logger,
    domain,
    password,
    ignore_dyndns=False,
    force_password=False,
    force_diskspace=False,
):
    """
    YunoHost post-install

    Keyword argument:
        domain -- YunoHost main domain
        ignore_dyndns -- Do not subscribe domain to a DynDNS service (only
        needed for nohost.me, noho.st domains)
        password -- YunoHost admin password

    """
    from yunohost.utils.password import assert_password_is_strong_enough
    from yunohost.domain import domain_main_domain
    import psutil

    dyndns_provider = "dyndns.yunohost.org"

    # Do some checks at first
    if os.path.isfile("/etc/yunohost/installed"):
        raise YunohostValidationError("yunohost_already_installed")

    if os.path.isdir(
            "/etc/yunohost/apps") and os.listdir("/etc/yunohost/apps") != []:
        raise YunohostValidationError(
            "It looks like you're trying to re-postinstall a system that was already working previously ... If you recently had some bug or issues with your installation, please first discuss with the team on how to fix the situation instead of savagely re-running the postinstall ...",
            raw_msg=True,
        )

    # Check there's at least 10 GB on the rootfs...
    disk_partitions = sorted(psutil.disk_partitions(),
                             key=lambda k: k.mountpoint)
    main_disk_partitions = [
        d for d in disk_partitions if d.mountpoint in ["/", "/var"]
    ]
    main_space = sum(
        [psutil.disk_usage(d.mountpoint).total for d in main_disk_partitions])
    GB = 1024**3
    if not force_diskspace and main_space < 10 * GB:
        raise YunohostValidationError("postinstall_low_rootfsspace")

    # Check password
    if not force_password:
        assert_password_is_strong_enough("admin", password)

    if not ignore_dyndns:
        # Check if yunohost dyndns can handle the given domain
        # (i.e. is it a .nohost.me ? a .noho.st ?)
        try:
            is_nohostme_or_nohost = _dyndns_provides(dyndns_provider, domain)
        # If an exception is thrown, most likely we don't have internet
        # connectivity or something. Assume that this domain isn't manageable
        # and inform the user that we could not contact the dyndns host server.
        except Exception:
            logger.warning(
                m18n.n("dyndns_provider_unreachable",
                       provider=dyndns_provider))
            is_nohostme_or_nohost = False

        # If this is a nohost.me/noho.st, actually check for availability
        if is_nohostme_or_nohost:
            # (Except if the user explicitly said he/she doesn't care about dyndns)
            if ignore_dyndns:
                dyndns = False
            # Check if the domain is available...
            elif _dyndns_available(dyndns_provider, domain):
                dyndns = True
            # If not, abort the postinstall
            else:
                raise YunohostValidationError("dyndns_unavailable",
                                              domain=domain)
        else:
            dyndns = False
    else:
        dyndns = False

    if os.system("iptables -V >/dev/null 2>/dev/null") != 0:
        raise YunohostValidationError(
            "iptables/nftables does not seems to be working on your setup. You may be in a container or your kernel does have the proper modules loaded. Sometimes, rebooting the machine may solve the issue.",
            raw_msg=True,
        )

    operation_logger.start()
    logger.info(m18n.n("yunohost_installing"))

    # New domain config
    domain_add(domain, dyndns)
    domain_main_domain(domain)

    # Change LDAP admin password
    tools_adminpw(password, check_strength=not force_password)

    # Enable UPnP silently and reload firewall
    firewall_upnp("enable", no_refresh=True)

    # Initialize the apps catalog system
    _initialize_apps_catalog_system()

    # Try to update the apps catalog ...
    # we don't fail miserably if this fails,
    # because that could be for example an offline installation...
    try:
        _update_apps_catalog()
    except Exception as e:
        logger.warning(str(e))

    # Init migrations (skip them, no need to run them on a fresh system)
    _skip_all_migrations()

    os.system("touch /etc/yunohost/installed")

    # Enable and start YunoHost firewall at boot time
    service_enable("yunohost-firewall")
    service_start("yunohost-firewall")

    regen_conf(names=["ssh"], force=True)

    # Restore original ssh conf, as chosen by the
    # admin during the initial install
    #
    # c.f. the install script and in particular
    # https://github.com/YunoHost/install_script/pull/50
    # The user can now choose during the install to keep
    # the initial, existing sshd configuration
    # instead of YunoHost's recommended conf
    #
    original_sshd_conf = "/etc/ssh/sshd_config.before_yunohost"
    if os.path.exists(original_sshd_conf):
        os.rename(original_sshd_conf, "/etc/ssh/sshd_config")

    regen_conf(force=True)

    logger.success(m18n.n("yunohost_configured"))

    logger.warning(m18n.n("yunohost_postinstall_end_tip"))
예제 #5
0
파일: user.py 프로젝트: trogeat/yunohost
def user_create(
    operation_logger,
    username,
    firstname,
    lastname,
    domain,
    password,
    mailbox_quota="0",
    mail=None,
):

    from yunohost.domain import domain_list, _get_maindomain
    from yunohost.hook import hook_callback
    from yunohost.utils.password import assert_password_is_strong_enough
    from yunohost.utils.ldap import _get_ldap_interface

    # Ensure sufficiently complex password
    assert_password_is_strong_enough("user", password)

    if mail is not None:
        logger.warning(
            "Packagers ! Using --mail in 'yunohost user create' is deprecated ... please use --domain instead."
        )
        domain = mail.split("@")[-1]

    # Validate domain used for email address/xmpp account
    if domain is None:
        if msettings.get("interface") == "api":
            raise YunohostValidationError(
                "Invalid usage, you should specify a domain argument")
        else:
            # On affiche les differents domaines possibles
            msignals.display(m18n.n("domains_available"))
            for domain in domain_list()["domains"]:
                msignals.display("- {}".format(domain))

            maindomain = _get_maindomain()
            domain = msignals.prompt(
                m18n.n("ask_user_domain") + " (default: %s)" % maindomain)
            if not domain:
                domain = maindomain

    # Check that the domain exists
    if domain not in domain_list()["domains"]:
        raise YunohostValidationError("domain_name_unknown", domain=domain)

    mail = username + "@" + domain
    ldap = _get_ldap_interface()

    if username in user_list()["users"]:
        raise YunohostValidationError("user_already_exists", user=username)

    # Validate uniqueness of username and mail in LDAP
    try:
        ldap.validate_uniqueness({
            "uid": username,
            "mail": mail,
            "cn": username
        })
    except Exception as e:
        raise YunohostValidationError("user_creation_failed",
                                      user=username,
                                      error=e)

    # Validate uniqueness of username in system users
    all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
    if username in all_existing_usernames:
        raise YunohostValidationError("system_username_exists")

    main_domain = _get_maindomain()
    aliases = [
        "root@" + main_domain,
        "admin@" + main_domain,
        "webmaster@" + main_domain,
        "postmaster@" + main_domain,
        "abuse@" + main_domain,
    ]

    if mail in aliases:
        raise YunohostValidationError("mail_unavailable")

    operation_logger.start()

    # Get random UID/GID
    all_uid = {str(x.pw_uid) for x in pwd.getpwall()}
    all_gid = {str(x.gr_gid) for x in grp.getgrall()}

    uid_guid_found = False
    while not uid_guid_found:
        # LXC uid number is limited to 65536 by default
        uid = str(random.randint(1001, 65000))
        uid_guid_found = uid not in all_uid and uid not in all_gid

    # Adapt values for LDAP
    fullname = "%s %s" % (firstname, lastname)

    attr_dict = {
        "objectClass": [
            "mailAccount",
            "inetOrgPerson",
            "posixAccount",
            "userPermissionYnh",
        ],
        "givenName": [firstname],
        "sn": [lastname],
        "displayName": [fullname],
        "cn": [fullname],
        "uid": [username],
        "mail":
        mail,  # NOTE: this one seems to be already a list
        "maildrop": [username],
        "mailuserquota": [mailbox_quota],
        "userPassword": [_hash_user_password(password)],
        "gidNumber": [uid],
        "uidNumber": [uid],
        "homeDirectory": ["/home/" + username],
        "loginShell": ["/bin/false"],
    }

    # If it is the first user, add some aliases
    if not ldap.search(base="ou=users,dc=yunohost,dc=org", filter="uid=*"):
        attr_dict["mail"] = [attr_dict["mail"]] + aliases

    try:
        ldap.add("uid=%s,ou=users" % username, attr_dict)
    except Exception as e:
        raise YunohostError("user_creation_failed", user=username, error=e)

    # Invalidate passwd and group to take user and group creation into account
    subprocess.call(["nscd", "-i", "passwd"])
    subprocess.call(["nscd", "-i", "group"])

    try:
        # Attempt to create user home folder
        subprocess.check_call(["mkhomedir_helper", username])
    except subprocess.CalledProcessError:
        if not os.path.isdir("/home/{0}".format(username)):
            logger.warning(m18n.n("user_home_creation_failed"), exc_info=1)

    # Create group for user and add to group 'all_users'
    user_group_create(groupname=username,
                      gid=uid,
                      primary_group=True,
                      sync_perm=False)
    user_group_update(groupname="all_users",
                      add=username,
                      force=True,
                      sync_perm=True)

    # Trigger post_user_create hooks
    env_dict = {
        "YNH_USER_USERNAME": username,
        "YNH_USER_MAIL": mail,
        "YNH_USER_PASSWORD": password,
        "YNH_USER_FIRSTNAME": firstname,
        "YNH_USER_LASTNAME": lastname,
    }

    hook_callback("post_user_create", args=[username, mail], env=env_dict)

    # TODO: Send a welcome mail to user
    logger.success(m18n.n("user_created"))

    return {"fullname": fullname, "username": username, "mail": mail}
예제 #6
0
def tools_postinstall(operation_logger,
                      domain,
                      password,
                      ignore_dyndns=False,
                      force_password=False):
    """
    YunoHost post-install

    Keyword argument:
        domain -- YunoHost main domain
        ignore_dyndns -- Do not subscribe domain to a DynDNS service (only
        needed for nohost.me, noho.st domains)
        password -- YunoHost admin password

    """
    from yunohost.utils.password import assert_password_is_strong_enough
    from yunohost.domain import domain_main_domain

    dyndns_provider = "dyndns.yunohost.org"

    # Do some checks at first
    if os.path.isfile('/etc/yunohost/installed'):
        raise YunohostError('yunohost_already_installed')

    if os.path.isdir(
            "/etc/yunohost/apps") and os.listdir("/etc/yunohost/apps") != []:
        raise YunohostError(
            "It looks like you're trying to re-postinstall a system that was already working previously ... If you recently had some bug or issues with your installation, please first discuss with the team on how to fix the situation instead of savagely re-running the postinstall ...",
            raw_msg=True)

    # Check password
    if not force_password:
        assert_password_is_strong_enough("admin", password)

    if not ignore_dyndns:
        # Check if yunohost dyndns can handle the given domain
        # (i.e. is it a .nohost.me ? a .noho.st ?)
        try:
            is_nohostme_or_nohost = _dyndns_provides(dyndns_provider, domain)
        # If an exception is thrown, most likely we don't have internet
        # connectivity or something. Assume that this domain isn't manageable
        # and inform the user that we could not contact the dyndns host server.
        except:
            logger.warning(
                m18n.n('dyndns_provider_unreachable',
                       provider=dyndns_provider))
            is_nohostme_or_nohost = False

        # If this is a nohost.me/noho.st, actually check for availability
        if is_nohostme_or_nohost:
            # (Except if the user explicitly said he/she doesn't care about dyndns)
            if ignore_dyndns:
                dyndns = False
            # Check if the domain is available...
            elif _dyndns_available(dyndns_provider, domain):
                dyndns = True
            # If not, abort the postinstall
            else:
                raise YunohostError('dyndns_unavailable', domain=domain)
        else:
            dyndns = False
    else:
        dyndns = False

    if os.system("iptables -V >/dev/null 2>/dev/null") != 0:
        raise YunohostError(
            "iptables/nftables does not seems to be working on your setup. You may be in a container or your kernel does have the proper modules loaded. Sometimes, rebooting the machine may solve the issue.",
            raw_msg=True)

    operation_logger.start()
    logger.info(m18n.n('yunohost_installing'))

    regen_conf(['nslcd', 'nsswitch'], force=True)

    # Initialize LDAP for YunoHost
    # TODO: Improve this part by integrate ldapinit into conf_regen hook
    tools_ldapinit()

    # Create required folders
    folders_to_create = [
        '/etc/yunohost/apps', '/etc/yunohost/certs',
        '/var/cache/yunohost/repo', '/home/yunohost.backup',
        '/home/yunohost.app'
    ]

    for folder in [x for x in folders_to_create if not os.path.exists(x)]:
        os.makedirs(folder)

    # Change folders permissions
    os.system('chmod 755 /home/yunohost.app')

    # Init ssowat's conf.json.persistent
    if not os.path.exists('/etc/ssowat/conf.json.persistent'):
        write_to_json('/etc/ssowat/conf.json.persistent', {})

    os.system('chmod 644 /etc/ssowat/conf.json.persistent')

    # Create SSL CA
    regen_conf(['ssl'], force=True)
    ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
    # (Update the serial so that it's specific to this very instance)
    os.system("openssl rand -hex 19 > %s/serial" % ssl_dir)
    commands = [
        'rm %s/index.txt' % ssl_dir,
        'touch %s/index.txt' % ssl_dir,
        'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir),
        'sed -i s/yunohost.org/%s/g %s/openssl.ca.cnf ' % (domain, ssl_dir),
        'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch -subj /CN=%s/O=%s'
        % (ssl_dir, ssl_dir, ssl_dir, domain, os.path.splitext(domain)[0]),
        'cp %s/ca/cacert.pem /etc/ssl/certs/ca-yunohost_crt.pem' % ssl_dir,
        'update-ca-certificates'
    ]

    for command in commands:
        p = subprocess.Popen(command.split(),
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)

        out, _ = p.communicate()

        if p.returncode != 0:
            logger.warning(out)
            raise YunohostError('yunohost_ca_creation_failed')
        else:
            logger.debug(out)

    logger.success(m18n.n('yunohost_ca_creation_success'))

    # New domain config
    regen_conf(['nsswitch'], force=True)
    domain_add(domain, dyndns)
    domain_main_domain(domain)

    # Change LDAP admin password
    tools_adminpw(password, check_strength=not force_password)

    # Enable UPnP silently and reload firewall
    firewall_upnp('enable', no_refresh=True)

    # Initialize the apps catalog system
    _initialize_apps_catalog_system()

    # Try to update the apps catalog ...
    # we don't fail miserably if this fails,
    # because that could be for example an offline installation...
    try:
        _update_apps_catalog()
    except Exception as e:
        logger.warning(str(e))

    # Create the archive directory (makes it easier for people to upload backup
    # archives, otherwise it's only created after running `yunohost backup
    # create` once.
    from yunohost.backup import _create_archive_dir
    _create_archive_dir()

    # Init migrations (skip them, no need to run them on a fresh system)
    _skip_all_migrations()

    os.system('touch /etc/yunohost/installed')

    # Enable and start YunoHost firewall at boot time
    service_enable("yunohost-firewall")
    service_start("yunohost-firewall")

    regen_conf(names=["ssh"], force=True)

    # Restore original ssh conf, as chosen by the
    # admin during the initial install
    #
    # c.f. the install script and in particular
    # https://github.com/YunoHost/install_script/pull/50
    # The user can now choose during the install to keep
    # the initial, existing sshd configuration
    # instead of YunoHost's recommended conf
    #
    original_sshd_conf = '/etc/ssh/sshd_config.before_yunohost'
    if os.path.exists(original_sshd_conf):
        os.rename(original_sshd_conf, '/etc/ssh/sshd_config')

    regen_conf(force=True)

    logger.success(m18n.n('yunohost_configured'))

    logger.warning(m18n.n('yunohost_postinstall_end_tip'))