On Debian 10, dist_info() returns a lower case "debian" and the installation of certbot fails. I changed the installation method to check the distribution name in case insensitive way.
120 lines
4.5 KiB
Python
120 lines
4.5 KiB
Python
"""SSL tools."""
|
|
|
|
import os
|
|
import sys
|
|
|
|
from . import package
|
|
from . import utils
|
|
|
|
|
|
class CertificateBackend(object):
|
|
"""Base class."""
|
|
|
|
def __init__(self, config):
|
|
"""Set path to certificates."""
|
|
self.config = config
|
|
|
|
def overwrite_existing_certificate(self):
|
|
"""Check if certificate already exists."""
|
|
if os.path.exists(self.config.get("general", "tls_key_file")):
|
|
if not self.config.getboolean("general", "force"):
|
|
answer = utils.user_input(
|
|
"Overwrite the existing SSL certificate? (y/N) ")
|
|
if not answer.lower().startswith("y"):
|
|
return False
|
|
return True
|
|
|
|
|
|
class SelfSignedCertificate(CertificateBackend):
|
|
"""Create a self signed certificate."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Sanity checks."""
|
|
super(SelfSignedCertificate, self).__init__(*args, **kwargs)
|
|
if self.config.has_option("general", "tls_key_file"):
|
|
# Compatibility
|
|
return
|
|
for base_dir in ["/etc/pki/tls", "/etc/ssl"]:
|
|
if os.path.exists(base_dir):
|
|
self.config.set(
|
|
"general", "tls_key_file",
|
|
"{}/private/%(hostname)s.key".format(base_dir))
|
|
self.config.set(
|
|
"general", "tls_cert_file",
|
|
"{}/certs/%(hostname)s.cert".format(base_dir))
|
|
return
|
|
raise RuntimeError("Cannot find a directory to store certificate")
|
|
|
|
def generate_cert(self):
|
|
"""Create a certificate."""
|
|
if not self.overwrite_existing_certificate():
|
|
return
|
|
utils.printcolor(
|
|
"Generating new self-signed certificate", utils.YELLOW)
|
|
utils.exec_cmd(
|
|
"openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 "
|
|
"-subj '/CN={}' -keyout {} -out {}".format(
|
|
self.config.get("general", "hostname"),
|
|
self.config.get("general", "tls_key_file"),
|
|
self.config.get("general", "tls_cert_file"))
|
|
)
|
|
|
|
|
|
class LetsEncryptCertificate(CertificateBackend):
|
|
"""Create a certificate using letsencrypt."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Update config."""
|
|
super(LetsEncryptCertificate, self).__init__(*args, **kwargs)
|
|
self.hostname = self.config.get("general", "hostname")
|
|
self.config.set("general", "tls_cert_file", (
|
|
"/etc/letsencrypt/live/{}/fullchain.pem".format(self.hostname)))
|
|
self.config.set("general", "tls_key_file", (
|
|
"/etc/letsencrypt/live/{}/privkey.pem".format(self.hostname)))
|
|
|
|
def install_certbot(self):
|
|
"""Install certbot script to generate cert."""
|
|
name, version, _id = utils.dist_info()
|
|
name = name.lower()
|
|
if name == "ubuntu":
|
|
package.backend.update()
|
|
package.backend.install("software-properties-common")
|
|
utils.exec_cmd("add-apt-repository -y universe")
|
|
if version == "18.04":
|
|
utils.exec_cmd("add-apt-repository -y ppa:certbot/certbot")
|
|
package.backend.update()
|
|
package.backend.install("certbot")
|
|
elif name == "debian":
|
|
package.backend.update()
|
|
package.backend.install("certbot")
|
|
elif "centos" in name:
|
|
package.backend.install("certbot")
|
|
else:
|
|
utils.printcolor("Failed to install certbot, aborting.", utils.RED)
|
|
sys.exit(1)
|
|
|
|
def generate_cert(self):
|
|
"""Create a certificate."""
|
|
utils.printcolor(
|
|
"Generating new certificate using letsencrypt", utils.YELLOW)
|
|
self.install_certbot()
|
|
utils.exec_cmd(
|
|
"certbot certonly -n --standalone -d {} -m {} --agree-tos"
|
|
.format(
|
|
self.hostname, self.config.get("letsencrypt", "email")))
|
|
with open("/etc/cron.d/letsencrypt", "w") as fp:
|
|
fp.write("0 */12 * * * root certbot renew "
|
|
"--quiet --no-self-upgrade --force-renewal\n")
|
|
cfg_file = "/etc/letsencrypt/renewal/{}.conf".format(self.hostname)
|
|
pattern = "s/authenticator = standalone/authenticator = nginx/"
|
|
utils.exec_cmd("perl -pi -e '{}' {}".format(pattern, cfg_file))
|
|
|
|
|
|
def get_backend(config):
|
|
"""Return the appropriate backend."""
|
|
if not config.getboolean("certificate", "generate"):
|
|
return None
|
|
if config.get("certificate", "type") == "letsencrypt":
|
|
return LetsEncryptCertificate(config)
|
|
return SelfSignedCertificate(config)
|