Compare commits

...
This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.

10 Commits

Author SHA1 Message Date
Jens Langhammer 4e9a466d64
re-add paramiko
This reverts commit eb6f515ee0e9ed5196565c79eea9dd5b5cdc7444.

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer 9bd8cfbac0
fix folder perms
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer e18c2fe084
fix web issue
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer 205f11532f
fix build
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer bc6d66cd88
use open
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer 609e9a00b4
what a pointless warning
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer d5708d22e0
fix error handling
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer 71ac1282f9
cleanup
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer cf9d8f64a2
simplify config, adjust perms in dockerfile
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
Jens Langhammer 1cda01511b
outposts: use native ssh client
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-04-24 18:10:30 +03:00
4 changed files with 27 additions and 40 deletions

View File

@ -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 /

View File

@ -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

View File

@ -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,70 +27,54 @@ 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:
# SSH Config file already exists and there's no header from us, meaning that it's if not _config.writable():
# been externally mapped into the container for more complex configs # SSH Config file already exists and there's no header from us, meaning that it's
raise SSHManagedExternallyException( # been externally mapped into the container for more complex configs
"SSH Config exists and does not contain authentik header" raise SSHManagedExternallyException(
) "SSH Config exists and does not contain authentik header"
)
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

View File

@ -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>