Beispiel #1
0
    def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts",
              config_root="debian_apache_2_4/multiple_vhosts/apache2",
              vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"):
        # pylint: disable=arguments-differ
        super(ApacheTest, self).setUp()

        self.temp_dir, self.config_dir, self.work_dir = common.dir_setup(
            test_dir=test_dir,
            pkg="certbot_apache.tests")

        self.config_path = os.path.join(self.temp_dir, config_root)
        self.vhost_path = os.path.join(self.temp_dir, vhost_root)

        self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector(
            "rsa512_key.pem"))

        self.config = get_apache_configurator(self.config_path, vhost_root,
                                              self.config_dir, self.work_dir)

        # Make sure all vhosts in sites-enabled are symlinks (Python packaging
        # does not preserve symlinks)
        sites_enabled = os.path.join(self.config_path, "sites-enabled")
        if not os.path.exists(sites_enabled):
            return

        for vhost_basename in os.listdir(sites_enabled):
            # Keep the one non-symlink test vhost in place
            if vhost_basename == "non-symlink.conf":
                continue
            vhost = os.path.join(sites_enabled, vhost_basename)
            if not os.path.islink(vhost):  # pragma: no cover
                os.remove(vhost)
                target = os.path.join(
                    os.path.pardir, "sites-available", vhost_basename)
                os.symlink(target, vhost)
Beispiel #2
0
 def _symlink_to_account_dir(self, prev_server_path: str, server_path: str,
                             account_id: str) -> None:
     prev_account_dir = self._account_dir_path_for_server_path(
         account_id, prev_server_path)
     new_account_dir = self._account_dir_path_for_server_path(
         account_id, server_path)
     os.symlink(prev_account_dir, new_account_dir)
Beispiel #3
0
 def test_missing_cert(self):
     from certbot._internal import storage
     self.assertRaises(errors.CertStorageError, storage.RenewableCert,
                       self.config_file.filename, self.config)
     os.symlink("missing", self.config_file[ALL_FOUR[0]])
     self.assertRaises(errors.CertStorageError, storage.RenewableCert,
                       self.config_file.filename, self.config)
Beispiel #4
0
 def _symlink_to_account_dir(self, prev_server_path, server_path,
                             account_id):
     prev_account_dir = self._account_dir_path_for_server_path(
         account_id, prev_server_path)
     new_account_dir = self._account_dir_path_for_server_path(
         account_id, server_path)
     os.symlink(prev_account_dir, new_account_dir)
Beispiel #5
0
    def test_update_live_symlinks(self):
        """Test update_live_symlinks"""
        # pylint: disable=too-many-statements
        # create files with incorrect symlinks
        from certbot import cert_manager
        archive_paths = {}
        for domain in self.domains:
            custom_archive = self.domains[domain]
            if custom_archive is not None:
                archive_dir_path = custom_archive
            else:
                archive_dir_path = os.path.join(self.config.default_archive_dir, domain)
            archive_paths[domain] = dict((kind,
                os.path.join(archive_dir_path, kind + "1.pem")) for kind in ALL_FOUR)
            for kind in ALL_FOUR:
                live_path = self.config_files[domain][kind]
                archive_path = archive_paths[domain][kind]
                open(archive_path, 'a').close()
                # path is incorrect but base must be correct
                os.symlink(os.path.join(self.config.config_dir, kind + "1.pem"), live_path)

        # run update symlinks
        cert_manager.update_live_symlinks(self.config)

        # check that symlinks go where they should
        prev_dir = os.getcwd()
        try:
            for domain in self.domains:
                for kind in ALL_FOUR:
                    os.chdir(os.path.dirname(self.config_files[domain][kind]))
                    self.assertEqual(
                        os.path.realpath(os.readlink(self.config_files[domain][kind])),
                        os.path.realpath(archive_paths[domain][kind]))
        finally:
            os.chdir(prev_dir)
Beispiel #6
0
    def _update_link_to(self, kind, version):
        """Make the specified item point at the specified version.

        (Note that this method doesn't verify that the specified version
        exists.)

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

        """
        if kind not in ALL_FOUR:
            raise errors.CertStorageError("unknown kind of item")
        link = getattr(self, kind)
        filename = "{0}{1}.pem".format(kind, version)
        # Relative rather than absolute target directory
        target_directory = os.path.dirname(os.readlink(link))
        # TODO: it could be safer to make the link first under a temporary
        #       filename, then unlink the old link, then rename the new link
        #       to the old link; this ensures that this process is able to
        #       create symlinks.
        # TODO: we might also want to check consistency of related links
        #       for the other corresponding items
        os.unlink(link)
        os.symlink(os.path.join(target_directory, filename), link)
Beispiel #7
0
    def _update_link_to(self, kind, version):
        """Make the specified item point at the specified version.

        (Note that this method doesn't verify that the specified version
        exists.)

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

        """
        if kind not in ALL_FOUR:
            raise errors.CertStorageError("unknown kind of item")
        link = getattr(self, kind)
        filename = "{0}{1}.pem".format(kind, version)
        # Relative rather than absolute target directory
        target_directory = os.path.dirname(filesystem.readlink(link))
        # TODO: it could be safer to make the link first under a temporary
        #       filename, then unlink the old link, then rename the new link
        #       to the old link; this ensures that this process is able to
        #       create symlinks.
        # TODO: we might also want to check consistency of related links
        #       for the other corresponding items
        os.unlink(link)
        os.symlink(os.path.join(target_directory, filename), link)
Beispiel #8
0
 def test_current_version(self):
     for ver in (1, 5, 10, 20):
         self._write_out_kind("cert", ver)
     os.unlink(self.test_rc.cert)
     os.symlink(os.path.join("..", "..", "archive", "example.org",
                             "cert10.pem"), self.test_rc.cert)
     self.assertEqual(self.test_rc.current_version("cert"), 10)
Beispiel #9
0
    def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts",
              config_root="debian_apache_2_4/multiple_vhosts/apache2",
              vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"):
        # pylint: disable=arguments-differ
        super(ApacheTest, self).setUp()

        self.temp_dir, self.config_dir, self.work_dir = common.dir_setup(
            test_dir=test_dir,
            pkg="certbot_apache.tests")

        self.config_path = os.path.join(self.temp_dir, config_root)
        self.vhost_path = os.path.join(self.temp_dir, vhost_root)

        self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector(
            "rsa512_key.pem"))

        self.config = get_apache_configurator(self.config_path, vhost_root,
                                              self.config_dir, self.work_dir)

        # Make sure all vhosts in sites-enabled are symlinks (Python packaging
        # does not preserve symlinks)
        sites_enabled = os.path.join(self.config_path, "sites-enabled")
        if not os.path.exists(sites_enabled):
            return

        for vhost_basename in os.listdir(sites_enabled):
            # Keep the one non-symlink test vhost in place
            if vhost_basename == "non-symlink.conf":
                continue
            vhost = os.path.join(sites_enabled, vhost_basename)
            if not os.path.islink(vhost):  # pragma: no cover
                os.remove(vhost)
                target = os.path.join(
                    os.path.pardir, "sites-available", vhost_basename)
                os.symlink(target, vhost)
Beispiel #10
0
 def test_current_version(self):
     for ver in (1, 5, 10, 20):
         self._write_out_kind("cert", ver)
     os.unlink(self.test_rc.cert)
     os.symlink(os.path.join("..", "..", "archive", "example.org",
                             "cert10.pem"), self.test_rc.cert)
     self.assertEqual(self.test_rc.current_version("cert"), 10)
Beispiel #11
0
    def test_update_live_symlinks(self):
        """Test update_live_symlinks"""
        # pylint: disable=too-many-statements
        # create files with incorrect symlinks
        from certbot import cert_manager
        archive_paths = {}
        for domain in self.domains:
            custom_archive = self.domains[domain]
            if custom_archive is not None:
                archive_dir_path = custom_archive
            else:
                archive_dir_path = os.path.join(self.config.default_archive_dir, domain)
            archive_paths[domain] = dict((kind,
                os.path.join(archive_dir_path, kind + "1.pem")) for kind in ALL_FOUR)
            for kind in ALL_FOUR:
                live_path = self.config_files[domain][kind]
                archive_path = archive_paths[domain][kind]
                open(archive_path, 'a').close()
                # path is incorrect but base must be correct
                os.symlink(os.path.join(self.config.config_dir, kind + "1.pem"), live_path)

        # run update symlinks
        cert_manager.update_live_symlinks(self.config)

        # check that symlinks go where they should
        prev_dir = os.getcwd()
        try:
            for domain in self.domains:
                for kind in ALL_FOUR:
                    os.chdir(os.path.dirname(self.config_files[domain][kind]))
                    self.assertEqual(
                        filesystem.realpath(os.readlink(self.config_files[domain][kind])),
                        filesystem.realpath(archive_paths[domain][kind]))
        finally:
            os.chdir(prev_dir)
Beispiel #12
0
 def _symlink_to_accounts_dir(self, prev_server_path, server_path):
     accounts_dir = self.config.accounts_dir_for_server_path(server_path)
     if os.path.islink(accounts_dir):
         os.unlink(accounts_dir)
     else:
         os.rmdir(accounts_dir)
     prev_account_dir = self.config.accounts_dir_for_server_path(prev_server_path)
     os.symlink(prev_account_dir, accounts_dir)
Beispiel #13
0
 def _symlink_to_accounts_dir(self, prev_server_path, server_path):
     accounts_dir = self.config.accounts_dir_for_server_path(server_path)
     if os.path.islink(accounts_dir):
         os.unlink(accounts_dir)
     else:
         os.rmdir(accounts_dir)
     prev_account_dir = self.config.accounts_dir_for_server_path(prev_server_path)
     os.symlink(prev_account_dir, accounts_dir)
Beispiel #14
0
 def test_missing_cert(self):
     from certbot import storage
     self.assertRaises(errors.CertStorageError,
                       storage.RenewableCert,
                       self.config_file.filename, self.config)
     os.symlink("missing", self.config_file[ALL_FOUR[0]])
     self.assertRaises(errors.CertStorageError,
                       storage.RenewableCert,
                       self.config_file.filename, self.config)
Beispiel #15
0
    def _update_symlinks(self):
        """Updates symlinks to use archive_dir"""
        for kind in ALL_FOUR:
            link = getattr(self, kind)
            previous_link = get_link_target(link)
            new_link = os.path.join(self.relative_archive_dir(link),
                os.path.basename(previous_link))

            os.unlink(link)
            os.symlink(new_link, link)
Beispiel #16
0
    def _update_symlinks(self):
        """Updates symlinks to use archive_dir"""
        for kind in ALL_FOUR:
            link = getattr(self, kind)
            previous_link = get_link_target(link)
            new_link = os.path.join(self.relative_archive_dir(link),
                                    os.path.basename(previous_link))

            os.unlink(link)
            os.symlink(new_link, link)
Beispiel #17
0
 def _write_out_kind(self, kind, ver, value=None):
     link = getattr(self.test_rc, kind)
     if os.path.lexists(link):
         os.unlink(link)
     os.symlink(os.path.join(os.path.pardir, os.path.pardir, "archive",
                             "example.org", "{0}{1}.pem".format(kind, ver)),
                link)
     with open(link, "wb") as f:
         f.write(kind.encode('ascii') if value is None else value)
     if kind == "privkey":
         os.chmod(link, 0o600)
Beispiel #18
0
 def _write_out_kind(self, kind, ver, value=None):
     link = getattr(self.test_rc, kind)
     if os.path.lexists(link):
         os.unlink(link)
     os.symlink(
         os.path.join(os.path.pardir, os.path.pardir, "archive",
                      "example.org", "{0}{1}.pem".format(kind, ver)), link)
     with open(link, "wb") as f:
         f.write(kind.encode('ascii') if value is None else value)
     if kind == "privkey":
         filesystem.chmod(link, 0o600)
Beispiel #19
0
 def test_update_symlinks(self):
     from certbot import storage
     archive_dir_path = os.path.join(self.config.config_dir, "archive", "example.org")
     for kind in ALL_FOUR:
         live_path = self.config_file[kind]
         basename = kind + "1.pem"
         archive_path = os.path.join(archive_dir_path, basename)
         open(archive_path, 'a').close()
         os.symlink(os.path.join(self.config.config_dir, basename), live_path)
     self.assertRaises(errors.CertStorageError,
                       storage.RenewableCert, self.config_file.filename,
                       self.config)
     storage.RenewableCert(self.config_file.filename, self.config,
         update_symlinks=True)
Beispiel #20
0
 def test_update_symlinks(self):
     from certbot._internal import storage
     archive_dir_path = os.path.join(self.config.config_dir, "archive", "example.org")
     for kind in ALL_FOUR:
         live_path = self.config_file[kind]
         basename = kind + "1.pem"
         archive_path = os.path.join(archive_dir_path, basename)
         open(archive_path, 'a').close()
         os.symlink(os.path.join(self.config.config_dir, basename), live_path)
     self.assertRaises(errors.CertStorageError,
                       storage.RenewableCert, self.config_file.filename,
                       self.config)
     storage.RenewableCert(self.config_file.filename, self.config,
         update_symlinks=True)
Beispiel #21
0
    def test_symlink_resolution(self):
        link_path = os.path.join(self.tempdir, 'link')
        os.symlink(self.probe_path, link_path)

        ref_dacl_probe = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl()
        ref_dacl_link = _get_security_dacl(link_path).GetSecurityDescriptorDacl()

        filesystem.chmod(link_path, 0o700)

        # Assert the real file is impacted, not the link.
        cur_dacl_probe = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl()
        cur_dacl_link = _get_security_dacl(link_path).GetSecurityDescriptorDacl()
        self.assertFalse(filesystem._compare_dacls(ref_dacl_probe, cur_dacl_probe))  # pylint: disable=protected-access
        self.assertTrue(filesystem._compare_dacls(ref_dacl_link, cur_dacl_link))  # pylint: disable=protected-access
Beispiel #22
0
    def update_all_links_to(self, version):
        """Change all member objects to point to the specified version.

        :param int version: the desired version

        """
        with error_handler.ErrorHandler(self._fix_symlinks):
            previous_links = self._previous_symlinks()
            for kind, link in previous_links:
                os.symlink(self.current_target(kind), link)

            for kind in ALL_FOUR:
                self._update_link_to(kind, version)

            for _, link in previous_links:
                os.unlink(link)
Beispiel #23
0
    def update_all_links_to(self, version):
        """Change all member objects to point to the specified version.

        :param int version: the desired version

        """
        with error_handler.ErrorHandler(self._fix_symlinks):
            previous_links = self._previous_symlinks()
            for kind, link in previous_links:
                os.symlink(self.current_target(kind), link)

            for kind in ALL_FOUR:
                self._update_link_to(kind, version)

            for _, link in previous_links:
                os.unlink(link)
Beispiel #24
0
 def test_current_target(self):
     # Relative path logic
     self._write_out_kind("cert", 17)
     self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
                                      os.path.join(self.config.config_dir, "archive",
                                                   "example.org",
                                                   "cert17.pem")))
     # Absolute path logic
     os.unlink(self.test_rc.cert)
     os.symlink(os.path.join(self.config.config_dir, "archive", "example.org",
                             "cert17.pem"), self.test_rc.cert)
     with open(self.test_rc.cert, "w") as f:
         f.write("cert")
     self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
                                      os.path.join(self.config.config_dir, "archive",
                                                   "example.org",
                                                   "cert17.pem")))
Beispiel #25
0
 def test_current_target(self):
     # Relative path logic
     self._write_out_kind("cert", 17)
     self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
                                      os.path.join(self.config.config_dir, "archive",
                                                   "example.org",
                                                   "cert17.pem")))
     # Absolute path logic
     os.unlink(self.test_rc.cert)
     os.symlink(os.path.join(self.config.config_dir, "archive", "example.org",
                             "cert17.pem"), self.test_rc.cert)
     with open(self.test_rc.cert, "w") as f:
         f.write("cert")
     self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
                                      os.path.join(self.config.config_dir, "archive",
                                                   "example.org",
                                                   "cert17.pem")))
Beispiel #26
0
def make_lineage(config_dir: str, testfile: str, ec: bool = False) -> str:
    """Creates a lineage defined by testfile.

    This creates the archive, live, and renewal directories if
    necessary and creates a simple lineage.

    :param str config_dir: path to the configuration directory
    :param str testfile: configuration file to base the lineage on
    :param bool ec: True if we generate the lineage with an ECDSA key

    :returns: path to the renewal conf file for the created lineage
    :rtype: str

    """
    lineage_name = testfile[:-len('.conf')]

    conf_dir = os.path.join(config_dir, constants.RENEWAL_CONFIGS_DIR)
    archive_dir = os.path.join(config_dir, constants.ARCHIVE_DIR, lineage_name)
    live_dir = os.path.join(config_dir, constants.LIVE_DIR, lineage_name)

    for directory in (
            archive_dir,
            conf_dir,
            live_dir,
    ):
        if not os.path.exists(directory):
            filesystem.makedirs(directory)

    sample_archive = vector_path(
        'sample-archive{}'.format('-ec' if ec else ''))
    for kind in os.listdir(sample_archive):
        shutil.copyfile(os.path.join(sample_archive, kind),
                        os.path.join(archive_dir, kind))

    for kind in storage.ALL_FOUR:
        os.symlink(os.path.join(archive_dir, '{0}1.pem'.format(kind)),
                   os.path.join(live_dir, '{0}.pem'.format(kind)))

    conf_path = os.path.join(config_dir, conf_dir, testfile)
    with open(vector_path(testfile)) as src:
        with open(conf_path, 'w') as dst:
            dst.writelines(
                line.replace('MAGICDIR', config_dir) for line in src)

    return conf_path
Beispiel #27
0
    def _fix_symlinks(self):
        """Fixes symlinks in the event of an incomplete version update.

        If there is no problem with the current symlinks, this function
        has no effect.

        """
        previous_symlinks = self._previous_symlinks()
        if all(os.path.exists(link[1]) for link in previous_symlinks):
            for kind, previous_link in previous_symlinks:
                current_link = getattr(self, kind)
                if os.path.lexists(current_link):
                    os.unlink(current_link)
                os.symlink(os.readlink(previous_link), current_link)

        for _, link in previous_symlinks:
            if os.path.exists(link):
                os.unlink(link)
Beispiel #28
0
    def _fix_symlinks(self):
        """Fixes symlinks in the event of an incomplete version update.

        If there is no problem with the current symlinks, this function
        has no effect.

        """
        previous_symlinks = self._previous_symlinks()
        if all(os.path.exists(link[1]) for link in previous_symlinks):
            for kind, previous_link in previous_symlinks:
                current_link = getattr(self, kind)
                if os.path.lexists(current_link):
                    os.unlink(current_link)
                os.symlink(filesystem.readlink(previous_link), current_link)

        for _, link in previous_symlinks:
            if os.path.exists(link):
                os.unlink(link)
Beispiel #29
0
    def enable_site(self, vhost: VirtualHost) -> None:
        """Enables an available site, Apache reload required.

        .. note:: Does not make sure that the site correctly works or that all
                  modules are enabled appropriately.

        :param vhost: vhost to enable
        :type vhost: :class:`~certbot_apache._internal.obj.VirtualHost`

        :raises .errors.NotSupportedError: If filesystem layout is not
            supported.

        """
        if vhost.enabled:
            return None

        enabled_path = ("%s/sites-enabled/%s" %
                        (self.parser.root,
                         os.path.basename(vhost.filep)))
        if not os.path.isdir(os.path.dirname(enabled_path)):
            # For some reason, sites-enabled / sites-available do not exist
            # Call the parent method
            return super().enable_site(vhost)
        self.reverter.register_file_creation(False, enabled_path)
        try:
            os.symlink(vhost.filep, enabled_path)
        except OSError as err:
            if os.path.islink(enabled_path) and filesystem.realpath(
               enabled_path) == vhost.filep:
                # Already in shape
                vhost.enabled = True
                return None
            logger.error(
                "Could not symlink %s to %s, got error: %s", enabled_path,
                vhost.filep, err.strerror)
            errstring = ("Encountered error while trying to enable a " +
                         "newly created VirtualHost located at {0} by " +
                         "linking to it from {1}")
            raise errors.NotSupportedError(errstring.format(vhost.filep,
                                                            enabled_path))
        vhost.enabled = True
        logger.info("Enabling available site: %s", vhost.filep)
        self.save_notes += "Enabled site %s\n" % vhost.filep
        return None
Beispiel #30
0
def make_lineage(config_dir, testfile):
    """Creates a lineage defined by testfile.

    This creates the archive, live, and renewal directories if
    necessary and creates a simple lineage.

    :param str config_dir: path to the configuration directory
    :param str testfile: configuration file to base the lineage on

    :returns: path to the renewal conf file for the created lineage
    :rtype: str

    """
    lineage_name = testfile[:-len('.conf')]

    conf_dir = os.path.join(
        config_dir, constants.RENEWAL_CONFIGS_DIR)
    archive_dir = os.path.join(
        config_dir, constants.ARCHIVE_DIR, lineage_name)
    live_dir = os.path.join(
        config_dir, constants.LIVE_DIR, lineage_name)

    for directory in (archive_dir, conf_dir, live_dir,):
        if not os.path.exists(directory):
            os.makedirs(directory)

    sample_archive = vector_path('sample-archive')
    for kind in os.listdir(sample_archive):
        shutil.copyfile(os.path.join(sample_archive, kind),
                        os.path.join(archive_dir, kind))

    for kind in storage.ALL_FOUR:
        os.symlink(os.path.join(archive_dir, '{0}1.pem'.format(kind)),
                   os.path.join(live_dir, '{0}.pem'.format(kind)))

    conf_path = os.path.join(config_dir, conf_dir, testfile)
    with open(vector_path(testfile)) as src:
        with open(conf_path, 'w') as dst:
            dst.writelines(
                line.replace('MAGICDIR', config_dir) for line in src)

    return conf_path
Beispiel #31
0
    def test_symlink_resolution(self):
        # Absolute resolution
        link_path = os.path.join(self.tempdir, 'link_abs')
        os.symlink(self.probe_path, link_path)

        self.assertEqual(self.probe_path, filesystem.realpath(self.probe_path))
        self.assertEqual(self.probe_path, filesystem.realpath(link_path))

        # Relative resolution
        curdir = os.getcwd()
        link_path = os.path.join(self.tempdir, 'link_rel')
        probe_name = os.path.basename(self.probe_path)
        try:
            os.chdir(os.path.dirname(self.probe_path))
            os.symlink(probe_name, link_path)

            self.assertEqual(self.probe_path, filesystem.realpath(probe_name))
            self.assertEqual(self.probe_path, filesystem.realpath(link_path))
        finally:
            os.chdir(curdir)
Beispiel #32
0
    def test_symlink_loop_mitigation(self):
        link1_path = os.path.join(self.tempdir, 'link1')
        link2_path = os.path.join(self.tempdir, 'link2')
        link3_path = os.path.join(self.tempdir, 'link3')
        os.symlink(link1_path, link2_path)
        os.symlink(link2_path, link3_path)
        os.symlink(link3_path, link1_path)

        with self.assertRaises(RuntimeError) as error:
            filesystem.realpath(link1_path)
        self.assertTrue('link1 is a loop!' in str(error.exception))
Beispiel #33
0
 def test_consistent(self):
     # pylint: disable=protected-access
     oldcert = self.test_rc.cert
     self.test_rc.cert = "relative/path"
     # Absolute path for item requirement
     self.assertFalse(self.test_rc._consistent())
     self.test_rc.cert = oldcert
     # Items must exist requirement
     self.assertFalse(self.test_rc._consistent())
     # Items must be symlinks requirements
     fill_with_sample_data(self.test_rc)
     self.assertFalse(self.test_rc._consistent())
     unlink_all(self.test_rc)
     # Items must point to desired place if they are relative
     for kind in ALL_FOUR:
         os.symlink(os.path.join("..", kind + "17.pem"),
                    getattr(self.test_rc, kind))
     self.assertFalse(self.test_rc._consistent())
     unlink_all(self.test_rc)
     # Items must point to desired place if they are absolute
     for kind in ALL_FOUR:
         os.symlink(os.path.join(self.config.config_dir, kind + "17.pem"),
                    getattr(self.test_rc, kind))
     self.assertFalse(self.test_rc._consistent())
     unlink_all(self.test_rc)
     # Items must point to things that exist
     for kind in ALL_FOUR:
         os.symlink(
             os.path.join("..", "..", "archive", "example.org",
                          kind + "17.pem"), getattr(self.test_rc, kind))
     self.assertFalse(self.test_rc._consistent())
     # This version should work
     fill_with_sample_data(self.test_rc)
     self.assertTrue(self.test_rc._consistent())
     # Items must point to things that follow the naming convention
     os.unlink(self.test_rc.fullchain)
     os.symlink(
         os.path.join("..", "..", "archive", "example.org",
                      "fullchain_17.pem"), self.test_rc.fullchain)
     with open(self.test_rc.fullchain, "w") as f:
         f.write("wrongly-named fullchain")
     self.assertFalse(self.test_rc._consistent())
Beispiel #34
0
 def test_consistent(self):
     # pylint: disable=too-many-statements,protected-access
     oldcert = self.test_rc.cert
     self.test_rc.cert = "relative/path"
     # Absolute path for item requirement
     self.assertFalse(self.test_rc._consistent())
     self.test_rc.cert = oldcert
     # Items must exist requirement
     self.assertFalse(self.test_rc._consistent())
     # Items must be symlinks requirements
     fill_with_sample_data(self.test_rc)
     self.assertFalse(self.test_rc._consistent())
     unlink_all(self.test_rc)
     # Items must point to desired place if they are relative
     for kind in ALL_FOUR:
         os.symlink(os.path.join("..", kind + "17.pem"),
                    getattr(self.test_rc, kind))
     self.assertFalse(self.test_rc._consistent())
     unlink_all(self.test_rc)
     # Items must point to desired place if they are absolute
     for kind in ALL_FOUR:
         os.symlink(os.path.join(self.config.config_dir, kind + "17.pem"),
                    getattr(self.test_rc, kind))
     self.assertFalse(self.test_rc._consistent())
     unlink_all(self.test_rc)
     # Items must point to things that exist
     for kind in ALL_FOUR:
         os.symlink(os.path.join("..", "..", "archive", "example.org",
                                 kind + "17.pem"),
                    getattr(self.test_rc, kind))
     self.assertFalse(self.test_rc._consistent())
     # This version should work
     fill_with_sample_data(self.test_rc)
     self.assertTrue(self.test_rc._consistent())
     # Items must point to things that follow the naming convention
     os.unlink(self.test_rc.fullchain)
     os.symlink(os.path.join("..", "..", "archive", "example.org",
                             "fullchain_17.pem"), self.test_rc.fullchain)
     with open(self.test_rc.fullchain, "w") as f:
         f.write("wrongly-named fullchain")
     self.assertFalse(self.test_rc._consistent())
Beispiel #35
0
    def save_successor(self, prior_version, new_cert,
                       new_privkey, new_chain, cli_config):
        """Save new cert and chain as a successor of a prior version.

        Returns the new version number that was created.

        .. note:: this function does NOT update links to deploy this
                  version

        :param int prior_version: the old version to which this version
            is regarded as a successor (used to choose a privkey, if the
            key has not changed, but otherwise this information is not
            permanently recorded anywhere)
        :param bytes new_cert: the new certificate, in PEM format
        :param bytes new_privkey: the new private key, in PEM format,
            or ``None``, if the private key has not changed
        :param bytes new_chain: the new chain, in PEM format
        :param .NamespaceConfig cli_config: parsed command line
            arguments

        :returns: the new version number that was created
        :rtype: int

        """
        # XXX: assumes official archive location rather than examining links
        # XXX: consider using os.open for availability of os.O_EXCL
        # XXX: ensure file permissions are correct; also create directories
        #      if needed (ensuring their permissions are correct)
        # Figure out what the new version is and hence where to save things

        self.cli_config = cli_config
        target_version = self.next_free_version()
        target = dict(
            [(kind,
              os.path.join(self.archive_dir, "{0}{1}.pem".format(kind, target_version)))
             for kind in ALL_FOUR])

        old_privkey = os.path.join(
            self.archive_dir, "privkey{0}.pem".format(prior_version))

        # Distinguish the cases where the privkey has changed and where it
        # has not changed (in the latter case, making an appropriate symlink
        # to an earlier privkey version)
        if new_privkey is None:
            # The behavior below keeps the prior key by creating a new
            # symlink to the old key or the target of the old key symlink.
            if os.path.islink(old_privkey):
                old_privkey = os.readlink(old_privkey)
            else:
                old_privkey = "privkey{0}.pem".format(prior_version)
            logger.debug("Writing symlink to old private key, %s.", old_privkey)
            os.symlink(old_privkey, target["privkey"])
        else:
            with util.safe_open(target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f:
                logger.debug("Writing new private key to %s.", target["privkey"])
                f.write(new_privkey)
            # Preserve gid and (mode & 074) from previous privkey in this lineage.
            old_mode = stat.S_IMODE(os.stat(old_privkey).st_mode) & \
                (stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | \
                 stat.S_IROTH)
            mode = BASE_PRIVKEY_MODE | old_mode
            os.chown(target["privkey"], -1, os.stat(old_privkey).st_gid)
            os.chmod(target["privkey"], mode)

        # Save everything else
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(new_cert)
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(new_chain)
        with open(target["fullchain"], "wb") as f:
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(new_cert + new_chain)

        symlinks = dict((kind, self.configuration[kind]) for kind in ALL_FOUR)
        # Update renewal config file
        self.configfile = update_configuration(
            self.lineagename, self.archive_dir, symlinks, cli_config)
        self.configuration = config_with_defaults(self.configfile)

        return target_version
Beispiel #36
0
    def new_lineage(cls, lineagename, cert, privkey, chain, cli_config):
        # pylint: disable=too-many-locals
        """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 .NamespaceConfig cli_config: parsed command line
            arguments

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

        """

        # Examine the configuration and find the new lineage's name
        for i in (cli_config.renewal_configs_dir, cli_config.default_archive_dir,
                  cli_config.live_dir):
            if not os.path.exists(i):
                os.makedirs(i, 0o700)
                logger.debug("Creating directory %s.", i)
        config_file, config_filename = util.unique_lineage_name(
            cli_config.renewal_configs_dir, lineagename)
        base_readme_path = os.path.join(cli_config.live_dir, README)
        if not os.path.exists(base_readme_path):
            _write_live_readme_to(base_readme_path, is_base_dir=True)

        # Determine where on disk everything will go
        # lineagename will now potentially be modified based on which
        # renewal configuration file could actually be created
        lineagename = lineagename_for_filename(config_filename)
        archive = full_archive_path(None, cli_config, lineagename)
        live_dir = _full_live_path(cli_config, lineagename)
        if os.path.exists(archive):
            config_file.close()
            raise errors.CertStorageError(
                "archive directory exists for " + lineagename)
        if os.path.exists(live_dir):
            config_file.close()
            raise errors.CertStorageError(
                "live directory exists for " + lineagename)
        os.mkdir(archive)
        os.mkdir(live_dir)
        logger.debug("Archive directory %s and live "
                     "directory %s created.", archive, live_dir)

        # Put the data into the appropriate files on disk
        target = dict([(kind, os.path.join(live_dir, kind + ".pem"))
                       for kind in ALL_FOUR])
        archive_target = dict([(kind, os.path.join(archive, kind + "1.pem"))
                               for kind in ALL_FOUR])
        for kind in ALL_FOUR:
            os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind])
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(cert)
        with util.safe_open(archive_target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f:
            logger.debug("Writing private key to %s.", target["privkey"])
            f.write(privkey)
            # XXX: Let's make sure to get the file permissions right here
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(chain)
        with open(target["fullchain"], "wb") as f:
            # assumes that OpenSSL.crypto.dump_certificate includes
            # ending newline character
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(cert + chain)

        # Write a README file to the live directory
        readme_path = os.path.join(live_dir, README)
        _write_live_readme_to(readme_path)

        # Document what we've done in a new renewal config file
        config_file.close()

        # Save only the config items that are relevant to renewal
        values = relevant_values(vars(cli_config.namespace))

        new_config = write_renewal_config(config_filename, config_filename, archive,
            target, values)
        return cls(new_config.filename, cli_config)
Beispiel #37
0
 def _symlink_to_account_dir(self, prev_server_path, server_path, account_id):
     prev_account_dir = self._account_dir_path_for_server_path(account_id, prev_server_path)
     new_account_dir = self._account_dir_path_for_server_path(account_id, server_path)
     os.symlink(prev_account_dir, new_account_dir)
Beispiel #38
0
    def new_lineage(cls, lineagename, cert, privkey, chain, cli_config):
        """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 .NamespaceConfig cli_config: parsed command line
            arguments

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

        """

        # Examine the configuration and find the new lineage's name
        for i in (cli_config.renewal_configs_dir,
                  cli_config.default_archive_dir, cli_config.live_dir):
            if not os.path.exists(i):
                filesystem.makedirs(i, 0o700)
                logger.debug("Creating directory %s.", i)
        config_file, config_filename = util.unique_lineage_name(
            cli_config.renewal_configs_dir, lineagename)
        base_readme_path = os.path.join(cli_config.live_dir, README)
        if not os.path.exists(base_readme_path):
            _write_live_readme_to(base_readme_path, is_base_dir=True)

        # Determine where on disk everything will go
        # lineagename will now potentially be modified based on which
        # renewal configuration file could actually be created
        lineagename = lineagename_for_filename(config_filename)
        archive = full_archive_path(None, cli_config, lineagename)
        live_dir = _full_live_path(cli_config, lineagename)
        if os.path.exists(archive) and (not os.path.isdir(archive)
                                        or os.listdir(archive)):
            config_file.close()
            raise errors.CertStorageError("archive directory exists for " +
                                          lineagename)
        if os.path.exists(live_dir) and (not os.path.isdir(live_dir)
                                         or os.listdir(live_dir)):
            config_file.close()
            raise errors.CertStorageError("live directory exists for " +
                                          lineagename)
        for i in (archive, live_dir):
            if not os.path.exists(i):
                filesystem.makedirs(i)
                logger.debug("Creating directory %s.", i)

        # Put the data into the appropriate files on disk
        target = {
            kind: os.path.join(live_dir, kind + ".pem")
            for kind in ALL_FOUR
        }
        archive_target = {
            kind: os.path.join(archive, kind + "1.pem")
            for kind in ALL_FOUR
        }
        for kind in ALL_FOUR:
            os.symlink(_relpath_from_file(archive_target[kind], target[kind]),
                       target[kind])
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(cert)
        with util.safe_open(archive_target["privkey"],
                            "wb",
                            chmod=BASE_PRIVKEY_MODE) as f:
            logger.debug("Writing private key to %s.", target["privkey"])
            f.write(privkey)
            # XXX: Let's make sure to get the file permissions right here
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(chain)
        with open(target["fullchain"], "wb") as f:
            # assumes that OpenSSL.crypto.dump_certificate includes
            # ending newline character
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(cert + chain)

        # Write a README file to the live directory
        readme_path = os.path.join(live_dir, README)
        _write_live_readme_to(readme_path)

        # Document what we've done in a new renewal config file
        config_file.close()

        # Save only the config items that are relevant to renewal
        values = relevant_values(vars(cli_config.namespace))

        new_config = write_renewal_config(config_filename, config_filename,
                                          archive, target, values)
        return cls(new_config.filename, cli_config)
Beispiel #39
0
    def save_successor(self, prior_version, new_cert, new_privkey, new_chain,
                       cli_config):
        """Save new cert and chain as a successor of a prior version.

        Returns the new version number that was created.

        .. note:: this function does NOT update links to deploy this
                  version

        :param int prior_version: the old version to which this version
            is regarded as a successor (used to choose a privkey, if the
            key has not changed, but otherwise this information is not
            permanently recorded anywhere)
        :param bytes new_cert: the new certificate, in PEM format
        :param bytes new_privkey: the new private key, in PEM format,
            or ``None``, if the private key has not changed
        :param bytes new_chain: the new chain, in PEM format
        :param .NamespaceConfig cli_config: parsed command line
            arguments

        :returns: the new version number that was created
        :rtype: int

        """
        # XXX: assumes official archive location rather than examining links
        # XXX: consider using os.open for availability of os.O_EXCL
        # XXX: ensure file permissions are correct; also create directories
        #      if needed (ensuring their permissions are correct)
        # Figure out what the new version is and hence where to save things

        self.cli_config = cli_config
        target_version = self.next_free_version()
        target = {
            kind: os.path.join(self.archive_dir,
                               "{0}{1}.pem".format(kind, target_version))
            for kind in ALL_FOUR
        }

        old_privkey = os.path.join(self.archive_dir,
                                   "privkey{0}.pem".format(prior_version))

        # Distinguish the cases where the privkey has changed and where it
        # has not changed (in the latter case, making an appropriate symlink
        # to an earlier privkey version)
        if new_privkey is None:
            # The behavior below keeps the prior key by creating a new
            # symlink to the old key or the target of the old key symlink.
            if os.path.islink(old_privkey):
                old_privkey = filesystem.readlink(old_privkey)
            else:
                old_privkey = "privkey{0}.pem".format(prior_version)
            logger.debug("Writing symlink to old private key, %s.",
                         old_privkey)
            os.symlink(old_privkey, target["privkey"])
        else:
            with util.safe_open(target["privkey"],
                                "wb",
                                chmod=BASE_PRIVKEY_MODE) as f:
                logger.debug("Writing new private key to %s.",
                             target["privkey"])
                f.write(new_privkey)
            # Preserve gid and (mode & MASK_FOR_PRIVATE_KEY_PERMISSIONS)
            # from previous privkey in this lineage.
            mode = filesystem.compute_private_key_mode(old_privkey,
                                                       BASE_PRIVKEY_MODE)
            filesystem.copy_ownership_and_apply_mode(old_privkey,
                                                     target["privkey"],
                                                     mode,
                                                     copy_user=False,
                                                     copy_group=True)

        # Save everything else
        with open(target["cert"], "wb") as f:
            logger.debug("Writing certificate to %s.", target["cert"])
            f.write(new_cert)
        with open(target["chain"], "wb") as f:
            logger.debug("Writing chain to %s.", target["chain"])
            f.write(new_chain)
        with open(target["fullchain"], "wb") as f:
            logger.debug("Writing full chain to %s.", target["fullchain"])
            f.write(new_cert + new_chain)

        symlinks = {kind: self.configuration[kind] for kind in ALL_FOUR}
        # Update renewal config file
        self.configfile = update_configuration(self.lineagename,
                                               self.archive_dir, symlinks,
                                               cli_config)
        self.configuration = config_with_defaults(self.configfile)

        return target_version