Ejemplo n.º 1
0
 def _check_symlinks(self):
     """Raises an exception if a symlink doesn't exist"""
     for kind in ALL_FOUR:
         link = getattr(self, kind)
         if not os.path.islink(link):
             raise errors.CertStorageError(
                 "expected {0} to be a symlink".format(link))
         target = get_link_target(link)
         if not os.path.exists(target):
             raise errors.CertStorageError("target {0} of symlink {1} does "
                                           "not exist".format(target, link))
Ejemplo n.º 2
0
    def __init__(self, configfile, config_opts=None, cli_config=None):
        """Instantiate a RenewableCert object from an existing lineage.

        :param configobj.ConfigObj configfile: an already-parsed
            ConfigObj object made from reading the renewal config file
            that defines this lineage.

        :param configobj.ConfigObj config_opts: systemwide defaults for
            renewal properties not otherwise specified in the individual
            renewal config file.
        :param .RenewerConfiguration cli_config:

        :raises .CertStorageError: if the configuration file's name didn't end
            in ".conf", or the file is missing or broken.
        :raises TypeError: if the provided renewal configuration isn't a
            ConfigObj object.

        """
        self.cli_config = cli_config
        if isinstance(configfile, configobj.ConfigObj):
            if not os.path.basename(configfile.filename).endswith(".conf"):
                raise errors.CertStorageError(
                    "renewal config file name must end in .conf")
            self.lineagename = os.path.basename(
                configfile.filename)[:-len(".conf")]
        else:
            raise TypeError("RenewableCert config must be ConfigObj object")

        # self.configuration should be used to read parameters that
        # may have been chosen based on default values from the
        # systemwide renewal configuration; self.configfile should be
        # used to make and save changes.
        self.configfile = configfile
        # TODO: Do we actually use anything from defaults and do we want to
        #       read further defaults from the systemwide renewal configuration
        #       file at this stage?
        self.configuration = config_with_defaults(config_opts)
        self.configuration.merge(self.configfile)

        if not all(x in self.configuration for x in ALL_FOUR):
            raise errors.CertStorageError(
                "renewal config file {0} is missing a required "
                "file reference".format(configfile))

        self.cert = self.configuration["cert"]
        self.privkey = self.configuration["privkey"]
        self.chain = self.configuration["chain"]
        self.fullchain = self.configuration["fullchain"]
Ejemplo n.º 3
0
    def __init__(self, config_filename, cli_config):
        """Instantiate a RenewableCert object from an existing lineage.

        :param str config_filename: the path to the renewal config file
            that defines this lineage.
        :param .RenewerConfiguration: parsed command line arguments

        :raises .CertStorageError: if the configuration file's name didn't end
            in ".conf", or the file is missing or broken.

        """
        self.cli_config = cli_config
        if not config_filename.endswith(".conf"):
            raise errors.CertStorageError(
                "renewal config file name must end in .conf")
        self.lineagename = os.path.basename(
            config_filename[:-len(".conf")])

        # self.configuration should be used to read parameters that
        # may have been chosen based on default values from the
        # systemwide renewal configuration; self.configfile should be
        # used to make and save changes.
        try:
            self.configfile = configobj.ConfigObj(config_filename)
        except configobj.ConfigObjError:
            raise errors.CertStorageError(
                "error parsing {0}".format(config_filename))
        # TODO: Do we actually use anything from defaults and do we want to
        #       read further defaults from the systemwide renewal configuration
        #       file at this stage?
        self.configuration = config_with_defaults(self.configfile)

        if not all(x in self.configuration for x in ALL_FOUR):
            raise errors.CertStorageError(
                "renewal config file {0} is missing a required "
                "file reference".format(self.configfile))

        self.cert = self.configuration["cert"]
        self.privkey = self.configuration["privkey"]
        self.chain = self.configuration["chain"]
        self.fullchain = self.configuration["fullchain"]

        self._fix_symlinks()
        self._check_symlinks()
Ejemplo n.º 4
0
    def _check_symlinks(self):
        """Raises an exception if a symlink doesn't exist"""
        def check(link):
            """Checks if symlink points to a file that exists"""
            return os.path.exists(os.path.realpath(link))

        for kind in ALL_FOUR:
            if not check(getattr(self, kind)):
                raise errors.CertStorageError(
                    "link: {0} does not exist".format(getattr(self, kind)))
Ejemplo n.º 5
0
    def current_target(self, kind):
        """Returns full path to which the specified item currently points.

        :param str kind: the lineage member item ("cert", "privkey",
            "chain", or "fullchain")

        :returns: The path to the current version of the specified
            member.
        :rtype: str

        """
        if kind not in ALL_FOUR:
            raise errors.CertStorageError("unknown kind of item")
        link = getattr(self, kind)
        if not os.path.exists(link):
            return None
        target = os.readlink(link)
        if not os.path.isabs(target):
            target = os.path.join(os.path.dirname(link), target)
        return os.path.abspath(target)
Ejemplo n.º 6
0
    def new_lineage(cls,
                    lineagename,
                    cert,
                    privkey,
                    chain,
                    renewalparams=None,
                    config=None,
                    cli_config=None):
        # pylint: disable=too-many-locals,too-many-arguments
        """Create a new certificate lineage.

        Attempts to create a certificate lineage -- enrolled for
        potential future renewal -- with the (suggested) lineage name
        lineagename, and the associated cert, privkey, and chain (the
        associated fullchain will be created automatically). Optional
        configurator and renewalparams record the configuration that was
        originally used to obtain this cert, so that it can be reused
        later during automated renewal.

        Returns a new RenewableCert object referring to the created
        lineage. (The actual lineage name, as well as all the relevant
        file paths, will be available within this object.)

        :param str lineagename: the suggested name for this lineage
            (normally the current cert's first subject DNS name)
        :param str cert: the initial certificate version in PEM format
        :param str privkey: the private key in PEM format
        :param str chain: the certificate chain in PEM format
        :param configobj.ConfigObj renewalparams: parameters that
            should be used when instantiating authenticator and installer
            objects in the future to attempt to renew this cert or deploy
            new versions of it
        :param configobj.ConfigObj config: renewal configuration
            defaults, affecting, for example, the locations of the
            directories where the associated files will be saved
        :param .RenewerConfiguration cli_config: parsed command line
            arguments

        :returns: the newly-created RenewalCert object
        :rtype: :class:`storage.renewableCert`"""

        config = config_with_defaults(config)
        # This attempts to read the renewer config file and augment or replace
        # the renewer defaults with any options contained in that file.  If
        # renewer_config_file is undefined or if the file is nonexistent or
        # empty, this .merge() will have no effect.
        config.merge(configobj.ConfigObj(cli_config.renewer_config_file))

        # Examine the configuration and find the new lineage's name
        for i in (cli_config.renewal_configs_dir, cli_config.archive_dir,
                  cli_config.live_dir):
            if not os.path.exists(i):
                os.makedirs(i, 0700)
        config_file, config_filename = le_util.unique_lineage_name(
            cli_config.renewal_configs_dir, lineagename)
        if not config_filename.endswith(".conf"):
            raise errors.CertStorageError(
                "renewal config file name must end in .conf")

        # Determine where on disk everything will go
        # lineagename will now potentially be modified based on which
        # renewal configuration file could actually be created
        lineagename = os.path.basename(config_filename)[:-len(".conf")]
        archive = os.path.join(cli_config.archive_dir, lineagename)
        live_dir = os.path.join(cli_config.live_dir, lineagename)
        if os.path.exists(archive):
            raise errors.CertStorageError("archive directory exists for " +
                                          lineagename)
        if os.path.exists(live_dir):
            raise errors.CertStorageError("live directory exists for " +
                                          lineagename)
        os.mkdir(archive)
        os.mkdir(live_dir)
        relative_archive = os.path.join("..", "..", "archive", lineagename)

        # Put the data into the appropriate files on disk
        target = dict([(kind, os.path.join(live_dir, kind + ".pem"))
                       for kind in ALL_FOUR])
        for kind in ALL_FOUR:
            os.symlink(os.path.join(relative_archive, kind + "1.pem"),
                       target[kind])
        with open(target["cert"], "w") as f:
            f.write(cert)
        with open(target["privkey"], "w") as f:
            f.write(privkey)
            # XXX: Let's make sure to get the file permissions right here
        with open(target["chain"], "w") as f:
            f.write(chain)
        with open(target["fullchain"], "w") as f:
            # assumes that OpenSSL.crypto.dump_certificate includes
            # ending newline character
            f.write(cert + chain)

        # Document what we've done in a new renewal config file
        config_file.close()
        new_config = configobj.ConfigObj(config_filename, create_empty=True)
        for kind in ALL_FOUR:
            new_config[kind] = target[kind]
        if renewalparams:
            new_config["renewalparams"] = renewalparams
            new_config.comments["renewalparams"] = [
                "", "Options and defaults used"
                " in the renewal process"
            ]
        # TODO: add human-readable comments explaining other available
        #       parameters
        new_config.write()
        return cls(new_config.filename, cli_config)