def _enable_certificate(domain, new_cert_folder): logger.debug("Enabling the certificate for domain %s ...", domain) live_link = os.path.join(CERT_FOLDER, domain) # If a live link (or folder) already exists if os.path.exists(live_link): # If it's not a link ... expect if to be a folder if not os.path.islink(live_link): # Backup it and remove it _backup_current_cert(domain) shutil.rmtree(live_link) # Else if it's a link, simply delete it elif os.path.lexists(live_link): os.remove(live_link) os.symlink(new_cert_folder, live_link) logger.debug("Restarting services...") for service in ("postfix", "dovecot", "metronome"): _run_service_command("restart", service) if os.path.isfile("/etc/yunohost/installed"): # regen nginx conf to be sure it integrates OCSP Stapling # (We don't do this yet if postinstall is not finished yet) regen_conf(names=["nginx"]) _run_service_command("reload", "nginx") from yunohost.hook import hook_callback hook_callback("post_cert_update", args=[domain])
def _regen_dnsmasq_if_needed(): """ Update the dnsmasq conf if some IPs are not up to date... """ ipv4 = get_public_ip() ipv6 = get_public_ip(6) do_regen = False # For all domain files in DNSmasq conf... domainsconf = glob.glob("/etc/dnsmasq.d/*.*") for domainconf in domainsconf: # Look for the IP, it's in the lines with this format : # host-record=the.domain.tld,11.22.33.44 for line in open(domainconf).readlines(): if not line.startswith("host-record"): continue ip = line.strip().split(",")[-1] # Compared found IP to current IPv4 / IPv6 # IPv6 IPv4 if (":" in ip and ip != ipv6) or (ip != ipv4): do_regen = True break if do_regen: break if do_regen: regen_conf(["dnsmasq"])
def add_new_ldap_attributes(self): from yunohost.utils.ldap import _get_ldap_interface from yunohost.regenconf import regen_conf, BACKUP_CONF_DIR # Check if the migration can be processed ldap_regen_conf_status = regen_conf(names=["slapd"], dry_run=True) # By this we check if the have been customized if ldap_regen_conf_status and ldap_regen_conf_status["slapd"][ "pending"]: logger.warning( m18n.n( "migration_0019_slapd_config_will_be_overwritten", conf_backup_folder=BACKUP_CONF_DIR, )) # Update LDAP schema restart slapd logger.info(m18n.n("migration_0011_update_LDAP_schema")) regen_conf(names=["slapd"], force=True) logger.info(m18n.n("migration_0019_add_new_attributes_in_ldap")) ldap = _get_ldap_interface() permission_list = user_permission_list(full=True)["permissions"] for permission in permission_list: system_perms = { "mail": "E-mail", "xmpp": "XMPP", "ssh": "SSH", "sftp": "STFP", } if permission.split(".")[0] in system_perms: update = { "authHeader": ["FALSE"], "label": [system_perms[permission.split(".")[0]]], "showTile": ["FALSE"], "isProtected": ["TRUE"], } else: app, subperm_name = permission.split(".") if permission.endswith(".main"): update = { "authHeader": ["TRUE"], "label": [ app ], # Note that this is later re-changed during the call to migrate_legacy_permission_settings() if a 'label' setting exists "showTile": ["TRUE"], "isProtected": ["FALSE"], } else: update = { "authHeader": ["TRUE"], "label": [subperm_name.title()], "showTile": ["FALSE"], "isProtected": ["TRUE"], } ldap.update("cn=%s,ou=permission" % permission, update)
def test_ssh_conf_unmanaged(): _force_clear_hashes([SSHD_CONFIG]) assert SSHD_CONFIG not in _get_conf_hashes("ssh") regen_conf() assert SSHD_CONFIG in _get_conf_hashes("ssh")
def domain_main_domain(operation_logger, new_main_domain=None): """ Check the current main domain, or change it Keyword argument: new_main_domain -- The new domain to be set as the main domain """ from yunohost.tools import _set_hostname # If no new domain specified, we return the current main domain if not new_main_domain: return {'current_main_domain': _get_maindomain()} # Check domain exists if new_main_domain not in domain_list()['domains']: raise YunohostError('domain_name_unknown', domain=new_main_domain) operation_logger.related_to.append(('domain', new_main_domain)) operation_logger.start() # Apply changes to ssl certs ssl_key = "/etc/ssl/private/yunohost_key.pem" ssl_crt = "/etc/ssl/private/yunohost_crt.pem" new_ssl_key = "/etc/yunohost/certs/%s/key.pem" % new_main_domain new_ssl_crt = "/etc/yunohost/certs/%s/crt.pem" % new_main_domain try: if os.path.exists(ssl_key) or os.path.lexists(ssl_key): os.remove(ssl_key) if os.path.exists(ssl_crt) or os.path.lexists(ssl_crt): os.remove(ssl_crt) os.symlink(new_ssl_key, ssl_key) os.symlink(new_ssl_crt, ssl_crt) _set_maindomain(new_main_domain) except Exception as e: logger.warning("%s" % e, exc_info=1) raise YunohostError('main_domain_change_failed') _set_hostname(new_main_domain) # Generate SSOwat configuration file app_ssowatconf() # Regen configurations try: with open('/etc/yunohost/installed', 'r'): regen_conf() except IOError: pass logger.success(m18n.n('main_domain_changed'))
def clean(): assert os.system("pgrep slapd >/dev/null") == 0 assert os.system("pgrep nginx >/dev/null") == 0 if TEST_DOMAIN in domain_list()["domains"]: domain_remove(TEST_DOMAIN) assert not os.path.exists(TEST_DOMAIN_NGINX_CONFIG) os.system("rm -f %s" % TEST_DOMAIN_NGINX_CONFIG) assert os.system("nginx -t 2>/dev/null") == 0 assert not os.path.exists(TEST_DOMAIN_NGINX_CONFIG) assert TEST_DOMAIN_NGINX_CONFIG not in _get_conf_hashes("nginx") assert TEST_DOMAIN_NGINX_CONFIG not in manually_modified_files() regen_conf(["ssh"], force=True)
def domain_main_domain(operation_logger, new_main_domain=None): """ Check the current main domain, or change it Keyword argument: new_main_domain -- The new domain to be set as the main domain """ from yunohost.tools import _set_hostname # If no new domain specified, we return the current main domain if not new_main_domain: return {"current_main_domain": _get_maindomain()} # Check domain exists if new_main_domain not in domain_list()["domains"]: raise YunohostValidationError("domain_name_unknown", domain=new_main_domain) operation_logger.related_to.append(("domain", new_main_domain)) operation_logger.start() # Apply changes to ssl certs try: write_to_file("/etc/yunohost/current_host", new_main_domain) _set_hostname(new_main_domain) except Exception as e: logger.warning("%s" % e, exc_info=1) raise YunohostError("main_domain_change_failed") # Generate SSOwat configuration file app_ssowatconf() # Regen configurations if os.path.exists("/etc/yunohost/installed"): regen_conf() logger.success(m18n.n("main_domain_changed"))
def test_stale_hashes_if_file_manually_deleted(): """ Same as other test, but manually delete the file in between and check behavior """ domain_add(TEST_DOMAIN) assert os.path.exists(TEST_DOMAIN_DNSMASQ_CONFIG) assert TEST_DOMAIN_DNSMASQ_CONFIG in _get_conf_hashes("dnsmasq") os.remove(TEST_DOMAIN_DNSMASQ_CONFIG) assert not os.path.exists(TEST_DOMAIN_DNSMASQ_CONFIG) regen_conf(names=["dnsmasq"]) assert not os.path.exists(TEST_DOMAIN_DNSMASQ_CONFIG) assert TEST_DOMAIN_DNSMASQ_CONFIG in _get_conf_hashes("dnsmasq") domain_remove(TEST_DOMAIN) assert not os.path.exists(TEST_DOMAIN_DNSMASQ_CONFIG) assert TEST_DOMAIN_DNSMASQ_CONFIG not in _get_conf_hashes("dnsmasq")
def reconfigure_dovecot(setting_name, old_value, new_value): dovecot_package = "dovecot-pop3d" environment = os.environ.copy() environment.update({"DEBIAN_FRONTEND": "noninteractive"}) if new_value == "True": command = [ "apt-get", "-y", "--no-remove", "-o Dpkg::Options::=--force-confdef", "-o Dpkg::Options::=--force-confold", "install", dovecot_package, ] subprocess.call(command, env=environment) if old_value != new_value: regen_conf(names=["dovecot"]) else: if old_value != new_value: regen_conf(names=["dovecot"]) command = ["apt-get", "-y", "remove", dovecot_package] subprocess.call(command, env=environment)
def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False, list_pending=False): services = _get_services() if isinstance(names, str): names = [names] for name in names: if name not in services.keys(): raise YunohostError('service_unknown', service=name) if names is []: names = list(services.keys()) logger.warning(m18n.n("service_regen_conf_is_deprecated")) from yunohost.regenconf import regen_conf return regen_conf(names, with_diff, force, dry_run, list_pending)
def test_ssh_conf_unmanaged_and_manually_modified(mocker): _force_clear_hashes([SSHD_CONFIG]) os.system("echo ' ' >> %s" % SSHD_CONFIG) assert SSHD_CONFIG not in _get_conf_hashes("ssh") regen_conf() assert SSHD_CONFIG in _get_conf_hashes("ssh") assert SSHD_CONFIG in manually_modified_files() with message(mocker, "regenconf_need_to_explicitly_specify_ssh"): regen_conf(force=True) assert SSHD_CONFIG in _get_conf_hashes("ssh") assert SSHD_CONFIG in manually_modified_files() regen_conf(["ssh"], force=True) assert SSHD_CONFIG in _get_conf_hashes("ssh") assert SSHD_CONFIG not in manually_modified_files()
def reconfigure_postfix(setting_name, old_value, new_value): if old_value != new_value: regen_conf(names=["postfix"])
def reconfigure_ssh(setting_name, old_value, new_value): if old_value != new_value: regen_conf(names=["ssh"])
def reconfigure_nginx(setting_name, old_value, new_value): if old_value != new_value: regen_conf(names=["nginx"])
def domain_remove(operation_logger, domain, force=False): """ Delete domains Keyword argument: domain -- Domain to delete force -- Force the domain removal """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info from yunohost.utils.ldap import _get_ldap_interface if not force and domain not in domain_list()['domains']: raise YunohostError('domain_name_unknown', domain=domain) # Check domain is not the main domain if domain == _get_maindomain(): other_domains = domain_list()["domains"] other_domains.remove(domain) if other_domains: raise YunohostError('domain_cannot_remove_main', domain=domain, other_domains="\n * " + ("\n * ".join(other_domains))) else: raise YunohostError('domain_cannot_remove_main_add_new_one', domain=domain) # Check if apps are installed on the domain apps_on_that_domain = [] for app in _installed_apps(): settings = _get_app_settings(app) label = app_info(app)["name"] if settings.get("domain") == domain: apps_on_that_domain.append(" - %s \"%s\" on https://%s%s" % (app, label, domain, settings["path"]) if "path" in settings else app) if apps_on_that_domain: raise YunohostError('domain_uninstall_app_first', apps="\n".join(apps_on_that_domain)) operation_logger.start() ldap = _get_ldap_interface() try: ldap.remove('virtualdomain=' + domain + ',ou=domains') except Exception as e: raise YunohostError('domain_deletion_failed', domain=domain, error=e) os.system('rm -rf /etc/yunohost/certs/%s' % domain) # Sometime we have weird issues with the regenconf where some files # appears as manually modified even though they weren't touched ... # There are a few ideas why this happens (like backup/restore nginx # conf ... which we shouldnt do ...). This in turns creates funky # situation where the regenconf may refuse to re-create the conf # (when re-creating a domain..) # # So here we force-clear the has out of the regenconf if it exists. # This is a pretty ad hoc solution and only applied to nginx # because it's one of the major service, but in the long term we # should identify the root of this bug... _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain]) # And in addition we even force-delete the file Otherwise, if the file was # manually modified, it may not get removed by the regenconf which leads to # catastrophic consequences of nginx breaking because it can't load the # cert file which disappeared etc.. if os.path.exists("/etc/nginx/conf.d/%s.conf" % domain): _process_regen_conf("/etc/nginx/conf.d/%s.conf" % domain, new_conf=None, save=True) regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) app_ssowatconf() hook_callback('post_domain_remove', args=[domain]) logger.success(m18n.n('domain_deleted'))
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'))
def domain_add(operation_logger, domain, dyndns=False): """ Create a custom domain Keyword argument: domain -- Domain name to add dyndns -- Subscribe to DynDNS """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf from yunohost.utils.ldap import _get_ldap_interface if domain.startswith("xmpp-upload."): raise YunohostError("domain_cannot_add_xmpp_upload") ldap = _get_ldap_interface() try: ldap.validate_uniqueness({"virtualdomain": domain}) except MoulinetteError: raise YunohostError("domain_exists") operation_logger.start() # Lower domain to avoid some edge cases issues # See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765 domain = domain.lower() # DynDNS domain if dyndns: # Do not allow to subscribe to multiple dyndns domains... if os.path.exists("/etc/cron.d/yunohost-dyndns"): raise YunohostError("domain_dyndns_already_subscribed") from yunohost.dyndns import dyndns_subscribe, _dyndns_provides # Check that this domain can effectively be provided by # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st) if not _dyndns_provides("dyndns.yunohost.org", domain): raise YunohostError("domain_dyndns_root_unknown") # Actually subscribe dyndns_subscribe(domain=domain) try: import yunohost.certificate yunohost.certificate._certificate_install_selfsigned([domain], False) attr_dict = { "objectClass": ["mailDomain", "top"], "virtualdomain": domain, } try: ldap.add("virtualdomain=%s,ou=domains" % domain, attr_dict) except Exception as e: raise YunohostError("domain_creation_failed", domain=domain, error=e) # Don't regen these conf if we're still in postinstall if os.path.exists("/etc/yunohost/installed"): # Sometime we have weird issues with the regenconf where some files # appears as manually modified even though they weren't touched ... # There are a few ideas why this happens (like backup/restore nginx # conf ... which we shouldnt do ...). This in turns creates funky # situation where the regenconf may refuse to re-create the conf # (when re-creating a domain..) # So here we force-clear the has out of the regenconf if it exists. # This is a pretty ad hoc solution and only applied to nginx # because it's one of the major service, but in the long term we # should identify the root of this bug... _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain]) regen_conf( names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd"]) app_ssowatconf() except Exception: # Force domain removal silently try: domain_remove(domain, force=True) except Exception: pass raise hook_callback("post_domain_add", args=[domain]) logger.success(m18n.n("domain_created"))
def domain_remove(operation_logger, domain, remove_apps=False, force=False): """ Delete domains Keyword argument: domain -- Domain to delete remove_apps -- Remove applications installed on the domain force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface # the 'force' here is related to the exception happening in domain_add ... # we don't want to check the domain exists because the ldap add may have # failed if not force and domain not in domain_list()['domains']: raise YunohostError('domain_name_unknown', domain=domain) # Check domain is not the main domain if domain == _get_maindomain(): other_domains = domain_list()["domains"] other_domains.remove(domain) if other_domains: raise YunohostError( "domain_cannot_remove_main", domain=domain, other_domains="\n * " + ("\n * ".join(other_domains)), ) else: raise YunohostError("domain_cannot_remove_main_add_new_one", domain=domain) # Check if apps are installed on the domain apps_on_that_domain = [] for app in _installed_apps(): settings = _get_app_settings(app) label = app_info(app)["name"] if settings.get("domain") == domain: apps_on_that_domain.append( (app, " - %s \"%s\" on https://%s%s" % (app, label, domain, settings["path"]) if "path" in settings else app)) if apps_on_that_domain: if remove_apps: if msettings.get('interface') == "cli" and not force: answer = msignals.prompt(m18n.n( 'domain_remove_confirm_apps_removal', apps="\n".join([x[1] for x in apps_on_that_domain]), answers='y/N'), color="yellow") if answer.upper() != "Y": raise YunohostError("aborting") for app, _ in apps_on_that_domain: app_remove(app) else: raise YunohostError('domain_uninstall_app_first', apps="\n".join( [x[1] for x in apps_on_that_domain])) operation_logger.start() ldap = _get_ldap_interface() try: ldap.remove("virtualdomain=" + domain + ",ou=domains") except Exception as e: raise YunohostError("domain_deletion_failed", domain=domain, error=e) os.system("rm -rf /etc/yunohost/certs/%s" % domain) # Sometime we have weird issues with the regenconf where some files # appears as manually modified even though they weren't touched ... # There are a few ideas why this happens (like backup/restore nginx # conf ... which we shouldnt do ...). This in turns creates funky # situation where the regenconf may refuse to re-create the conf # (when re-creating a domain..) # # So here we force-clear the has out of the regenconf if it exists. # This is a pretty ad hoc solution and only applied to nginx # because it's one of the major service, but in the long term we # should identify the root of this bug... _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain]) # And in addition we even force-delete the file Otherwise, if the file was # manually modified, it may not get removed by the regenconf which leads to # catastrophic consequences of nginx breaking because it can't load the # cert file which disappeared etc.. if os.path.exists("/etc/nginx/conf.d/%s.conf" % domain): _process_regen_conf("/etc/nginx/conf.d/%s.conf" % domain, new_conf=None, save=True) regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix"]) app_ssowatconf() hook_callback("post_domain_remove", args=[domain]) logger.success(m18n.n("domain_deleted"))
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"))
def tools_regen_conf(names=[], with_diff=False, force=False, dry_run=False, list_pending=False): return regen_conf(names, with_diff, force, dry_run, list_pending)
def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", domain=None, key=None): """ Subscribe to a DynDNS service Keyword argument: domain -- Full domain to subscribe with key -- Public DNS key subscribe_host -- Dynette HTTP API to subscribe to """ if _guess_current_dyndns_domain(subscribe_host) != (None, None): raise YunohostValidationError('domain_dyndns_already_subscribed') if domain is None: domain = _get_maindomain() operation_logger.related_to.append(("domain", domain)) # Verify if domain is provided by subscribe_host if not _dyndns_provides(subscribe_host, domain): raise YunohostValidationError("dyndns_domain_not_provided", domain=domain, provider=subscribe_host) # Verify if domain is available if not _dyndns_available(subscribe_host, domain): raise YunohostValidationError("dyndns_unavailable", domain=domain) operation_logger.start() if key is None: if len(glob.glob("/etc/yunohost/dyndns/*.key")) == 0: if not os.path.exists("/etc/yunohost/dyndns"): os.makedirs("/etc/yunohost/dyndns") logger.debug(m18n.n("dyndns_key_generating")) os.system( "cd /etc/yunohost/dyndns && " "dnssec-keygen -a hmac-sha512 -b 512 -r /dev/urandom -n USER %s" % domain) os.system( "chmod 600 /etc/yunohost/dyndns/*.key /etc/yunohost/dyndns/*.private" ) private_file = glob.glob("/etc/yunohost/dyndns/*%s*.private" % domain)[0] key_file = glob.glob("/etc/yunohost/dyndns/*%s*.key" % domain)[0] with open(key_file) as f: key = f.readline().strip().split(" ", 6)[-1] import requests # lazy loading this module for performance reasons # Send subscription try: r = requests.post( "https://%s/key/%s?key_algo=hmac-sha512" % (subscribe_host, base64.b64encode(key.encode()).decode()), data={"subdomain": domain}, timeout=30, ) except Exception as e: os.system("rm -f %s" % private_file) os.system("rm -f %s" % key_file) raise YunohostError("dyndns_registration_failed", error=str(e)) if r.status_code != 201: os.system("rm -f %s" % private_file) os.system("rm -f %s" % key_file) try: error = json.loads(r.text)["error"] except Exception: error = 'Server error, code: %s. (Message: "%s")' % (r.status_code, r.text) raise YunohostError("dyndns_registration_failed", error=error) # Yunohost regen conf will add the dyndns cron job if a private key exists # in /etc/yunohost/dyndns regen_conf(["yunohost"]) # Add some dyndns update in 2 and 4 minutes from now such that user should # not have to wait 10ish minutes for the conf to propagate cmd = "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) subprocess.check_call(["bash", "-c", cmd.format(t="4 min")]) logger.success(m18n.n('dyndns_registered'))
def reconfigure_ssh_and_fail2ban(setting_name, old_value, new_value): if old_value != new_value: regen_conf(names=["ssh", "fail2ban"]) firewall_reload()