Compare commits
	
		
			10 Commits
		
	
	
		
			version/20
			...
			outposts/f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4e9a466d64 | |||
| 9bd8cfbac0 | |||
| e18c2fe084 | |||
| 205f11532f | |||
| bc6d66cd88 | |||
| 609e9a00b4 | |||
| d5708d22e0 | |||
| 71ac1282f9 | |||
| cf9d8f64a2 | |||
| 1cda01511b | 
| @ -84,6 +84,8 @@ RUN apt-get update && \ | |||||||
|     apt-get install -y --no-install-recommends libxmlsec1-openssl libmaxminddb0 && \ |     apt-get install -y --no-install-recommends libxmlsec1-openssl libmaxminddb0 && \ | ||||||
|     # Required for bootstrap & healtcheck |     # Required for bootstrap & healtcheck | ||||||
|     apt-get install -y --no-install-recommends runit && \ |     apt-get install -y --no-install-recommends runit && \ | ||||||
|  |     # Required for outposts | ||||||
|  |     apt-get install -y --no-install-recommends openssh-client && \ | ||||||
|     pip install --no-cache-dir -r /requirements.txt && \ |     pip install --no-cache-dir -r /requirements.txt && \ | ||||||
|     apt-get remove --purge -y build-essential pkg-config libxmlsec1-dev && \ |     apt-get remove --purge -y build-essential pkg-config libxmlsec1-dev && \ | ||||||
|     apt-get autoremove --purge -y && \ |     apt-get autoremove --purge -y && \ | ||||||
| @ -91,8 +93,9 @@ RUN apt-get update && \ | |||||||
|     rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \ |     rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \ | ||||||
|     adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \ |     adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \ | ||||||
|     mkdir -p /certs /media /blueprints && \ |     mkdir -p /certs /media /blueprints && \ | ||||||
|     mkdir -p /authentik/.ssh && \ |     chown authentik:authentik /certs /media && \ | ||||||
|     chown authentik:authentik /certs /media /authentik/.ssh |     chmod g+w /etc/ssh/ssh_config.d/ && \ | ||||||
|  |     chgrp authentik /etc/ssh/ssh_config.d/ | ||||||
|  |  | ||||||
| COPY ./authentik/ /authentik | COPY ./authentik/ /authentik | ||||||
| COPY ./pyproject.toml / | COPY ./pyproject.toml / | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| """Docker controller""" | """Docker controller""" | ||||||
|  | from subprocess import SubprocessError  # nosec | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Optional | from typing import Optional | ||||||
| from urllib.parse import urlparse | from urllib.parse import urlparse | ||||||
| @ -9,7 +10,6 @@ from docker import DockerClient as UpstreamDockerClient | |||||||
| from docker.errors import DockerException, NotFound | from docker.errors import DockerException, NotFound | ||||||
| from docker.models.containers import Container | from docker.models.containers import Container | ||||||
| from docker.utils.utils import kwargs_from_env | from docker.utils.utils import kwargs_from_env | ||||||
| from paramiko.ssh_exception import SSHException |  | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
| from yaml import safe_dump | from yaml import safe_dump | ||||||
|  |  | ||||||
| @ -58,8 +58,9 @@ class DockerClient(UpstreamDockerClient, BaseClient): | |||||||
|                 super().__init__( |                 super().__init__( | ||||||
|                     base_url=connection.url, |                     base_url=connection.url, | ||||||
|                     tls=tls_config, |                     tls=tls_config, | ||||||
|  |                     use_ssh_client=True, | ||||||
|                 ) |                 ) | ||||||
|             except SSHException as exc: |             except SubprocessError as exc: | ||||||
|                 if self.ssh: |                 if self.ssh: | ||||||
|                     self.ssh.cleanup() |                     self.ssh.cleanup() | ||||||
|                 raise ServiceConnectionInvalid(exc) from exc |                 raise ServiceConnectionInvalid(exc) from exc | ||||||
|  | |||||||
| @ -7,8 +7,7 @@ from docker.errors import DockerException | |||||||
|  |  | ||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
|  |  | ||||||
| HEADER = "### Managed by authentik" | SSH_CONFIG_DIR = Path("/etc/ssh/ssh_config.d/") | ||||||
| FOOTER = "### End Managed by authentik" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def opener(path, flags): | def opener(path, flags): | ||||||
| @ -28,13 +27,13 @@ class DockerInlineSSH: | |||||||
|  |  | ||||||
|     key_path: str |     key_path: str | ||||||
|     config_path: Path |     config_path: Path | ||||||
|     header: str |  | ||||||
|  |  | ||||||
|     def __init__(self, host: str, keypair: CertificateKeyPair) -> None: |     def __init__(self, host: str, keypair: CertificateKeyPair) -> None: | ||||||
|         self.host = host |         self.host = host | ||||||
|         self.keypair = keypair |         self.keypair = keypair | ||||||
|         self.config_path = Path("~/.ssh/config").expanduser() |         self.config_path = SSH_CONFIG_DIR / Path(self.host + ".conf") | ||||||
|         if self.config_path.exists() and HEADER not in self.config_path.read_text(encoding="utf-8"): |         with open(self.config_path, "w", encoding="utf-8") as _config: | ||||||
|  |             if not _config.writable(): | ||||||
|                 # SSH Config file already exists and there's no header from us, meaning that it's |                 # 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 |                 # been externally mapped into the container for more complex configs | ||||||
|                 raise SSHManagedExternallyException( |                 raise SSHManagedExternallyException( | ||||||
| @ -42,56 +41,40 @@ class DockerInlineSSH: | |||||||
|                 ) |                 ) | ||||||
|         if not self.keypair: |         if not self.keypair: | ||||||
|             raise DockerException("keypair must be set for SSH connections") |             raise DockerException("keypair must be set for SSH connections") | ||||||
|         self.header = f"{HEADER} - {self.host}\n" |  | ||||||
|  |  | ||||||
|     def write_config(self, key_path: str) -> bool: |     def write_config(self, key_path: str): | ||||||
|         """Update the local user's ssh config file""" |         """Update the local user's ssh config file""" | ||||||
|         with open(self.config_path, "a+", encoding="utf-8") as ssh_config: |         with open(self.config_path, "w", encoding="utf-8") as ssh_config: | ||||||
|             if self.header in ssh_config.readlines(): |  | ||||||
|                 return False |  | ||||||
|             ssh_config.writelines( |             ssh_config.writelines( | ||||||
|                 [ |                 [ | ||||||
|                     self.header, |  | ||||||
|                     f"Host {self.host}\n", |                     f"Host {self.host}\n", | ||||||
|                     f"    IdentityFile {key_path}\n", |                     f"    IdentityFile {str(key_path)}\n", | ||||||
|                     "    StrictHostKeyChecking No\n", |                     "    StrictHostKeyChecking No\n", | ||||||
|                     "    UserKnownHostsFile /dev/null\n", |                     "    UserKnownHostsFile /dev/null\n", | ||||||
|                     f"{FOOTER}\n", |  | ||||||
|                     "\n", |                     "\n", | ||||||
|                 ] |                 ] | ||||||
|             ) |             ) | ||||||
|         return True |  | ||||||
|  |  | ||||||
|     def write_key(self): |     def write_key(self) -> Path: | ||||||
|         """Write keypair's private key to a temporary file""" |         """Write keypair's private key to a temporary file""" | ||||||
|         path = Path(gettempdir(), f"{self.keypair.pk}_private.pem") |         path = Path(gettempdir(), f"{self.keypair.pk}_private.pem") | ||||||
|         with open(path, "w", encoding="utf8", opener=opener) as _file: |         with open(path, "w", encoding="utf8", opener=opener) as _file: | ||||||
|             _file.write(self.keypair.key_data) |             _file.write(self.keypair.key_data) | ||||||
|         return str(path) |         return path | ||||||
|  |  | ||||||
|     def write(self): |     def write(self): | ||||||
|         """Write keyfile and update ssh config""" |         """Write keyfile and update ssh config""" | ||||||
|         self.key_path = self.write_key() |         self.key_path = self.write_key() | ||||||
|         was_written = self.write_config(self.key_path) |         try: | ||||||
|         if not was_written: |             self.write_config(self.key_path) | ||||||
|  |         except OSError: | ||||||
|             self.cleanup() |             self.cleanup() | ||||||
|  |  | ||||||
|     def cleanup(self): |     def cleanup(self): | ||||||
|         """Cleanup when we're done""" |         """Cleanup when we're done""" | ||||||
|         try: |         try: | ||||||
|             os.unlink(self.key_path) |             os.unlink(self.key_path) | ||||||
|             with open(self.config_path, "r", encoding="utf-8") as ssh_config: |             os.unlink(self.config_path) | ||||||
|                 start = 0 |  | ||||||
|                 end = 0 |  | ||||||
|                 lines = ssh_config.readlines() |  | ||||||
|                 for idx, line in enumerate(lines): |  | ||||||
|                     if line == self.header: |  | ||||||
|                         start = idx |  | ||||||
|                     if start != 0 and line == f"{FOOTER}\n": |  | ||||||
|                         end = idx |  | ||||||
|             with open(self.config_path, "w+", encoding="utf-8") as ssh_config: |  | ||||||
|                 lines = lines[:start] + lines[end + 2 :] |  | ||||||
|                 ssh_config.writelines(lines) |  | ||||||
|         except OSError: |         except OSError: | ||||||
|             # If we fail deleting a file it doesn't matter that much |             # If we fail deleting a file it doesn't matter that much | ||||||
|             # since we're just in a container |             # since we're just in a container | ||||||
|  | |||||||
| @ -85,7 +85,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio | |||||||
|             html`<ak-label color=${item.local ? PFColor.Grey : PFColor.Green}> |             html`<ak-label color=${item.local ? PFColor.Grey : PFColor.Green}> | ||||||
|                 ${item.local ? t`Yes` : t`No`} |                 ${item.local ? t`Yes` : t`No`} | ||||||
|             </ak-label>`, |             </ak-label>`, | ||||||
|             html`${itemState.healthy |             html`${itemState?.healthy | ||||||
|                 ? html`<ak-label color=${PFColor.Green}>${ifDefined(itemState.version)}</ak-label>` |                 ? html`<ak-label color=${PFColor.Green}>${ifDefined(itemState.version)}</ak-label>` | ||||||
|                 : html`<ak-label color=${PFColor.Red}>${t`Unhealthy`}</ak-label>`}`, |                 : html`<ak-label color=${PFColor.Red}>${t`Unhealthy`}</ak-label>`}`, | ||||||
|             html` <ak-forms-modal> |             html` <ak-forms-modal> | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	