diff --git a/authentik/outposts/controllers/docker.py b/authentik/outposts/controllers/docker.py index ed10dcb7f..af4c08651 100644 --- a/authentik/outposts/controllers/docker.py +++ b/authentik/outposts/controllers/docker.py @@ -15,7 +15,7 @@ from yaml import safe_dump from authentik import __version__ from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException -from authentik.outposts.docker_ssh import DockerInlineSSH +from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException from authentik.outposts.docker_tls import DockerInlineTLS from authentik.outposts.managed import MANAGED_OUTPOST from authentik.outposts.models import ( @@ -35,6 +35,7 @@ class DockerClient(UpstreamDockerClient, BaseClient): def __init__(self, connection: DockerServiceConnection): self.tls = None self.ssh = None + self.logger = get_logger() if connection.local: # Same result as DockerClient.from_env super().__init__(**kwargs_from_env()) @@ -42,8 +43,12 @@ class DockerClient(UpstreamDockerClient, BaseClient): parsed_url = urlparse(connection.url) tls_config = False if parsed_url.scheme == "ssh": - self.ssh = DockerInlineSSH(parsed_url.hostname, connection.tls_authentication) - self.ssh.write() + try: + self.ssh = DockerInlineSSH(parsed_url.hostname, connection.tls_authentication) + self.ssh.write() + except SSHManagedExternallyException as exc: + # SSH config is managed externally + self.logger.info(f"SSH Managed externally: {exc}") else: self.tls = DockerInlineTLS( verification_kp=connection.tls_verification, @@ -57,7 +62,6 @@ class DockerClient(UpstreamDockerClient, BaseClient): ) except SSHException as exc: raise ServiceConnectionInvalid from exc - self.logger = get_logger() # Ensure the client actually works self.containers.list() diff --git a/authentik/outposts/docker_ssh.py b/authentik/outposts/docker_ssh.py index 929f55e34..657e38a76 100644 --- a/authentik/outposts/docker_ssh.py +++ b/authentik/outposts/docker_ssh.py @@ -16,6 +16,10 @@ def opener(path, flags): return os.open(path, flags, 0o700) +class SSHManagedExternallyException(DockerException): + """Raised when the ssh config file is managed externally.""" + + class DockerInlineSSH: """Create paramiko ssh config from CertificateKeyPair""" @@ -29,9 +33,15 @@ class DockerInlineSSH: def __init__(self, host: str, keypair: CertificateKeyPair) -> None: self.host = host self.keypair = keypair + self.config_path = Path("~/.ssh/config").expanduser() + if self.config_path.exists() and HEADER not in self.config_path.read_text(encoding="utf-8"): + # SSH Config file already exists and there's no header from us, meaning that it's + # been externally mapped into the container for more complex configs + raise SSHManagedExternallyException( + "SSH Config exists and does not contain authentik header" + ) if not self.keypair: raise DockerException("keypair must be set for SSH connections") - self.config_path = Path("~/.ssh/config").expanduser() self.header = f"{HEADER} - {self.host}\n" def write_config(self, key_path: str) -> bool: