def _get_signed_certificate(name, args, log=LOGGER): """Gets a signed certificate (using acme-tiny)""" account = os.path.join(args.path, 'account.key') csr = os.path.join(args.path, name, 'domain.csr') crt = os.path.join(args.path, name, 'domain.crt') out = acme_tiny.get_crt(account, csr, args.acme_dir, log, args.ca) _mode(_dump_bytes, 0640)(out, crt) os.chmod(crt, 0640)
def main(): crt = acme_tiny.get_crt(config.ACCOUNT_KEY, config.CSR, config.ACMEDIR) ssl_args = {'cert': crt} def try_read(key, setting): if hasattr(config, setting): with open(getattr(config, setting), 'r') as f: ssl_args[key] = f.read() try_read('key', 'SERVER_KEY') try_read('cabundle', 'CABUNDLE') return CLIENT.uapi('SSL', 'install_ssl', **ssl_args)
def create_crt(name): if exist_crt(name): shutil.copyfile( "%s/%s.crt" % (crt_dir, name), "%s/%s_%s.crt" % (crt_bak_dir, datetime.now().strftime("%Y%m%d_%H%M%S"), name)) signed_crt = acme_tiny.get_crt("config/account.key", "%s/%s.csr" % (crt_dir, name), acme_challenge_dir, logger, acme_ca) if chained_crt == "true": signed_crt += urlopen(acme_intermediate).read().decode("utf8") with open("%s/%s.crt" % (crt_dir, name), "w") as file: file.write(signed_crt)
def renew_domain(domain: str): d = date.today() csr = f"/etc/ssl/acme/{domain}.csr" crt = f"/etc/ssl/acme/{domain}-{d.year}-{d.month}-{d.day}.crt" crt_link = f"/etc/ssl/private/{domain}.crt" # sign the request using acme_tiny crt_data = acme_tiny.get_crt(csr=csr, acme_dir=ACME_CHALLENGE, account_key=ACME_KEY) with open(crt, "wt") as crt_file: crt_file.write(crt_data) utils.log_info("Generated signed certificate " + crt) # update the symlink to point to newly generated file if os.path.exists(crt_link): os.unlink(crt_link) os.symlink(crt, crt_link) utils.log_info("Symlinked certificate to " + crt_link) subprocess.run(["nginx", "-t"], check=True) subprocess.run(["systemctl", "reload", "nginx"], check=True) utils.log_info("Nginx reloaded")
def cron(self): domain = urllib.parse.urlparse( self.env['ir.config_parameter'].get_param('web.base.url', 'localhost')).netloc self.validate_domain(domain) account_key = self.generate_account_key() csr = self.generate_csr(domain) acme_challenge = get_challenge_dir() if not os.path.isdir(acme_challenge): os.makedirs(acme_challenge) if self.env.context.get('letsencrypt_dry_run'): crt_text = 'I\'m a test text' else: # pragma: no cover from acme_tiny import get_crt, DEFAULT_CA crt_text = get_crt(account_key, csr, acme_challenge, log=_logger, CA=DEFAULT_CA) with open(os.path.join(get_data_dir(), '%s.crt' % domain), 'w')\ as crt: crt.write(crt_text) chain_cert = urllib.request.urlopen( self.env['ir.config_parameter'].get_param( 'letsencrypt.chain_certificate_address', 'https://letsencrypt.org/certs/' 'lets-encrypt-x3-cross-signed.pem')) crt.write(str(chain_cert.read())) chain_cert.close() _logger.info('wrote %s', crt.name) reload_cmd = self.env['ir.config_parameter'].sudo().get_param( 'letsencrypt.reload_command', False) if reload_cmd: _logger.info('reloading webserver...') self.call_cmdline(['sh', '-c', reload_cmd]) else: _logger.info('no command defined for reloading webserver, please ' 'do it manually in order to apply new certificate')
def generate_cert(domain, cert_dir, challenge_dir): """Generate a TLS certificate signed by Let's Encrypt for given domain.""" account_key = cert_dir / "account.key" if not account_key.exists(): sh.openssl("genrsa", "4096", _out=str(account_key)) private_key = cert_dir / "domain.key" if not private_key.exists(): sh.openssl("genrsa", "4096", _out=str(private_key)) csr = cert_dir / "domain.csr" if not csr.exists(): sh.openssl( "req", "-new", "-sha256", "-key", private_key, "-subj", f"/CN={domain}", _out=str(csr), ) with (cert_dir / "domain.crt").open("w") as fp: fp.write(acme_tiny.get_crt(account_key, csr, challenge_dir)) csr.unlink()
def createOrRenew(domain, userkey="keys/user.key", with_www=False, interactive=False, renewalOnly=False): DOMAIN_PATH=path.join(STORE_PATH, "keys", domain) CHALLENGE_PATH=path.join(STORE_PATH, "challenges", domain) isNew = False print "Domain: %s" % domain if interactive: if not ask("Proceed?"): return False if not path.isdir(DOMAIN_PATH): if renewalOnly: print "Set to renewal only, refusing to create new key" return False print "Directory not found, creating..." os.mkdir(DOMAIN_PATH) isNew = True if not path.isdir(CHALLENGE_PATH): print "Creating challenge dir..." os.mkdir(CHALLENGE_PATH) isNew = True #Fetch intermediate print "Downloading intermediate certificate..." intermediateKey = getHttp(INTERMEDIATE_KEY_URL) if not intermediateKey: print "Failed to download intermediate certificate" return False # Check for user key if not path.isfile(USERKEY): if not ask("No user key was found, generate?"): return False print "Generating user key: %s..." % USERKEY sh("openssl genrsa 4096 > %s" % USERKEY) # Build CSR _baseName = "%s/%s" % (DOMAIN_PATH, domain) KEY=_baseName + ".key" CSR=_baseName + ".csr" CRT=_baseName + ".crt" if not path.isfile(KEY): print "Generating private key..." sh("openssl genrsa 4096 > %s" % KEY) else: print " Existing private key found" if not path.isfile(CSR): print "Generating certificate signing request (csr)..." if with_www: print " Set www=true" sh('openssl req -new -sha256 -key %s -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:%s,DNS:www.%s")) > %s' % (KEY, domain, domain, CSR)) else: sh('openssl req -new -sha256 -key %s -subj "/CN=%s" > %s' % (KEY, domain, CSR)) else: print " Existing CSR found." if isNew and interactive: print print "At this point, make sure your web server is correctly set up" print "to point the the challenge directory:" print "http://%s/.well-known/acme-challenge/ -> %s" % (domain, CHALLENGE_PATH) try: raw_input("[Press enter to continue]") except: return False # Create certificate try: crtData = acme_tiny.get_crt(USERKEY, CSR, CHALLENGE_PATH) open(CRT, 'w').write(crtData) except Exception as e: print "Error getting certificate" print e return False # Concat and output certs to /etc/certs if not path.isdir(OUT_PATH): os.mkdir(OUT_PATH) OUT = path.join(OUT_PATH, domain + ".pem") print "Writing full key to %s..." % OUT FULL_KEY = open(KEY).read() + open(CRT).read() + intermediateKey open(OUT, 'w').write(FULL_KEY) return True
def createOrRenew(domain, userkey="keys/user.key", with_www=False, interactive=False, renewalOnly=False): DOMAIN_PATH = path.join(STORE_PATH, "keys", domain) CHALLENGE_PATH = path.join(STORE_PATH, "challenges", domain) isNew = False print "Domain: %s" % domain if interactive: if not ask("Proceed?"): return False if not path.isdir(DOMAIN_PATH): if renewalOnly: print "Set to renewal only, refusing to create new key" return False print "Directory not found, creating..." os.mkdir(DOMAIN_PATH) isNew = True if not path.isdir(CHALLENGE_PATH): print "Creating challenge dir..." os.mkdir(CHALLENGE_PATH) isNew = True #Fetch intermediate print "Downloading intermediate certificate..." intermediateKey = getHttp(INTERMEDIATE_KEY_URL) if not intermediateKey: print "Failed to download intermediate certificate" return False # Check for user key if not path.isfile(USERKEY): if not ask("No user key was found, generate?"): return False print "Generating user key: %s..." % USERKEY sh("openssl genrsa 4096 > %s" % USERKEY) # Build CSR _baseName = "%s/%s" % (DOMAIN_PATH, domain) KEY = _baseName + ".key" CSR = _baseName + ".csr" CRT = _baseName + ".crt" if not path.isfile(KEY): print "Generating private key..." sh("openssl genrsa 4096 > %s" % KEY) else: print " Existing private key found" if not path.isfile(CSR): print "Generating certificate signing request (csr)..." if with_www: print " Set www=true" sh('openssl req -new -sha256 -key %s -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:%s,DNS:www.%s")) > %s' % (KEY, domain, domain, CSR)) else: sh('openssl req -new -sha256 -key %s -subj "/CN=%s" > %s' % (KEY, domain, CSR)) else: print " Existing CSR found." if isNew and interactive: print print "At this point, make sure your web server is correctly set up" print "to point the the challenge directory:" print "http://%s/.well-known/acme-challenge/ -> %s" % (domain, CHALLENGE_PATH) try: raw_input("[Press enter to continue]") except: return False # Create certificate try: crtData = acme_tiny.get_crt(USERKEY, CSR, CHALLENGE_PATH) open(CRT, 'w').write(crtData) except Exception as e: print "Error getting certificate" print e return False # Concat and output certs to /etc/certs if not path.isdir(OUT_PATH): os.mkdir(OUT_PATH) OUT = path.join(OUT_PATH, domain + ".pem") print "Writing full key to %s..." % OUT FULL_KEY = open(KEY).read() + open(CRT).read() + intermediateKey open(OUT, 'w').write(FULL_KEY) return True
def sign_cert(self): """ Sign a certificate for the given domain and csr file """ import acme_tiny self.cert_directory = "{}{}".format(self.conf['CERT_DIR'], self.domain) sign_args = [ sys.executable, self.conf["ACME_TINY"], '--account-key', self.conf["ACCOUNT_KEY"], '--csr', self.csr_file, '--acme-dir', self.conf["CHALLENGE_FOLDER"] ] if self.conf['STAGING_ONLY']: sign_args += ["--ca", self.conf["STAGING_CA"]] logging.debug( ('Requesting signed certificate for {} with the equivalent for ' + 'the following command:\n\t$ {}').format(self.domain, " ".join(sign_args))) if not self.conf['DRY_RUN']: # if we are doing a real run, create a backup for writing new crt if (os.path.exists(self.crt_file) and not os.path.exists(self.crt_file + '_backup') and not self.conf['STAGING_ONLY']): shutil.copyfile(self.crt_file, self.crt_file + '_backup') # Do not override crts with staging_only crts if self.conf['STAGING_ONLY']: crt = "{}/{}_staging.crt".format(self.cert_directory, self.domain) else: crt = "{}/{}.crt".format(self.cert_directory, self.domain) # Handoff to acme-tiny try: if self.conf['STAGING_ONLY']: cert = acme_tiny.get_crt(self.conf["ACCOUNT_KEY"], self.csr_file, self.conf["CHALLENGE_FOLDER"], CA=self.conf["STAGING_CA"]) else: cert = acme_tiny.get_crt(self.conf["ACCOUNT_KEY"], self.csr_file, self.conf["CHALLENGE_FOLDER"]) # Write the actual cert-file with open(crt, 'w') as cert_file: cert_file.write(cert) # If there is an intermediate/chain cert, append it # This is necessary for nginx and does not hurt apache-uses if os.path.exists(self.conf['CHAIN_CERT']): fp = open(self.conf['CHAIN_CERT'], 'r') intermediate = fp.read() cert_file.write(intermediate) fp.close() except IOError as err: logging.debug("Error: Permission denied: " + err.filename) raise LetsEncryptFatalException( "Could not write into the acme-challange " + "folder (rerun with -v for debug information)") except Exception as err: logging.debug("Error: " + err.message) if 'urn:acme:error:rateLimited' in err.message: raise LetsEncryptRateLimitException( "Rate Limit encountered") elif 'Wrote file to' in err.message: raise LetsEncryptFatalException( "Acme-challange folder was not accessible from " + "the web (rerun with -v for debug information)") else: raise LetsEncryptGenericException( "Certificate Signing Error:\n\t{}".format(err)) # If we were successful, remove the backup crt if os.path.exists(self.crt_file + '_backup'): os.remove(self.crt_file + '_backup') else: logging.debug("Dry-Run requested, command not executed!")