core: FIPS (#9683)

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
Jens L
2024-05-23 19:34:52 +02:00
committed by GitHub
parent c2da6822dc
commit c3445374c2
24 changed files with 222 additions and 57 deletions

View File

@ -38,7 +38,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
RUN npm run build RUN npm run build
# Stage 3: Build go proxy # Stage 3: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22.3-bookworm AS go-builder FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS go-builder
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
@ -49,6 +49,11 @@ ARG GOARCH=$TARGETARCH
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends crossbuild-essential-arm64 gcc-aarch64-linux-gnu
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \ --mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
@ -63,11 +68,11 @@ COPY ./internal /go/src/goauthentik.io/internal
COPY ./go.mod /go/src/goauthentik.io/go.mod COPY ./go.mod /go/src/goauthentik.io/go.mod
COPY ./go.sum /go/src/goauthentik.io/go.sum COPY ./go.sum /go/src/goauthentik.io/go.sum
ENV CGO_ENABLED=0
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \ --mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
GOARM="${TARGETVARIANT#v}" go build -o /go/authentik ./cmd/server if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
go build -o /go/authentik ./cmd/server
# Stage 4: MaxMind GeoIP # Stage 4: MaxMind GeoIP
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0.1 as geoip FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0.1 as geoip
@ -84,7 +89,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0" /bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Python dependencies # Stage 5: Python dependencies
FROM docker.io/python:3.12.3-slim-bookworm AS python-deps FROM ghcr.io/goauthentik/fips-python:3.12.3-slim-bookworm-fips-full AS python-deps
WORKDIR /ak-root/poetry WORKDIR /ak-root/poetry
@ -97,7 +102,7 @@ RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloa
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \ RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
apt-get update && \ apt-get update && \
# Required for installing pip packages # Required for installing pip packages
apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev zlib1g-dev libpq-dev apt-get install -y --no-install-recommends build-essential pkg-config libpq-dev
RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \ RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
--mount=type=bind,target=./poetry.lock,src=./poetry.lock \ --mount=type=bind,target=./poetry.lock,src=./poetry.lock \
@ -107,10 +112,11 @@ RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
bash -c "source ${VENV_PATH}/bin/activate && \ bash -c "source ${VENV_PATH}/bin/activate && \
pip3 install --upgrade pip && \ pip3 install --upgrade pip && \
pip3 install poetry && \ pip3 install poetry && \
poetry install --only=main --no-ansi --no-interaction --no-root" poetry install --only=main --no-ansi --no-interaction --no-root && \
pip install --force-reinstall /wheels/*"
# Stage 6: Run # Stage 6: Run
FROM docker.io/python:3.12.3-slim-bookworm AS final-image FROM ghcr.io/goauthentik/fips-python:3.12.3-slim-bookworm-fips-full AS final-image
ARG GIT_BUILD_HASH ARG GIT_BUILD_HASH
ARG VERSION ARG VERSION
@ -127,7 +133,7 @@ WORKDIR /
# We cannot cache this layer otherwise we'll end up with a bigger image # We cannot cache this layer otherwise we'll end up with a bigger image
RUN apt-get update && \ RUN apt-get update && \
# Required for runtime # Required for runtime
apt-get install -y --no-install-recommends libpq5 openssl libxmlsec1-openssl libmaxminddb0 ca-certificates && \ apt-get install -y --no-install-recommends libpq5 libmaxminddb0 ca-certificates && \
# Required for bootstrap & healtcheck # Required for bootstrap & healtcheck
apt-get install -y --no-install-recommends runit && \ apt-get install -y --no-install-recommends runit && \
apt-get clean && \ apt-get clean && \
@ -163,6 +169,8 @@ ENV TMPDIR=/dev/shm/ \
VENV_PATH="/ak-root/venv" \ VENV_PATH="/ak-root/venv" \
POETRY_VIRTUALENVS_CREATE=false POETRY_VIRTUALENVS_CREATE=false
ENV GOFIPS=1
HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "ak", "healthcheck" ] HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "ak", "healthcheck" ]
ENTRYPOINT [ "dumb-init", "--", "ak" ] ENTRYPOINT [ "dumb-init", "--", "ak" ]

View File

@ -2,17 +2,19 @@
import platform import platform
from datetime import datetime from datetime import datetime
from ssl import OPENSSL_VERSION
from sys import version as python_version from sys import version as python_version
from typing import TypedDict from typing import TypedDict
from cryptography.hazmat.backends.openssl.backend import backend
from django.utils.timezone import now from django.utils.timezone import now
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from gunicorn import version_info as gunicorn_version
from rest_framework.fields import SerializerMethodField from rest_framework.fields import SerializerMethodField
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from authentik import get_full_version
from authentik.core.api.utils import PassiveSerializer from authentik.core.api.utils import PassiveSerializer
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_env from authentik.lib.utils.reflection import get_env
@ -25,11 +27,13 @@ class RuntimeDict(TypedDict):
"""Runtime information""" """Runtime information"""
python_version: str python_version: str
gunicorn_version: str
environment: str environment: str
architecture: str architecture: str
platform: str platform: str
uname: str uname: str
openssl_version: str
openssl_fips_mode: bool
authentik_version: str
class SystemInfoSerializer(PassiveSerializer): class SystemInfoSerializer(PassiveSerializer):
@ -64,11 +68,13 @@ class SystemInfoSerializer(PassiveSerializer):
def get_runtime(self, request: Request) -> RuntimeDict: def get_runtime(self, request: Request) -> RuntimeDict:
"""Get versions""" """Get versions"""
return { return {
"python_version": python_version,
"gunicorn_version": ".".join(str(x) for x in gunicorn_version),
"environment": get_env(),
"architecture": platform.machine(), "architecture": platform.machine(),
"authentik_version": get_full_version(),
"environment": get_env(),
"openssl_fips_enabled": backend._fips_enabled,
"openssl_version": OPENSSL_VERSION,
"platform": platform.platform(), "platform": platform.platform(),
"python_version": python_version,
"uname": " ".join(platform.uname()), "uname": " ".join(platform.uname()),
} }

View File

@ -42,8 +42,8 @@ class TestUsersAvatars(APITestCase):
with Mocker() as mocker: with Mocker() as mocker:
mocker.head( mocker.head(
( (
"https://secure.gravatar.com/avatar/84730f9c1851d1ea03f1a" "https://www.gravatar.com/avatar/76eb3c74c8beb6faa037f1b6e2ecb3e252bdac"
"a9ed85bd1ea?size=158&rating=g&default=404" "6cf71fb567ae36025a9d4ea86b?size=158&rating=g&default=404"
), ),
text="foo", text="foo",
) )

View File

@ -92,7 +92,11 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
@property @property
def kid(self): def kid(self):
"""Get Key ID used for JWKS""" """Get Key ID used for JWKS"""
return md5(self.key_data.encode("utf-8")).hexdigest() if self.key_data else "" # nosec return (
md5(self.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
if self.key_data
else ""
) # nosec
def __str__(self) -> str: def __str__(self) -> str:
return f"Certificate-Key Pair {self.name}" return f"Certificate-Key Pair {self.name}"

View File

@ -2,7 +2,7 @@
from base64 import b64encode from base64 import b64encode
from functools import cache as funccache from functools import cache as funccache
from hashlib import md5 from hashlib import md5, sha256
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from urllib.parse import urlencode from urllib.parse import urlencode
@ -20,7 +20,7 @@ from authentik.tenants.utils import get_current_tenant
if TYPE_CHECKING: if TYPE_CHECKING:
from authentik.core.models import User from authentik.core.models import User
GRAVATAR_URL = "https://secure.gravatar.com" GRAVATAR_URL = "https://www.gravatar.com"
DEFAULT_AVATAR = static("dist/assets/images/user_default.png") DEFAULT_AVATAR = static("dist/assets/images/user_default.png")
CACHE_KEY_GRAVATAR = "goauthentik.io/lib/avatars/" CACHE_KEY_GRAVATAR = "goauthentik.io/lib/avatars/"
CACHE_KEY_GRAVATAR_AVAILABLE = "goauthentik.io/lib/avatars/gravatar_available" CACHE_KEY_GRAVATAR_AVAILABLE = "goauthentik.io/lib/avatars/gravatar_available"
@ -55,10 +55,9 @@ def avatar_mode_gravatar(user: "User", mode: str) -> str | None:
if not cache.get(CACHE_KEY_GRAVATAR_AVAILABLE, True): if not cache.get(CACHE_KEY_GRAVATAR_AVAILABLE, True):
return None return None
# gravatar uses md5 for their URLs, so md5 can't be avoided mail_hash = sha256(user.email.lower().encode("utf-8")).hexdigest() # nosec
mail_hash = md5(user.email.lower().encode("utf-8")).hexdigest() # nosec parameters = {"size": "158", "rating": "g", "default": "404"}
parameters = [("size", "158"), ("rating", "g"), ("default", "404")] gravatar_url = f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters)}"
gravatar_url = f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters, doseq=True)}"
full_key = CACHE_KEY_GRAVATAR + mail_hash full_key = CACHE_KEY_GRAVATAR + mail_hash
if cache.has_key(full_key): if cache.has_key(full_key):
@ -84,7 +83,9 @@ def avatar_mode_gravatar(user: "User", mode: str) -> str | None:
def generate_colors(text: str) -> tuple[str, str]: def generate_colors(text: str) -> tuple[str, str]:
"""Generate colours based on `text`""" """Generate colours based on `text`"""
color = int(md5(text.lower().encode("utf-8")).hexdigest(), 16) % 0xFFFFFF # nosec color = (
int(md5(text.lower().encode("utf-8"), usedforsecurity=False).hexdigest(), 16) % 0xFFFFFF
) # nosec
# Get a (somewhat arbitrarily) reduced scope of colors # Get a (somewhat arbitrarily) reduced scope of colors
# to avoid too dark or light backgrounds # to avoid too dark or light backgrounds
@ -179,7 +180,7 @@ def avatar_mode_generated(user: "User", mode: str) -> str | None:
def avatar_mode_url(user: "User", mode: str) -> str | None: def avatar_mode_url(user: "User", mode: str) -> str | None:
"""Format url""" """Format url"""
mail_hash = md5(user.email.lower().encode("utf-8")).hexdigest() # nosec mail_hash = md5(user.email.lower().encode("utf-8"), usedforsecurity=False).hexdigest() # nosec
return mode % { return mode % {
"username": user.username, "username": user.username,
"mail_hash": mail_hash, "mail_hash": mail_hash,

View File

@ -117,8 +117,12 @@ class OutpostHealthSerializer(PassiveSerializer):
uid = CharField(read_only=True) uid = CharField(read_only=True)
last_seen = DateTimeField(read_only=True) last_seen = DateTimeField(read_only=True)
version = CharField(read_only=True) version = CharField(read_only=True)
version_should = CharField(read_only=True) golang_version = CharField(read_only=True)
openssl_enabled = BooleanField(read_only=True)
openssl_version = CharField(read_only=True)
fips_enabled = BooleanField(read_only=True)
version_should = CharField(read_only=True)
version_outdated = BooleanField(read_only=True) version_outdated = BooleanField(read_only=True)
build_hash = CharField(read_only=True, required=False) build_hash = CharField(read_only=True, required=False)
@ -173,6 +177,10 @@ class OutpostViewSet(UsedByMixin, ModelViewSet):
"version_should": state.version_should, "version_should": state.version_should,
"version_outdated": state.version_outdated, "version_outdated": state.version_outdated,
"build_hash": state.build_hash, "build_hash": state.build_hash,
"golang_version": state.golang_version,
"openssl_enabled": state.openssl_enabled,
"openssl_version": state.openssl_version,
"fips_enabled": state.fips_enabled,
"hostname": state.hostname, "hostname": state.hostname,
"build_hash_should": get_build_hash(), "build_hash_should": get_build_hash(),
} }

View File

@ -121,6 +121,10 @@ class OutpostConsumer(JsonWebsocketConsumer):
if msg.instruction == WebsocketMessageInstruction.HELLO: if msg.instruction == WebsocketMessageInstruction.HELLO:
state.version = msg.args.pop("version", None) state.version = msg.args.pop("version", None)
state.build_hash = msg.args.pop("buildHash", "") state.build_hash = msg.args.pop("buildHash", "")
state.golang_version = msg.args.pop("golangVersion", "")
state.openssl_enabled = msg.args.pop("opensslEnabled", False)
state.openssl_version = msg.args.pop("opensslVersion", "")
state.fips_enabled = msg.args.pop("fipsEnabled", False)
state.args.update(msg.args) state.args.update(msg.args)
elif msg.instruction == WebsocketMessageInstruction.ACK: elif msg.instruction == WebsocketMessageInstruction.ACK:
return return

View File

@ -131,7 +131,7 @@ class OutpostServiceConnection(models.Model):
verbose_name = _("Outpost Service-Connection") verbose_name = _("Outpost Service-Connection")
verbose_name_plural = _("Outpost Service-Connections") verbose_name_plural = _("Outpost Service-Connections")
def __str__(self) -> __version__: def __str__(self) -> str:
return f"Outpost service connection {self.name}" return f"Outpost service connection {self.name}"
@property @property
@ -434,6 +434,10 @@ class OutpostState:
version: str | None = field(default=None) version: str | None = field(default=None)
version_should: Version = field(default=OUR_VERSION) version_should: Version = field(default=OUR_VERSION)
build_hash: str = field(default="") build_hash: str = field(default="")
golang_version: str = field(default="")
openssl_enabled: bool = field(default=False)
openssl_version: str = field(default="")
fips_enabled: bool = field(default=False)
hostname: str = field(default="") hostname: str = field(default="")
args: dict = field(default_factory=dict) args: dict = field(default_factory=dict)

View File

@ -1,6 +1,8 @@
from os import environ from os import environ
from ssl import OPENSSL_VERSION
import pytest import pytest
from cryptography.hazmat.backends.openssl.backend import backend
from authentik import get_full_version from authentik import get_full_version
@ -18,4 +20,7 @@ def pytest_sessionstart(*_, **__):
@pytest.hookimpl(trylast=True) @pytest.hookimpl(trylast=True)
def pytest_report_header(*_, **__): def pytest_report_header(*_, **__):
"""Add authentik version to pytest output""" """Add authentik version to pytest output"""
return [f"authentik version: {get_full_version()}"] return [
f"authentik version: {get_full_version()}",
f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}",
]

View File

@ -0,0 +1,5 @@
//go:build requirefips
package backend
var FipsEnabled = true

View File

@ -0,0 +1,5 @@
//go:build !requirefips
package backend
var FipsEnabled = false

View File

@ -0,0 +1,5 @@
//go:build !goexperiment.systemcrypto
package backend
var OpensslEnabled = false

View File

@ -0,0 +1,5 @@
//go:build goexperiment.systemcrypto
package backend
var OpensslEnabled = true

View File

@ -0,0 +1,17 @@
package backend
import (
"bytes"
"os/exec"
)
func OpensslVersion() string {
cmd := exec.Command("openssl", "version")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return ""
}
return out.String()
}

View File

@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"runtime"
"syscall" "syscall"
"time" "time"
@ -15,11 +16,12 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"goauthentik.io/api/v3" "goauthentik.io/api/v3"
"goauthentik.io/internal/constants" "goauthentik.io/internal/constants"
cryptobackend "goauthentik.io/internal/crypto/backend"
"goauthentik.io/internal/utils/web" "goauthentik.io/internal/utils/web"
log "github.com/sirupsen/logrus"
) )
type WSHandler func(ctx context.Context, args map[string]interface{}) type WSHandler func(ctx context.Context, args map[string]interface{})
@ -187,6 +189,10 @@ func (a *APIController) getWebsocketPingArgs() map[string]interface{} {
"version": constants.VERSION, "version": constants.VERSION,
"buildHash": constants.BUILD("tagged"), "buildHash": constants.BUILD("tagged"),
"uuid": a.instanceUUID.String(), "uuid": a.instanceUUID.String(),
"golangVersion": runtime.Version(),
"opensslEnabled": cryptobackend.OpensslEnabled,
"opensslVersion": cryptobackend.OpensslVersion(),
"fipsEnabled": cryptobackend.FipsEnabled,
} }
hostname, err := os.Hostname() hostname, err := os.Hostname()
if err == nil { if err == nil {

View File

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Stage 1: Build # Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22.3-bookworm AS builder FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS builder
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
@ -12,20 +12,26 @@ ARG GOARCH=$TARGETARCH
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends crossbuild-essential-arm64 gcc-aarch64-linux-gnu
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \ --mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \ --mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
go mod download go mod download
ENV CGO_ENABLED=0
COPY . . COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \ --mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
GOARM="${TARGETVARIANT#v}" go build -o /go/ldap ./cmd/ldap if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
go build -o /go/ldap ./cmd/ldap
# Stage 2: Run # Stage 2: Run
FROM gcr.io/distroless/static-debian11:debug FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
ARG GIT_BUILD_HASH ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
@ -44,4 +50,6 @@ EXPOSE 3389 6636 9300
USER 1000 USER 1000
ENV GOFIPS=1
ENTRYPOINT ["/ldap"] ENTRYPOINT ["/ldap"]

View File

@ -84,6 +84,7 @@ elif [[ "$1" == "bash" ]]; then
elif [[ "$1" == "test-all" ]]; then elif [[ "$1" == "test-all" ]]; then
prepare_debug prepare_debug
chmod 777 /root chmod 777 /root
pip install --force-reinstall /wheels/*
check_if_root "python -m manage test authentik" check_if_root "python -m manage test authentik"
elif [[ "$1" == "healthcheck" ]]; then elif [[ "$1" == "healthcheck" ]]; then
run_authentik healthcheck $(cat $MODE_FILE) run_authentik healthcheck $(cat $MODE_FILE)

View File

@ -7,6 +7,9 @@ from pathlib import Path
from tempfile import gettempdir from tempfile import gettempdir
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from cryptography.exceptions import InternalError
from cryptography.hazmat.backends.openssl.backend import backend
from defusedxml import defuse_stdlib
from prometheus_client.values import MultiProcessValue from prometheus_client.values import MultiProcessValue
from authentik import get_full_version from authentik import get_full_version
@ -25,6 +28,13 @@ if TYPE_CHECKING:
from authentik.root.asgi import AuthentikAsgi from authentik.root.asgi import AuthentikAsgi
defuse_stdlib()
try:
backend._enable_fips()
except InternalError:
pass
wait_for_db() wait_for_db()
_tmp = Path(gettempdir()) _tmp = Path(gettempdir())

View File

@ -4,6 +4,8 @@ import os
import sys import sys
import warnings import warnings
from cryptography.exceptions import InternalError
from cryptography.hazmat.backends.openssl.backend import backend
from defusedxml import defuse_stdlib from defusedxml import defuse_stdlib
from django.utils.autoreload import DJANGO_AUTORELOAD_ENV from django.utils.autoreload import DJANGO_AUTORELOAD_ENV
@ -22,6 +24,12 @@ warnings.filterwarnings(
defuse_stdlib() defuse_stdlib()
try:
backend._enable_fips()
except InternalError:
pass
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
wait_for_db() wait_for_db()

View File

@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy RUN npm run build-proxy
# Stage 2: Build # Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22.3-bookworm AS builder FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS builder
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
@ -28,20 +28,26 @@ ARG GOARCH=$TARGETARCH
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends crossbuild-essential-arm64 gcc-aarch64-linux-gnu
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \ --mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \ --mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
go mod download go mod download
ENV CGO_ENABLED=0
COPY . . COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \ --mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
GOARM="${TARGETVARIANT#v}" go build -o /go/proxy ./cmd/proxy if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
go build -o /go/proxy ./cmd/proxy
# Stage 3: Run # Stage 3: Run
FROM gcr.io/distroless/static-debian11:debug FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
ARG GIT_BUILD_HASH ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
@ -64,4 +70,6 @@ EXPOSE 9000 9300 9443
USER 1000 USER 1000
ENV GOFIPS=1
ENTRYPOINT ["/proxy"] ENTRYPOINT ["/proxy"]

View File

@ -1,24 +1,37 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Stage 1: Build # Stage 1: Build
FROM docker.io/golang:1.22.3-bookworm AS builder FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
ARG GOOS=$TARGETOS
ARG GOARCH=$TARGETARCH
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends crossbuild-essential-arm64 gcc-aarch64-linux-gnu
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \ --mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \ --mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
go mod download go mod download
ENV CGO_ENABLED=0
COPY . . COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \ --mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
go build -o /go/rac ./cmd/rac go build -o /go/rac ./cmd/rac
# Stage 2: Run # Stage 2: Run
FROM ghcr.io/beryju/guacd:1.5.5 FROM ghcr.io/beryju/guacd:1.5.5-fips
ARG GIT_BUILD_HASH ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
@ -35,4 +48,6 @@ HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/rac", "healthch
USER 1000 USER 1000
ENV GOFIPS=1
ENTRYPOINT ["/rac"] ENTRYPOINT ["/rac"]

View File

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Stage 1: Build # Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22.3-bookworm AS builder FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS builder
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
@ -12,20 +12,26 @@ ARG GOARCH=$TARGETARCH
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends crossbuild-essential-arm64 gcc-aarch64-linux-gnu
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \ --mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \ --mount=type=bind,target=/go/src/goauthentik.io/gen-go-api,src=./gen-go-api \
--mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/go/pkg/mod \
go mod download go mod download
ENV CGO_ENABLED=0
COPY . . COPY . .
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \ --mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
GOARM="${TARGETVARIANT#v}" go build -o /go/radius ./cmd/radius if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
go build -o /go/radius ./cmd/radius
# Stage 2: Run # Stage 2: Run
FROM gcr.io/distroless/static-debian11:debug FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
ARG GIT_BUILD_HASH ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
@ -44,4 +50,6 @@ EXPOSE 1812/udp 9300
USER 1000 USER 1000
ENV GOFIPS=1
ENTRYPOINT ["/radius"] ENTRYPOINT ["/radius"]

View File

@ -39368,6 +39368,18 @@ components:
version: version:
type: string type: string
readOnly: true readOnly: true
golang_version:
type: string
readOnly: true
openssl_enabled:
type: boolean
readOnly: true
openssl_version:
type: string
readOnly: true
fips_enabled:
type: boolean
readOnly: true
version_should: version_should:
type: string type: string
readOnly: true readOnly: true
@ -39386,8 +39398,12 @@ components:
required: required:
- build_hash - build_hash
- build_hash_should - build_hash_should
- fips_enabled
- golang_version
- hostname - hostname
- last_seen - last_seen
- openssl_enabled
- openssl_version
- uid - uid
- version - version
- version_outdated - version_outdated
@ -47106,8 +47122,6 @@ components:
properties: properties:
python_version: python_version:
type: string type: string
gunicorn_version:
type: string
environment: environment:
type: string type: string
architecture: architecture:
@ -47116,10 +47130,18 @@ components:
type: string type: string
uname: uname:
type: string type: string
openssl_version:
type: string
openssl_fips_mode:
type: boolean
authentik_version:
type: string
required: required:
- architecture - architecture
- authentik_version
- environment - environment
- gunicorn_version - openssl_fips_mode
- openssl_version
- platform - platform
- python_version - python_version
- uname - uname

View File

@ -34,10 +34,12 @@ export class OutpostHealthElement extends AKElement {
} }
let versionString = this.outpostHealth.version; let versionString = this.outpostHealth.version;
if (this.outpostHealth.buildHash) { if (this.outpostHealth.buildHash) {
versionString = `${versionString} (build ${this.outpostHealth.buildHash.substring( versionString = msg(
0, str`${versionString} (build ${this.outpostHealth.buildHash.substring(0, 8)})`,
8, );
)})`; }
if (this.outpostHealth.fipsEnabled) {
versionString = msg(str`${versionString} (FIPS)`);
} }
return html`<dl class="pf-c-description-list pf-m-compact"> return html`<dl class="pf-c-description-list pf-m-compact">
<div class="pf-c-description-list__group"> <div class="pf-c-description-list__group">