authentik.outposts.docker_tls

Create Docker TLSConfig from CertificateKeyPair

 1"""Create Docker TLSConfig from CertificateKeyPair"""
 2
 3from os import unlink
 4from pathlib import Path
 5from tempfile import gettempdir
 6
 7from docker.tls import TLSConfig
 8
 9from authentik.crypto.models import CertificateKeyPair
10from authentik.outposts.docker_ssh import opener
11
12
13class DockerInlineTLS:
14    """Create Docker TLSConfig from CertificateKeyPair"""
15
16    verification_kp: CertificateKeyPair | None
17    authentication_kp: CertificateKeyPair | None
18
19    _paths: list[str]
20
21    def __init__(
22        self,
23        verification_kp: CertificateKeyPair | None,
24        authentication_kp: CertificateKeyPair | None,
25    ) -> None:
26        self.verification_kp = verification_kp
27        self.authentication_kp = authentication_kp
28        self._paths = []
29
30    def __enter__(self):
31        return self.write()
32
33    def __exit__(self, exc_type, exc, tb):
34        self.cleanup()
35
36    def write_file(self, name: str, contents: str) -> str:
37        """Wrapper for mkstemp that uses fdopen"""
38        path = Path(gettempdir(), name)
39        with open(path, "w", encoding="utf8", opener=opener) as _file:
40            _file.write(contents)
41        self._paths.append(str(path))
42        return str(path)
43
44    def cleanup(self):
45        """Clean up certificates when we're done"""
46        for path in self._paths:
47            unlink(path)
48
49    def write(self) -> TLSConfig:
50        """Create TLSConfig with Certificate Key pairs"""
51        # So yes, this is quite ugly. But sadly, there is no clean way to pass
52        # docker-py (which is using requests (which is using urllib3)) a certificate
53        # for verification or authentication as string.
54        # Because we run in docker, and our tmpfs is isolated to us, we can just
55        # write out the certificates and keys to files and use their paths
56        config_args = {}
57        if self.verification_kp:
58            ca_cert_path = self.write_file(
59                f"{self.verification_kp.pk.hex}-cert.pem",
60                self.verification_kp.certificate_data,
61            )
62            config_args["ca_cert"] = ca_cert_path
63        if self.authentication_kp:
64            auth_cert_path = self.write_file(
65                f"{self.authentication_kp.pk.hex}-cert.pem",
66                self.authentication_kp.certificate_data,
67            )
68            auth_key_path = self.write_file(
69                f"{self.authentication_kp.pk.hex}-key.pem",
70                self.authentication_kp.key_data,
71            )
72            config_args["client_cert"] = (auth_cert_path, auth_key_path)
73        return TLSConfig(**config_args)
class DockerInlineTLS:
14class DockerInlineTLS:
15    """Create Docker TLSConfig from CertificateKeyPair"""
16
17    verification_kp: CertificateKeyPair | None
18    authentication_kp: CertificateKeyPair | None
19
20    _paths: list[str]
21
22    def __init__(
23        self,
24        verification_kp: CertificateKeyPair | None,
25        authentication_kp: CertificateKeyPair | None,
26    ) -> None:
27        self.verification_kp = verification_kp
28        self.authentication_kp = authentication_kp
29        self._paths = []
30
31    def __enter__(self):
32        return self.write()
33
34    def __exit__(self, exc_type, exc, tb):
35        self.cleanup()
36
37    def write_file(self, name: str, contents: str) -> str:
38        """Wrapper for mkstemp that uses fdopen"""
39        path = Path(gettempdir(), name)
40        with open(path, "w", encoding="utf8", opener=opener) as _file:
41            _file.write(contents)
42        self._paths.append(str(path))
43        return str(path)
44
45    def cleanup(self):
46        """Clean up certificates when we're done"""
47        for path in self._paths:
48            unlink(path)
49
50    def write(self) -> TLSConfig:
51        """Create TLSConfig with Certificate Key pairs"""
52        # So yes, this is quite ugly. But sadly, there is no clean way to pass
53        # docker-py (which is using requests (which is using urllib3)) a certificate
54        # for verification or authentication as string.
55        # Because we run in docker, and our tmpfs is isolated to us, we can just
56        # write out the certificates and keys to files and use their paths
57        config_args = {}
58        if self.verification_kp:
59            ca_cert_path = self.write_file(
60                f"{self.verification_kp.pk.hex}-cert.pem",
61                self.verification_kp.certificate_data,
62            )
63            config_args["ca_cert"] = ca_cert_path
64        if self.authentication_kp:
65            auth_cert_path = self.write_file(
66                f"{self.authentication_kp.pk.hex}-cert.pem",
67                self.authentication_kp.certificate_data,
68            )
69            auth_key_path = self.write_file(
70                f"{self.authentication_kp.pk.hex}-key.pem",
71                self.authentication_kp.key_data,
72            )
73            config_args["client_cert"] = (auth_cert_path, auth_key_path)
74        return TLSConfig(**config_args)

Create Docker TLSConfig from CertificateKeyPair

DockerInlineTLS( verification_kp: authentik.crypto.models.CertificateKeyPair | None, authentication_kp: authentik.crypto.models.CertificateKeyPair | None)
22    def __init__(
23        self,
24        verification_kp: CertificateKeyPair | None,
25        authentication_kp: CertificateKeyPair | None,
26    ) -> None:
27        self.verification_kp = verification_kp
28        self.authentication_kp = authentication_kp
29        self._paths = []
authentication_kp: authentik.crypto.models.CertificateKeyPair | None
def write_file(self, name: str, contents: str) -> str:
37    def write_file(self, name: str, contents: str) -> str:
38        """Wrapper for mkstemp that uses fdopen"""
39        path = Path(gettempdir(), name)
40        with open(path, "w", encoding="utf8", opener=opener) as _file:
41            _file.write(contents)
42        self._paths.append(str(path))
43        return str(path)

Wrapper for mkstemp that uses fdopen

def cleanup(self):
45    def cleanup(self):
46        """Clean up certificates when we're done"""
47        for path in self._paths:
48            unlink(path)

Clean up certificates when we're done

def write(self) -> docker.tls.TLSConfig:
50    def write(self) -> TLSConfig:
51        """Create TLSConfig with Certificate Key pairs"""
52        # So yes, this is quite ugly. But sadly, there is no clean way to pass
53        # docker-py (which is using requests (which is using urllib3)) a certificate
54        # for verification or authentication as string.
55        # Because we run in docker, and our tmpfs is isolated to us, we can just
56        # write out the certificates and keys to files and use their paths
57        config_args = {}
58        if self.verification_kp:
59            ca_cert_path = self.write_file(
60                f"{self.verification_kp.pk.hex}-cert.pem",
61                self.verification_kp.certificate_data,
62            )
63            config_args["ca_cert"] = ca_cert_path
64        if self.authentication_kp:
65            auth_cert_path = self.write_file(
66                f"{self.authentication_kp.pk.hex}-cert.pem",
67                self.authentication_kp.certificate_data,
68            )
69            auth_key_path = self.write_file(
70                f"{self.authentication_kp.pk.hex}-key.pem",
71                self.authentication_kp.key_data,
72            )
73            config_args["client_cert"] = (auth_cert_path, auth_key_path)
74        return TLSConfig(**config_args)

Create TLSConfig with Certificate Key pairs