def test_get_secret_fingerprint(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        assert SecretUtils.get_secret_fingerprint(a) == "AA:BB"
        assert SecretUtils.get_secret_fingerprint(b) == "AA:CC"
        assert SecretUtils.get_secret_fingerprint(c) == "AA:DD"
        assert SecretUtils.get_secret_fingerprint(d) is None
    def test_get_secret_name(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        assert SecretUtils.get_secret_name(a) == "cert"
        assert SecretUtils.get_secret_name(b) == "chain"
        assert SecretUtils.get_secret_name(c) == "chain"
        assert SecretUtils.get_secret_name(d) is None
    def test_get_secret_domain(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        assert SecretUtils.get_secret_version(a) == "0"
        assert SecretUtils.get_secret_version(b) == "1"
        assert SecretUtils.get_secret_version(c) == "2"
        assert SecretUtils.get_secret_version(d) is None
    def test_get_secret_managed(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        assert SecretUtils.get_secret_managed(a) is False
        assert SecretUtils.get_secret_managed(b) is True
        assert SecretUtils.get_secret_managed(c) is True
        assert SecretUtils.get_secret_managed(d) is False
    def test_get_secret_domain(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        assert SecretUtils.get_secret_domain(a) == "1.example.com"
        assert SecretUtils.get_secret_domain(b) == "2.example.com"
        assert SecretUtils.get_secret_domain(c) == "2.example.com"
        assert SecretUtils.get_secret_domain(d) is None
    def get_secrets(self, domain, name, reverse=False):
        # type: (str, str) -> List[Secret]
        """Get all secrets of a specific type for a domain.

        The resulting list of secrets is sorted based on secret
        versions from lowest to highest.

        :param str domain: Secret domain.
        :param str name: Secret name.
        :param bool reverse: Sort the Secrets in reverse order.

        :return: A list of secrets.
        :rtype: List[Secret]
        """

        f = {}
        f[SecretUtils.L_MANAGED] = lambda x: x == "true"
        f[SecretUtils.L_DOMAIN] = lambda x: x == domain
        f[SecretUtils.L_NAME] = lambda x: x == name
        f[SecretUtils.L_VERSION] = lambda x: x is not None
        f[SecretUtils.L_FINGERPRINT] = lambda x: x is not None

        s = self.docker_client.secrets.list()
        s = SecretUtils.filter_secrets(s, f)
        s = SecretUtils.sort_secrets(
            s,
            SecretUtils.L_VERSION,
            reverse
        )

        return s
    def test_get_x509_fingerprint(self):
        fingerprint = ("D7:5C:60:9E:BE:8F:78:67:1D:0E:16:98:80:96:3A:B5:"
                       "FF:88:A7:94:19:75:6D:11:A0:3E:1F:33:21:90:54:7F")

        assert SecretUtils.get_x509_fingerprint(
            os.path.join(ASSET_PATH, "cert.pem")
        ) == fingerprint
    def test_sort_secrets_different_default(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        res = SecretUtils.sort_secrets(
            [a, b, c, d],
            SecretUtils.L_VERSION,
            reverse=True,
            default="100"
        )
        assert res == [a, b, c, d]

        res = SecretUtils.sort_secrets(
            [a, b, c, d],
            SecretUtils.L_VERSION,
            reverse=True,
            default="-1"
        )
        assert res == [d, a, b, c]
    def get_all_names(self):
        # type: () -> List[str]
        """Get all domain names that have at least one existing secret.

        :rtype: Set[str]
        """

        f = {}
        f[SecretUtils.L_MANAGED] = lambda x: x == "true"
        f[SecretUtils.L_DOMAIN] = lambda x: x is not None
        f[SecretUtils.L_NAME] = lambda x: x is not None
        f[SecretUtils.L_VERSION] = lambda x: x is not None
        f[SecretUtils.L_FINGERPRINT] = lambda x: x is not None

        s = self.docker_client.secrets.list()
        s = SecretUtils.filter_secrets(s, f)

        return {SecretUtils.get_secret_domain(x) for x in s}
    def test_sort_secrets(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        res = SecretUtils.sort_secrets(
            [a, b, c],
            SecretUtils.L_VERSION,
            reverse=False,
            default=None
        )
        assert res == [a, b, c]
    def test_sort_secrets_reverse(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        # Case 1
        res = SecretUtils.sort_secrets(
            [a, b, c],
            SecretUtils.L_VERSION,
            reverse=True,
            default=None
        )
        assert res == [c, b, a]
    def test_filter_secrets(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        f = {}
        filtered = SecretUtils.filter_secrets([a, b, c, d], f)
        assert filtered == []

        f[SecretUtils.L_MANAGED] = lambda x: x == "true"
        f[SecretUtils.L_DOMAIN] = lambda x: x == "2.example.com"
        filtered = SecretUtils.filter_secrets([a, b, c, d], f)
        assert filtered == [b, c]

        f[SecretUtils.L_MANAGED] = lambda x: x == "false"
        f[SecretUtils.L_DOMAIN] = lambda x: x == "1.example.com"
        f[SecretUtils.L_NAME] = lambda x: x == "cert"
        filtered = SecretUtils.filter_secrets([a, b, c, d], f)
        assert filtered == [a]

        f[SecretUtils.L_NAME] = lambda x: x == "fullchain"
        filtered = SecretUtils.filter_secrets([a, b, c, d], f)
        assert filtered == []
    def deploy_cert(  # pylint: disable=too-many-arguments
        self,
        domain,
        cert_path,
        key_path,
        chain_path,
        fullchain_path
    ):
        # type: (str, str, str, str, str) -> None
        """Create Docker Swarm Secrets from certificates.

        :param str domain: Certificate domain.
        :param str cert_path: Path to the certificate file.
        :param str key_path: Path to the private key file.
        :param str chain_path: Path to the certificate chain file.
        :param str fullchain_path: Path to the fullchain file.
        """

        fp = SecretUtils.get_x509_fingerprint(cert_path)

        cert = None
        key = None
        chain = None
        fc = None

        # Create new secrets.
        if not self.is_secret_deployed(domain, "cert", fp):
            cert = self.secret_from_file(domain, "cert", cert_path, fp)
        if not self.is_secret_deployed(domain, "key", fp):
            key = self.secret_from_file(domain, "key", key_path, fp)
        if not self.is_secret_deployed(domain, "chain", fp):
            chain = self.secret_from_file(domain, "chain", chain_path, fp)
        if not self.is_secret_deployed(domain, "fullchain", fp):
            fc = self.secret_from_file(domain, "fullchain", fullchain_path, fp)

        if not cert or not key or not chain or not fc:
            logger.info("Some secrets already deployed. They were skipped.")

        if cert is not None:
            self.secret_spec.update_refs(cert)
        if key is not None:
            self.secret_spec.update_refs(key)
        if chain is not None:
            self.secret_spec.update_refs(chain)
        if fc is not None:
            self.secret_spec.update_refs(fc)
Beispiel #14
0
    def get_updated_ref(self, ref, candidate):
        # type: (SecretReference, Secret) -> None
        """Attempt to renew a SecretReference with a Secret.

        :param SecretReference ref: The old SecretReference.
        :param Secret candidate: The new Secret candidate.

        :return: A new SecretReference or 'old' if renewal was not possible.
        :rtype: SecretReference
        """

        old = self.docker_client.secrets.get(ref.get("SecretID"))
        if SecretUtils.secret_renews(old, candidate):
            logger.info("--> Update %s: %s -> %s",
                        ref.get("File").get("Name"), old.name, candidate.name)
            return SecretReference(candidate.id, candidate.name,
                                   ref.get("File").get("Name"),
                                   ref.get("File").get("UID"),
                                   ref.get("File").get("GID"),
                                   ref.get("File").get("Mode"))

        return ref
    def is_secret_deployed(self, domain, name, fingerprint):
        # type: (str, str, str) -> bool
        """Check whether a secret is already deployed based on fingerprints.

        :param domain str: The domain the secret authenticates.
        :param name str: The name of the secret.
        :param fingerprint str: The fingerprint of the *certificate*
                                corresponding to this secret.

        :return: True if deployed, False otherwise.
        :rtype: bool
        """

        existing_secrets = self.get_secrets(domain, name)

        if len(existing_secrets) != 0:
            newest = existing_secrets[-1]
            newest_fp = SecretUtils.get_secret_fingerprint(newest)

            if newest_fp == fingerprint:
                # Skip deployment if the secret has already been deployed.
                return True

        return False
    def test_secret_renews(self, secrets):
        a, b, c, d = [secrets[x] for x in sorted(secrets)]

        assert SecretUtils.secret_renews(a, b) is False
        assert SecretUtils.secret_renews(a, c) is False
        assert SecretUtils.secret_renews(a, d) is False
        assert SecretUtils.secret_renews(b, a) is False
        assert SecretUtils.secret_renews(b, c) is True
        assert SecretUtils.secret_renews(b, d) is False
        assert SecretUtils.secret_renews(c, a) is False
        assert SecretUtils.secret_renews(c, b) is False
        assert SecretUtils.secret_renews(c, d) is False
        assert SecretUtils.secret_renews(d, a) is False
        assert SecretUtils.secret_renews(d, b) is False
        assert SecretUtils.secret_renews(d, c) is False