def unassign(self, assign): """ Unassign a TLS certificate from a website or service. :param dict assign: ``Assign`` object to unassign :returns: self """ signals.emit("certificates", "pre_unassign", (self, assign)) nginx_reload = False if assign["type"] == "website": websites.get(assign["id"]).ssl_disable() self.assigns.remove(assign) nginx_reload = True elif assign["type"] == "genesis": config.set("genesis", "cert_file", "") config.set("genesis", "cert_key", "") config.set("genesis", "ssl", False) config.save() self.assigns.remove(assign) else: applications.get(assign["aid"]).ssl_disable(assign["sid"]) self.assigns.remove(assign) if nginx_reload: websites.nginx_reload() signals.emit("certificates", "post_unassign", (self, assign)) return None
def get(self, id): websites.get() svcs = tracked_services.get(id) if id and not svcs: abort(404) if type(svcs) == list: return jsonify(policies=[x.as_dict() for x in svcs]) else: return jsonify(policy=svcs.as_dict())
def list_sites(): """List all websites""" try: adata = [x.serialized for x in websites.get()] _list_websites(adata) except Exception as e: raise CLIException(str(e))
def create(id, data=True, nthread=NotificationThread()): """ Convenience function to create a backup. :param str id: ID of associated app (or website) to backup :param bool data: Backup app data also? :returns: Backup info :rtype: Backup """ controller = None if id == "arkOS": controller = arkOSBackupCfg("arkOS", "setting", version=arkos_version) return controller.backup() app = applications.get(id) if app and app.type != "website" and hasattr(app, "_backup"): controller = app._backup(app.id, app.icon, version=app.version) else: sites = websites.get() for x in sites: if x.id == id: controller = x.backup break if not controller: raise errors.InvalidConfigError("No backup controller found") return controller.backup(data=data, nthread=nthread)
def put(self, id): data = request.get_json()["website"] site = websites.get(id) if not site: abort(404) if data.get("operation") == "enable": site.nginx_enable() elif data.get("operation") == "disable": site.nginx_disable() elif data.get("operation") == "enable_ssl": cert = certificates.get(data["cert"]) cert.assign("website", site.id) elif data.get("operation") == "disable_ssl": site.cert.unassign("website", site.id) elif data.get("operation") == "update": id = as_job(self._put, site) return job_response( id, data={"website": site.serialized.update({"is_ready": False})}) else: site.domain = data["domain"] site.port = data["port"] site.edit(data.get("new_name")) if data.get("new_name"): remove_record("website", id) push_record("website", site.serialized) return jsonify(website=site.serialized)
def restore(backup, data=True, nthread=NotificationThread()): """ Convenience function to restore a backup. :param Backup backup: Backup to restore :param bool data: Restore included data files as well? :returns: Backup info :rtype: Backup """ controller = None if backup["type"] == "site": sites = websites.get() for x in sites: if x.id == backup["pid"]: controller = x.backup break else: app = applications.get(backup["site_type"]) controller = app._backup(backup["pid"], backup["icon"], True) else: app = applications.get(backup["pid"]) controller = app._backup() if not controller: raise errors.InvalidConfigError("No backup controller found") b = controller.restore(backup, data, nthread) return b
def assigns(): """List all apps/sites that can use certificates.""" click.echo("Apps/Sites that can use certificates:") try: assigns = [] assigns.append({ "type": "genesis", "id": "genesis", "name": "arkOS Genesis/API" }) for x in websites.get(): assigns.append({ "type": "website", "id": x.id, "name": x.id if x.app else x.name }) for x in applications.get(installed=True): if x.type == "app" and x.uses_ssl: for y in x.get_ssl_able(): assigns.append(y) for x in assigns: imsg = click.style("(" + x["type"].capitalize() + ")", fg="green") click.echo( click.style(x["name"], fg="white", bold=True) + " " + imsg) except Exception as e: raise CLIException(str(e))
def assign(self, assign): """ Assign a TLS certificate to a website or service. :param dict assign: ``Assign`` object to assign :returns: self """ signals.emit("certificates", "pre_assign", (self, assign)) nginx_reload = False if assign["type"] == "genesis": config.set("genesis", "cert_file", self.cert_path) config.set("genesis", "cert_key", self.key_path) config.set("genesis", "ssl", True) config.save() self.assigns.append(assign) elif assign["type"] == "website": w = websites.get(assign["id"]) w.cert = self w.ssl_enable() self.assigns.append(assign) nginx_reload = True else: d = applications.get(assign["aid"]).ssl_enable(self, assign["sid"]) self.assigns.append(d) if nginx_reload: websites.nginx_reload() signals.emit("certificates", "post_assign", (self, assign)) return self
def disable(id): """Disable a website""" try: site = websites.get(id) site.nginx_disable() logger.success('ctl:site:disable', 'Disabled {0}'.format(id)) except Exception as e: raise CLIException(str(e))
def is_installed(): # Verify the different components of the server setup if not os.path.exists('/etc/radicale/config') or not os.path.isdir('/usr/lib/radicale') \ or not os.path.exists('/etc/radicale/radicale.wsgi') \ or not websites.get('radicale'): return False else: return True
def my_url(): url = "http" w = websites.get('radicale') if not w: return "" url += "s://" if w.cert else "://" url += w.addr url += (":" + str(w.port)) if w.port not in [80, 443] else "" return url
def delete(id): """Remove a website""" try: site = websites.get(id) site.remove() logger.success('ctl:site:delete', 'Removed {0}'.format(id)) except Exception as e: raise CLIException(str(e))
def update(id): """Update a website""" try: site = websites.get(id) site.update() logger.success('ctl:site:update', 'Updated {0}'.format(id)) except Exception as e: raise CLIException(str(e))
def restore(self, data): signals.emit("backups", "pre_restore", self) # Trigger pre-restore hook for the app/site self.pre_restore() # Extract all files in archive sitename = "" with tarfile.open(data["path"], "r:gz") as t: for x in t.getnames(): if x.startswith("etc/nginx/sites-available"): sitename = os.path.basename(x) t.extractall("/") # If it's a website that had a database, restore DB via SQL file too dbpasswd = "" if self.ctype == "site" and sitename: self.site = websites.get(sitename) if not self.site: websites.scan() self.site = websites.get(sitename) meta = ConfigParser.SafeConfigParser() meta.read(os.path.join(self.site.path, ".arkos")) if meta.get("website", "dbengine", None) and os.path.exists("/%s.sql"%sitename): dbmgr = databases.get_managers(meta.get("website", "dbengine")) if databases.get(sitename): databases.get(sitename).remove() db = dbmgr.add_db(sitename) with open("/%s.sql"%sitename, "r") as f: db.execute(f.read()) os.unlink("/%s.sql"%sitename) if dbmgr.meta.database_multiuser: dbpasswd = random_string()[0:16] if databases.get_user(sitename): databases.get_user(sitename).remove() db_user = dbmgr.add_user(sitename, dbpasswd) db_user.chperm("grant", db) # Trigger post-restore hook for the app/site if self.ctype == "site": self.post_restore(self.site, dbpasswd) self.site.nginx_enable() else: self.post_restore() signals.emit("backups", "post_restore", self) data["is_ready"] = True return data
def my_url(): url = "http" w = websites.get('radicale') if not w: return "" url += "s://" if w.cert else "://" url += w.addr url += (":"+str(w.port)) if w.port not in [80, 443] else "" return url
def setup(addr, port): # Make sure Radicale is installed and ready if not python.is_installed('Radicale'): python.install('radicale') # due to packaging bugs, make extra sure perms are readable st = os.stat('/usr/lib/python2.7/site-packages/radicale') for r, d, f in os.walk('/usr/lib/python2.7/site-packages/radicale'): for x in d: os.chmod(os.path.join(r, x), st.st_mode | stat.S_IROTH | stat.S_IRGRP) for x in f: os.chmod(os.path.join(r, x), st.st_mode | stat.S_IROTH | stat.S_IRGRP) if not os.path.exists('/etc/radicale/config'): if not os.path.isdir('/etc/radicale'): os.mkdir('/etc/radicale') with open('/etc/radicale/config', 'w') as f: f.write(default_config) if not os.path.isdir('/usr/lib/radicale'): os.mkdir('/usr/lib/radicale') # Add the site process u = users.SystemUser("radicale") u.add() g = groups.SystemGroup("radicale", users=["radicale"]) g.add() wsgi_file = 'import radicale\n' wsgi_file += 'radicale.log.start()\n' wsgi_file += 'application = radicale.Application()\n' with open('/etc/radicale/radicale.wsgi', 'w') as f: f.write(wsgi_file) os.chmod('/etc/radicale/radicale.wsgi', 0766) cfg = { 'directory': '/etc/radicale', 'user': '******', 'command': 'uwsgi -s /tmp/radicale.sock -C --plugin python2 --wsgi-file radicale.wsgi', 'stdout_logfile': '/var/log/radicale.log', 'stderr_logfile': '/var/log/radicale.log' } s = services.Service("radicale", "supervisor", cfg=cfg) s.add() block = [ nginx.Location( '/', nginx.Key('include', 'uwsgi_params'), nginx.Key('uwsgi_pass', 'unix:///tmp/radicale.sock'), ) ] s = websites.get("radicale") if s: s.remove() s = websites.ReverseProxy(id="radicale", name="Calendar/Contacts", addr=addr, port=port, base_path="/usr/lib/radicale", block=block) s.install()
def get(self, id): if request.args.get("rescan", None): websites.scan() sites = websites.get(id) if id and not sites: abort(404) if type(sites) == list: return jsonify(websites=[x.as_dict() for x in sites]) else: return jsonify(website=sites.as_dict())
def get(self, id): if request.args.get("rescan", None): websites.scan() sites = websites.get(id) if id and not sites: abort(404) if isinstance(sites, websites.Site): return jsonify(website=sites.serialized) else: return jsonify(websites=[x.serialized for x in sites])
def _delete(self, id): message = Message() site = websites.get(id) try: site.remove(message) message.complete("success", "%s site removed successfully" % site.meta.name) remove_record("website", id) except Exception, e: message.complete("error", "%s could not be removed: %s" % (id, str(e))) raise
def ssl_able(): assigns = [] for x in websites.get(): assigns.append({"type": "website", "id": x.id, "name": x.id if x.meta else x.name}) for x in applications.get(): if x.type == "app" and x.uses_ssl: for y in x.get_ssl_able(): assigns.append(y) return jsonify(certassigns=assigns)
def edit(id, address, port, new_name): """Edit a website""" try: site = websites.get(id) site.addr = address site.port = port site.edit(new_name or None) logger.success('ctl:site:edit', 'Edited {0}'.format(id)) except Exception as e: raise CLIException(str(e))
def unassign(self, assign): signals.emit("certificates", "pre_unassign", (self, assign)) nginx_reload = False if assign["type"] == "website": websites.get(assign["id"]).ssl_disable() self.assigns.remove(assign) nginx_reload = True elif assign["type"] == "genesis": config.set("genesis", "cert_file", "") config.set("genesis", "cert_key", "") config.set("genesis", "ssl", False) config.save() self.assigns.remove(assign) else: applications.get(assign["aid"]).ssl_disable(assign["sid"]) self.assigns.remove(assign) if nginx_reload: websites.nginx_reload() signals.emit("certificates", "post_unassign", (self, assign)) return None
def ssl_able(): assigns = [] assigns.append({"type": "genesis", "id": "genesis", "name": "arkOS Genesis/API"}) for x in websites.get(): assigns.append({"type": "website", "id": x.id, "name": x.id if x.app else x.name}) for x in applications.get(installed=True): if x.type == "app" and x.uses_ssl: for y in x.get_ssl_able(): assigns.append(y) return jsonify(assignments=assigns)
def perform_action(id, action): w = websites.get(id) if not w: abort(404) if not hasattr(w, action): abort(422) actionfunc = getattr(w, action) try: actionfunc() except Exception, e: resp = jsonify(message=str(e)) resp.status_code = 500 return resp
def perform_action(id, action): w = websites.get(id) if not w: abort(404) if not hasattr(w, action): abort(422) actionfunc = getattr(w, action) try: actionfunc() except Exception as e: logger.error("Websites", str(e)) return jsonify(errors={"msg": str(e)}), 500 finally: return Response(status=200)
def get_able(): able = [] for x in applications.get(installed=True): if x.type != "website" and hasattr(x, "_backup"): able.append({"type": "app", "icon": x.icon, "id": x.id}) for x in websites.get(): if not isinstance(x, websites.ReverseProxy): able.append({"type": "site", "icon": x.meta.icon, "id": x.id}) for x in get(): if not x["pid"] in [y["id"] for y in able]: able.append({"type": x["type"], "icon": x["icon"], "id": x["pid"]}) if not "arkOS" in [x["id"] for x in able]: able.append({"type": "app", "icon": "fa fa-cog", "id": "arkOS"}) return able
def setup(addr, port): # Make sure Radicale is installed and ready if not python.is_installed('Radicale'): python.install('radicale') # due to packaging bugs, make extra sure perms are readable st = os.stat('/usr/lib/python2.7/site-packages/radicale') for r, d, f in os.walk('/usr/lib/python2.7/site-packages/radicale'): for x in d: os.chmod(os.path.join(r, x), st.st_mode | stat.S_IROTH | stat.S_IRGRP) for x in f: os.chmod(os.path.join(r, x), st.st_mode | stat.S_IROTH | stat.S_IRGRP) if not os.path.exists('/etc/radicale/config'): if not os.path.isdir('/etc/radicale'): os.mkdir('/etc/radicale') with open('/etc/radicale/config', 'w') as f: f.write(default_config) if not os.path.isdir('/usr/lib/radicale'): os.mkdir('/usr/lib/radicale') # Add the site process u = users.SystemUser("radicale") u.add() g = groups.SystemGroup("radicale", users=["radicale"]) g.add() wsgi_file = 'import radicale\n' wsgi_file += 'radicale.log.start()\n' wsgi_file += 'application = radicale.Application()\n' with open('/etc/radicale/radicale.wsgi', 'w') as f: f.write(wsgi_file) os.chmod('/etc/radicale/radicale.wsgi', 0766) cfg = { 'directory': '/etc/radicale', 'user': '******', 'command': 'uwsgi -s /tmp/radicale.sock -C --plugin python2 --wsgi-file radicale.wsgi', 'stdout_logfile': '/var/log/radicale.log', 'stderr_logfile': '/var/log/radicale.log' } s = services.Service("radicale", "supervisor", cfg=cfg) s.add() block = [ nginx.Location('/', nginx.Key('include', 'uwsgi_params'), nginx.Key('uwsgi_pass', 'unix:///tmp/radicale.sock'), ) ] s = websites.get("radicale") if s: s.remove() s = websites.ReverseProxy(id="radicale", name="Calendar/Contacts", addr=addr, port=port, base_path="/usr/lib/radicale", block=block) s.install()
def create(id, data=True): controller = None if id == "arkOS": controller = arkOSBackupCfg(id="arkOS", icon="fa fa-cog", version=arkos_version) return controller.backup() app = applications.get(id) if app and app.type != "website" and hasattr(app, "_backup"): controller = app._backup(app.id, app.icon, version=app.version) else: sites = websites.get() for x in sites: if x.id == id: controller = x.backup break if not controller: raise Exception("No backup controller found") return controller.backup(data=data)
def restore(backup, data=True): controller = None if backup["type"] == "site": sites = websites.get() for x in sites: if x.id == backup["pid"]: controller = x.backup break else: app = applications.get(backup["site_type"]) controller = app._backup(backup["pid"], backup["icon"], True) else: app = applications.get(backup["pid"]) controller = app._backup() if not controller: raise Exception("No backup controller found") b = controller.restore(backup) return b
def get_points(id=None, path=None): """ Retrieve points of interest from the system. Points of interest are obtained at scan from websites and mounted disks. :param str id: If present, filter by point ID :param str path: Filter by filesystem path :return: Point(s)OfInterest :rtype: PointOfInterest or list thereof """ points = [] from arkos import websites for x in get(): if x.mountpoint and x.mountpoint not in ["/", "/boot"]: p = PointOfInterest(x.id, x.mountpoint, "crypt" if x.crypt else "disk", "disk outline") points.append(p) for x in websites.get(): if x.app and x.app.type == "website": p = PointOfInterest(x.id, x.data_path or x.path, "site", x.app.icon) points.append(p) for x in sharers.get_shares(): p = PointOfInterest(x.id, x.path, "share", "open folder") points.append(p) for x in sharers.get_mounts(): p = PointOfInterest(x.id, x.path, "mount", "open folder outline") points.append(p) if id: for x in points: if x.id == id: return x return None elif path: for x in points: if x.path == path: return x return None return points
def get_able(): """ Obtain a list of arkOS application instances that support backups. This list includes all currently installed websites, apps and also arkOS. :returns: Website/app information :rtype: dict """ able = [] for x in applications.get(installed=True): if x.type != "website" and hasattr(x, "_backup"): able.append({"type": "app", "icon": x.icon, "id": x.id}) for x in websites.get(): if not isinstance(x, websites.ReverseProxy): able.append({"type": "site", "icon": x.app.icon, "id": x.id}) for x in get(): if not x["pid"] in [y["id"] for y in able]: able.append({"type": x["type"], "icon": x["icon"], "id": x["pid"]}) if "arkOS" not in [x["id"] for x in able]: able.append({"type": "app", "icon": "setting", "id": "arkOS"}) return able
def get_points(id=None, path=None): points = [] from arkos import websites for x in get(): if x.mountpoint and not x.mountpoint in ["/", "/boot"]: p = PointOfInterest(x.id, x.mountpoint, "crypt" if x.crypt else "disk", "fa-hdd-o") points.append(p) for x in websites.get(): if x.meta: p = PointOfInterest(x.id, x.data_path or x.path, "site", x.meta.icon) points.append(p) if id: for x in points: if x.id == id: return x return None elif path: for x in points: if x.path == path: return x return None return points
def assign(self, assign): signals.emit("certificates", "pre_assign", (self, assign)) nginx_reload = False if assign["type"] == "genesis": config.set("genesis", "cert_file", self.cert_path) config.set("genesis", "cert_key", self.key_path) config.set("genesis", "ssl", True) config.save() self.assigns.append(assign) elif assign["type"] == "website": w = websites.get(assign["id"]) w.cert = self w.ssl_enable() self.assigns.append(assign) nginx_reload = True else: d = applications.get(assign["aid"]).ssl_enable(self, assign["sid"]) self.assigns.append(d) if nginx_reload: websites.nginx_reload() signals.emit("certificates", "post_assign", (self, assign)) return self
def put(self, id): data = json.loads(request.data)["website"] site = websites.get(id) if not site: abort(404) if data.get("operation") == "enable": site.nginx_enable() elif data.get("operation") == "disable": site.nginx_disable() elif data.get("operation") == "enable_ssl": cert = certificates.get(data["cert"]) cert.assign("website", site.id) elif data.get("operation") == "disable_ssl": site.cert.unassign("website", site.id) elif data.get("operation") == "update": site.update() else: site.addr = data["addr"] site.port = data["port"] site.edit(data.get("new_name")) push_record("website", site.as_dict()) remove_record("website", id) return jsonify(message="Site edited successfully")
def restore(self, backup, data=True, nthread=NotificationThread()): """ Restore an associated arkOS app backup. :param Backup backup: backup to restore :param bool data: Restore backed up data files too? :param NotificationThread nthread: notification thread to use :returns: ``Backup`` :rtype: dict """ nthread.title = "Restoring backup" # Trigger pre-restore hook for the app/site signals.emit("backups", "pre_restore", self) msg = "Running pre-restore for {0}...".format(backup["pid"]) nthread.update(Notification("info", "Backup", msg)) self.pre_restore() # Extract all files in archive sitename = "" nthread.update(Notification("info", "Backup", "Extracting files...")) with tarfile.open(backup["path"], "r:gz") as t: for x in t.getnames(): if x.startswith("etc/nginx/sites-available"): sitename = os.path.basename(x) t.extractall("/") # If it's a website that had a database, restore DB via SQL file too dbpasswd = "" if self.ctype == "site" and sitename: self.site = websites.get(sitename) if not self.site: websites.scan() self.site = websites.get(sitename) meta = configparser.SafeConfigParser() meta.read(os.path.join(self.site.path, ".arkos")) sql_path = "/{0}.sql".format(sitename) if meta.get("website", "dbengine", fallback=None) \ and os.path.exists(sql_path): nthread.update( Notification("info", "Backup", "Restoring database...")) dbmgr = databases.get_managers(meta.get("website", "dbengine")) if databases.get(sitename): databases.get(sitename).remove() db = dbmgr.add_db(sitename) with open(sql_path, "r") as f: db.execute(f.read()) os.unlink(sql_path) if dbmgr.meta.database_multiuser: dbpasswd = random_string(16) dbuser = databases.get_users(sitename) if dbuser: dbuser.remove() db_user = dbmgr.add_user(sitename, dbpasswd) db_user.chperm("grant", db) # Trigger post-restore hook for the app/site msg = "Running post-restore for {0}...".format(backup["pid"]) nthread.update(Notification("info", "Backup", msg)) if self.ctype == "site": self.post_restore(self.site, dbpasswd) self.site.nginx_enable() else: self.post_restore() signals.emit("backups", "post_restore", self) backup["is_ready"] = True msg = "{0} restored successfully.".format(backup["pid"]) nthread.complete(Notification("info", "Backup", msg)) return backup
def _delete(self, job, id): nthread = NotificationThread(id=job.id) site = websites.get(id) site.remove(nthread) remove_record("website", id) remove_record("policy", id)
def _request_acme_certificate(domain, webroot, nthread): nthread.title = "Requesting ACME certificate" signals.emit("certificates", "pre_add", id) domains = [domain] uid = users.get_system("http").uid gid = groups.get_system("ssl-cert").gid if webroot: webroot = os.path.join(webroot, ".well-known", "acme-challenge") acme_dir = config.get("certificates", "acme_dir") cert_dir = os.path.join(acme_dir, "certs", domain) cert_path = os.path.join(cert_dir, "cert.pem") key_path = os.path.join(cert_dir, "privkey.pem") if not os.path.exists(cert_dir): os.makedirs(cert_dir) if not webroot: sites = websites.get() for x in sites: if x.port in [80, 443] and x.domain == domain: webroot = x.add_acme_challenge() break else: webroot = websites.create_acme_dummy(domain) smsg = "Requesting certificate from Let's Encrypt CA..." nthread.update(Notification("info", "Certificates", smsg)) agree_to_tos = None has_written_files = False while True: try: leclient.issue_certificate( domains, acme_dir, acme_server=config.get("certificates", "acme_server"), certificate_file=cert_path, private_key_file=key_path, agree_to_tos_url=agree_to_tos) break except leclient.NeedToAgreeToTOS as e: agree_to_tos = e.url continue except leclient.NeedToTakeAction as e: if not has_written_files: if not os.path.exists(webroot): os.makedirs(webroot) os.chown(webroot, uid, gid) for x in e.actions: fn = os.path.join(webroot, x.file_name) with open(fn, 'w') as f: f.write(x.contents) os.chown(fn, uid, gid) has_written_files = True continue else: raise errors.InvalidConfigError( "Requesting a certificate failed - it doesn't appear your " "requested domain's DNS is pointing to your server, or " "there was a port problem. Please check these things and " "try again.") except leclient.WaitABit as e: while e.until_when > datetime.datetime.now(): until = e.until_when - datetime.datetime.now() until_secs = int(round(until.total_seconds())) + 1 if until_secs > 300: raise errors.InvalidConfigError( "Requesting a certificate failed - LE rate limiting " "detected, for a period of more than five minutes. " "Please try again later." ) nthread.update( Notification( "warning", "Certificates", "LE rate limiting detected." " Will reattempt in {0} seconds".format(until_secs)) ) time.sleep(until_secs) continue except leclient.InvalidDomainName: raise errors.InvalidConfigError( "Requesting a certificate failed - invalid domain name" ) except leclient.RateLimited: raise errors.InvalidConfigError( "Requesting a certificate failed - LE is refusing to issue " "more certificates to you for this domain. Please choose " "another domain or try again another time." ) os.chown(cert_path, -1, gid) os.chown(key_path, -1, gid) os.chmod(cert_path, 0o750) os.chmod(key_path, 0o750) with open(cert_path, "rb") as f: cert = x509.load_pem_x509_certificate(f.read(), default_backend()) with open(key_path, "rb") as f: key = serialization.load_pem_private_key( f.read(), password=None, backend=default_backend() ) sha1 = binascii.hexlify(cert.fingerprint(hashes.SHA1())).decode() md5 = binascii.hexlify(cert.fingerprint(hashes.MD5())).decode() sha1 = ":".join([sha1[i:i+2].upper() for i in range(0, len(sha1), 2)]) md5 = ":".join([md5[i:i+2].upper() for i in range(0, len(md5), 2)]) if isinstance(key.public_key(), rsa.RSAPublicKey): ktype = "RSA" elif isinstance(key.public_key(), dsa.DSAPublicKey): ktype = "DSA" elif isinstance(key.public_key(), ec.EllipticCurvePublicKey): ktype = "EC" else: ktype = "Unknown" ksize = key.key_size c = Certificate(domain, domain, cert_path, key_path, ktype, ksize, [], cert.not_valid_after, sha1, md5, is_acme=True) storage.certificates[c.id] = c with open("/etc/cron.d/arkos-acme-renew", "a") as f: f.write("0 4 * * * systemctl reload nginx\n") fln = ("30 3 * * * free_tls_certificate {0} {1} {2} {3} {4} " ">> /var/log/acme-renew.log\n") f.write(fln.format( " ".join(domains), key_path, cert_path, webroot.split("/.well-known/acme-challenge")[0], acme_dir )) signals.emit("certificates", "post_add", c) msg = "Certificate issued successfully" nthread.complete(Notification("success", "Certificates", msg)) return c