Ejemplo n.º 1
0
    def _run(self, cfg, board, kwargs):
        self.interval = 900

        # only set trusted domains, if nextcloud is installed
        if not self.nc.is_installed:
            log.debug("cannot set trusted_domains - uninitialized nextcloud")
            self.interval = 15
            return False

        try:
            trusted_domains = self.nc.get_config("trusted_domains")
        except NextcloudError:
            log.warning("cannot get trusted_domains from nextcloud")
            self.interval = 15
            return False        

        default_entry = trusted_domains[0]

        entries = [default_entry] + self.static_entries[:]
        if cfg["config"].get("domain"):
            entries.append(cfg["config"]["domain"])

        if cfg["config"].get("proxy_active") and cfg["config"].get("proxy_domain"):
            entries.append(cfg["config"]["proxy_domain"])

        if any(entry not in trusted_domains for entry in entries):
            try:
                self.nc.set_config("trusted_domains", entries)
            except NextcloudError:
                log.warning("failed to write all trusted_domains")
                self.interval = 15
Ejemplo n.º 2
0
    def _run(self, cfg, board, kwargs):
        self.interval = None


        # if the custom-dns-config is active, ensure ddclient is up
        if cfg["config"]["dns_mode"] == "config_done" and not services.is_active("ddclient"):
            services.restart("ddclient")

        # ensure that neither updater nor factory-reset are masked (paranoia!)
        services.unmask("nextbox-updater")
        services.unmask("nextbox-factory-reset")

        # log a welcome + version
        log.info(f"Hello World - I am NextBox - call me: v{nextbox_version()} - let'sa gooo")

        # update/upgrade now
        shield.set_led_state("updating")

        log.info("running 'apt-get update'")
        cache = apt.cache.Cache()
        cache.update()
        cache.open()

        # which debian package
        pkg = cfg["config"]["debian_package"]

        try:
            pkg_obj = cache[pkg]
        except KeyError:
            log.error(f"self-update failed: designated package: {pkg} not found!")
            log.error("falling back to 'nextbox' - retrying upgrade...")
            pkg = "nextbox"
        
            try:
                pkg_obj = cache[pkg]
            except KeyError:
                log.error("CRITICAL: failed to find 'nextbox' in apt-cache")
                # we should never ever end here, this means that the nextbox 
                # debian package is not available...
                # nextbox debian (ppa) repository not available ???!!
                return 

        # install package (i.e., other nextbox package is already installed)
        # will trigger for e.g., 'nextbox' to 'nextbox-testing' switching
        if not pkg_obj.is_installed:
            log.info(f"installing debian package: {pkg} (start service: nextbox-updater)")
            services.start("nextbox-updater")
        elif pkg_obj.is_upgradable:
            log.info(f"upgrading debian package: {pkg} (start service: nextbox-updater)")
            services.start("nextbox-updater")
        else:
            log.debug(f"no need to upgrade or install debian package: {pkg}")
Ejemplo n.º 3
0
 def _run(self, cfg, board, kwargs):
     # only enable, if nextcloud is installed
     if not self.nc.is_installed:
         log.debug("cannot enable nextbox-app - uninitialized nextcloud")
         return 
     
     # try to enable
     try:
         if self.nc.enable_nextbox_app():
             self.interval = 3600
             log.info("enabled nextcloud nextbox-app")
     except NextcloudError:
         pass
Ejemplo n.º 4
0
    def start(self):
        if self.started:
            log.warning(f"trying to start already started: {self.cmd}")
            return False

        self.cmd = self.cmd if not self.shell else " ".join(self.cmd)
        self.proc = subprocess.Popen(self.cmd, shell=self.shell,
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

        self.started = time.time()
        log.debug(f"started {self.cmd} ({'non-' if not self.block else ''}blocking)")

        # return if non-blocking, else wait + pass-on output
        if not self.block:
            return True

        log.debug("blocking call, waiting now...")

        while self.proc.returncode is None:
            try:
                self.proc.wait(timeout=10)
            except subprocess.TimeoutExpired:
                log.debug("blocking-cmd timeout (10secs)")

        self._handle_stdout_stream(poll_block_ms=1000)
        log.debug("blocking call finished")
        return True
Ejemplo n.º 5
0
    def retries(cls, count, *vargs, **kwargs):
        # cannot retry, if non-blocking
        if not kwargs.get("block"):
            log.warning("trying CommandRunner-retries w/o block: no retries are done!")
            return cls(*vargs, **kwargs)

        # if 'block' is set, retry until returncode == 0
        for idx in range(count):
            res = cls(*vargs, **kwargs)
            if res.returncode == 0:
                return res
            log.debug(f"retry #{idx+1}, returncode: {res.returncode}")
            res.log_output()

        # failed retries
        log.error("retried hard for {count} times, failed anyways")
        return res
Ejemplo n.º 6
0
    def _run(self, cfg, board, kwargs):

        token = cfg["config"]["desec_token"]
        dns_mode = cfg["config"]["dns_mode"]
        domain = cfg["config"]["domain"]

        if dns_mode not in ["desec_done", "off"]:
            if not services.is_active("ddclient"):
                services.restart("ddclient")
                services.enable("ddclient")
            return

        if dns_mode == "off":
            if services.is_active("ddclient"):
                services.stop("ddclient")
                services.disable("ddclient")
            return

        if dns_mode == "desec_done":
            if services.is_active("ddclient"):
                services.stop("ddclient")
                services.disable("ddclient")

            dns = DNSManager()
            ipv4 = dns.get_ipv4()
            ipv6 = dns.get_ipv6()

            headers = {"Authorization": f"Token {token}"}
            
            params = {"hostname": domain}
            if ipv4:
                params["myipv4"] = ipv4
            if ipv6:
                params["myipv6"] = ipv6

            res = requests.get("https://update.dedyn.io", params=params, headers=headers)

            if res.ok:
                log.info(f"updated deSEC IPv4 ({ipv4}) and IPv6 ({ipv6}) address for '{domain}'")
            else:
                log.warning(f"failed updating IPs ({ipv4} & {ipv6}) for domain: '{domain}'")
                log.debug(f"result: {res.text} status_code: {res.status_code} url: {res.url}")
Ejemplo n.º 7
0
def handle_config():
    if request.method == "GET":
        data = dict(cfg["config"])
        try:
            data["conf"] = Path(DDCLIENT_CONFIG_PATH).read_text("utf-8")
        except FileNotFoundError:
            data["conf"] = ""
        return success(data=data)

    # save dyndns related values to configuration
    elif request.method == "POST":
        run_jobs = []
        for key in request.form:
            val = request.form.get(key)

            # special config-value 'conf' represents ddclient-config-contents
            if key == "conf":
                old_conf = Path(DDCLIENT_CONFIG_PATH).read_text("utf-8")
                if old_conf != val:
                    log.info("writing ddclient config and restarting service")
                    Path(DDCLIENT_CONFIG_PATH).write_text(val, "utf-8")

                elif len(val.strip()) == 0:
                    log.info("writing empty ddclient config")
                    Path(DDCLIENT_CONFIG_PATH).write_text(val, "utf-8")
                    services.stop("ddclient")
                    services.disable("ddclient")

                if len(val.strip()) > 0:
                    services.enable("ddclient")
                    services.restart("ddclient")

                run_jobs.append("DynDNSUpdate")

            elif key in AVAIL_CONFIGS and val is not None:
                # only allow valid DYNDNS_MODES
                if key == "dns_mode" and val not in DYNDNS_MODES:
                    log.warning(
                        f"key: 'dns_mode' has invalid value: {val} - skipping")
                    continue

                # start DynDNS update on "desec_done"
                elif key == "dns_mode" and val == "desec_done":
                    run_jobs.append("DynDNSUpdate")

                # start TrustedDomains update on new domain
                elif "domain" in key:
                    run_jobs.append("TrustedDomains")

                # deactivate proxy on request
                elif key == "proxy_active" and val.lower() == "false":
                    proxy_tunnel = ProxyTunnel()
                    proxy_tunnel.stop()

                # skip if 'val' is empty
                elif val is None:
                    log.debug(f"skipping key: '{key}' -> no value provided")
                    continue

                # convert to bool, ugly?
                if val.lower() in ["true", "false"]:
                    val = val.lower() == "true"

                # put key-value into cfg and save (yes, saving each value)
                cfg["config"][key] = val
                log.debug(f"saving key: '{key}' with value: '{val}'")
                cfg.save()

        # run jobs collected during configuration update
        if len(run_jobs) > 0:
            for job in run_jobs:
                job_queue.put(job)

        return success("DynDNS configuration saved")
Ejemplo n.º 8
0
    def full_import(self, src_path):
        if not isinstance(src_path, Path):
            src_path = Path(src_path)

        steps = [
            ("sql", lambda: self.import_sql(src_path)),
            ("config", lambda: self.import_config_dir(src_path)),
            ("nextbox", lambda: self.import_nextbox_dir(src_path)),
            ("data", lambda: self.import_dir("data", src_path)),
            ("apps", lambda: self.import_dir("apps", src_path)),
            ("letsencrypt", lambda: self.import_dir("letsencrypt", src_path)),
        ]

        if not src_path.exists():
            os.makedirs(src_path.as_posix())

        self.check_backup(src_path)

        failed = False
        log.info("starting full import")
        for step_key, step_func in steps:
            log.debug(f"full import step: {step_key}")

            # blocking step (sql)
            ret = step_func()

            # blocking call, eval directly and yield state
            if step_key in ["sql", "nextbox", "config"]:
                if ret == True:
                    log.debug(f"finished import step: {step_key}")
                    yield ("finished", (step_key, "import"), 100)
                    continue
                else:
                    log.debug(f"failed import step: {step_key}")
                    failed = True

                    yield ("failed", (step_key, "import"), 100)
                    break

            # non-blocking call(s) check_progress() and yield state
            while True:
                act, desc, percent = self.check_progress()
                if act != "active":
                    if act == "failed":
                        log.debug(f"failed import step: {step_key}")
                        failed = True
                    else:
                        log.debug(f"finished import step: {step_key}")
                    yield (act, desc, 100)
                    break
                time.sleep(1)
                yield (act, desc, percent)

        # final restore steps
        if not failed:
            # recreate apache config based on current (imported) config
            conf_path = Path(self.dirs["nextbox"]) / self.nextbox_conf
            new_cfg = yaml.safe_load(conf_path.open()).get("config", {})
            has_https = new_cfg.get("https_port")
            if has_https:
                domain = new_cfg.get("domain")

                certs = Certificates()
                my_cert = certs.get_cert(domain)
                if not my_cert:
                    log.error(
                        f"expected https/ssl, but could not find certificate: {domain}"
                    )
                    log.error("switching apache2 config to non-ssl")
                    certs.set_apache_config(ssl=False)
                    #### naaah this is evil:
                    new_cfg["https_port"] = None
                    dct = {"config": new_cfg}
                    yaml.safe_dump(dct, conf_path.open("w"))
                else:
                    log.info(
                        f"found certificate for {domain} - activating ssl")

                    certs.write_apache_ssl_conf(my_cert["domains"][0],
                                                my_cert["fullchain_path"],
                                                my_cert["privkey_path"])
                    if not certs.set_apache_config(ssl=True):
                        log.error("failed enabling ssl config for apache")
                    else:
                        log.info("re-enabled ssl for imported configuration")

            log.debug("wrote apache config according to restored data")

            # restart daemon
            log.info("finalized import - all seems good!")
            log.info(".... restarting daemon")
            services.restart("nextbox-daemon")

            yield ("completed", ("all", "import"), 100)
Ejemplo n.º 9
0
 def set_env_data(self, docker_env_path, data_dct):
     with Path(docker_env_path).open("w") as fd:
         for key, val in sorted(data_dct.items()):
             fd.write(f"{key}={val}\n")
     log.debug(f"(re)wrote {docker_env_path} during restore")
Ejemplo n.º 10
0
 def run(self, cfg, board, kwargs):
     """Overridden Thread.run() is run inside the new thread after start()"""
     log.debug(f"starting worker job: {self.name}")
     self.last_run = dt.now()
     self._run(cfg, board, kwargs)
     log.debug(f"finished worker job: {self.name}")