refactor: configuration agnostic healthcheck (#2231)

This makes the healthcheck simple and configured directly by Authelia's configuration on startup.
pull/2239/head^2
James Elliott 2021-08-05 14:02:07 +10:00 committed by GitHub
parent cbedf79f86
commit c5c6bda8b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 113 additions and 26 deletions

View File

@ -21,3 +21,6 @@ examples
internal/server/public_html
authelia.service
bootstrap.sh
# Overrides
!.healthcheck.env

5
.healthcheck.env 100644
View File

@ -0,0 +1,5 @@
# Default Template
X_AUTHELIA_HEALTHCHECK_SCHEME=http
X_AUTHELIA_HEALTHCHECK_HOST=localhost
X_AUTHELIA_HEALTHCHECK_PORT=9091
X_AUTHELIA_HEALTHCHECK_PATH=

View File

@ -17,6 +17,7 @@ ARG LDFLAGS_EXTRA
RUN \
mv public_html internal/server/public_html && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia
@ -30,7 +31,7 @@ WORKDIR /app
RUN apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh ./
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091

View File

@ -17,6 +17,7 @@ ARG LDFLAGS_EXTRA
RUN \
mv public_html internal/server/public_html && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \
GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia
@ -31,7 +32,7 @@ WORKDIR /app
RUN \
apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh ./
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091

View File

@ -17,6 +17,7 @@ ARG LDFLAGS_EXTRA
RUN \
mv public_html internal/server/public_html && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia
@ -31,7 +32,7 @@ WORKDIR /app
RUN \
apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh ./
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091

View File

@ -32,6 +32,7 @@ ARG LDFLAGS_EXTRA
RUN \
mv api internal/server/public_html/api && \
cd cmd/authelia && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build (coverage via go test)..." && \
CGO_ENABLED=0 go test -c --tags coverage -covermode=atomic \
-ldflags "${LDFLAGS_EXTRA}" -o authelia -coverpkg github.com/authelia/authelia/...
@ -45,7 +46,7 @@ RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder-backend /go/src/app/cmd/authelia/authelia /go/src/app/LICENSE /go/src/app/healthcheck.sh ./
COPY --from=builder-backend /go/src/app/cmd/authelia/authelia /go/src/app/LICENSE /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091

View File

@ -53,6 +53,10 @@ server:
## Enables the expvars endpoint.
enable_expvars: false
## Disables writing the health check vars to /app/.healthcheck.env which makes healthcheck.sh return exit code 0.
## This is disabled by default if either /app/.healthcheck.env or /app/healthcheck.sh do not exist.
disable_healthcheck: false
## Authelia by default doesn't accept TLS communication on the server port. This section overrides this behaviour.
tls:
## The path to the DER base64/PEM format private key.

View File

@ -20,6 +20,7 @@ server:
write_buffer_size: 4096
enable_pprof: false
enable_expvars: false
disable_healthcheck: false
tls:
key: ""
certificate: ""
@ -134,6 +135,23 @@ required: no
Enables the go expvars endpoints.
### disable_healthcheck
<div markdown="1">
type: boolean
{: .label .label-config .label-purple }
default: false
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
On startup Authelia checks for the existence of /app/healthcheck.sh and /app/.healthcheck.env and if both of these exist
it writes the configuration vars for the healthcheck to the /app/.healthcheck.env file. In instances where this is not
desirable it's possible to disable these interactions entirely.
An example situation where this is the case is in Kubernetes when set security policies that prevent writing to the
ephemeral storage of a container or just don't want to enable the internal health check.
### tls
Authelia typically listens for plain unencrypted connections. This is by design as most environments allow to

View File

@ -1,23 +1,21 @@
#!/bin/sh
AUTHELIA_CONFIG=$(pgrep -af authelia | awk '{print $NF}')
AUTHELIA_SCHEME=$(grep ^tls "${AUTHELIA_CONFIG}")
AUTHELIA_HOST=$(grep ^host "${AUTHELIA_CONFIG}" | sed -e 's/host: //' -e 's/\r//')
AUTHELIA_PORT=$(grep ^port "${AUTHELIA_CONFIG}" | sed -e 's/port: //' -e 's/\r//')
AUTHELIA_PATH=$(grep ^\ \ path "${AUTHELIA_CONFIG}" | sed -e 's/ path: //' -e 's/\r//' -e 's/^/\//')
if [ -z "${AUTHELIA_SCHEME}" ]; then
AUTHELIA_SCHEME=http
else
AUTHELIA_SCHEME=https
if [ -z "${X_AUTHELIA_HEALTHCHECK}" ]; then
exit 0
fi
if [ -z "${AUTHELIA_HOST}" ] || [ "${AUTHELIA_HOST}" = "0.0.0.0" ]; then
AUTHELIA_HOST=localhost
source /app/.healthcheck.env
if [ -z "${X_AUTHELIA_HEALTHCHECK_SCHEME}" ]; then
X_AUTHELIA_HEALTHCHECK_SCHEME=http
fi
if [ -z "${AUTHELIA_PORT}" ]; then
AUTHELIA_PORT=9091
if [ -z "${X_AUTHELIA_HEALTHCHECK_HOST}" ]; then
X_AUTHELIA_HEALTHCHECK_HOST=localhost
fi
wget --quiet --no-check-certificate --tries=1 --spider "${AUTHELIA_SCHEME}://${AUTHELIA_HOST}:${AUTHELIA_PORT}${AUTHELIA_PATH}/api/health" || exit 1
if [ -z "${X_AUTHELIA_HEALTHCHECK_PORT}" ]; then
X_AUTHELIA_HEALTHCHECK_PORT=9091
fi
wget --quiet --no-check-certificate --tries=1 --spider "${X_AUTHELIA_HEALTHCHECK_SCHEME}://${X_AUTHELIA_HEALTHCHECK_HOST}:${X_AUTHELIA_HEALTHCHECK_PORT}${X_AUTHELIA_HEALTHCHECK_PATH}/api/health" || exit 1

View File

@ -53,6 +53,10 @@ server:
## Enables the expvars endpoint.
enable_expvars: false
## Disables writing the health check vars to /app/.healthcheck.env which makes healthcheck.sh return exit code 0.
## This is disabled by default if either /app/.healthcheck.env or /app/healthcheck.sh do not exist.
disable_healthcheck: false
## Authelia by default doesn't accept TLS communication on the server port. This section overrides this behaviour.
tls:
## The path to the DER base64/PEM format private key.

View File

@ -9,6 +9,7 @@ type ServerConfiguration struct {
WriteBufferSize int `koanf:"write_buffer_size"`
EnablePprof bool `koanf:"enable_endpoint_pprof"`
EnableExpvars bool `koanf:"enable_endpoint_expvars"`
DisableHealthcheck bool `koanf:"disable_healthcheck"`
TLS ServerTLSConfiguration `koanf:"tls"`
}

View File

@ -139,6 +139,7 @@ var ValidKeys = []string{
"server.path",
"server.enable_pprof",
"server.enable_expvars",
"server.disable_healthcheck",
"server.tls.key",
"server.tls.certificate",

View File

@ -6,3 +6,11 @@ const apiFile = "openapi.yml"
const indexFile = "index.html"
const dev = "dev"
const healthCheckEnv = `# Written by Authelia Process
X_AUTHELIA_HEALTHCHECK=1
X_AUTHELIA_HEALTHCHECK_SCHEME=%s
X_AUTHELIA_HEALTHCHECK_HOST=%s
X_AUTHELIA_HEALTHCHECK_PORT=%d
X_AUTHELIA_HEALTHCHECK_PATH=%s
`

View File

@ -192,9 +192,17 @@ func Start(configuration schema.Configuration, providers middlewares.Providers)
}
if configuration.Server.TLS.Certificate != "" && configuration.Server.TLS.Key != "" {
if err = writeHealthCheckEnv(configuration.Server.DisableHealthcheck, "https", configuration.Server.Host, configuration.Server.Path, configuration.Server.Port); err != nil {
logger.Fatalf("Could not configure healthcheck: %v", err)
}
logger.Infof("Listening for TLS connections on %s%s", addrPattern, configuration.Server.Path)
logger.Fatal(server.ServeTLS(listener, configuration.Server.TLS.Certificate, configuration.Server.TLS.Key))
} else {
if err = writeHealthCheckEnv(configuration.Server.DisableHealthcheck, "http", configuration.Server.Host, configuration.Server.Path, configuration.Server.Port); err != nil {
logger.Fatalf("Could not configure healthcheck: %v", err)
}
logger.Infof("Listening for non-TLS connections on %s%s", addrPattern, configuration.Server.Path)
logger.Fatal(server.Serve(listener))
}

View File

@ -64,3 +64,36 @@ func ServeTemplatedFile(publicDir, file, base, rememberMe, resetPassword, sessio
}
}
}
func writeHealthCheckEnv(disabled bool, scheme, host, path string, port int) (err error) {
if disabled {
return nil
}
_, err = os.Stat("/app/healthcheck.sh")
if err != nil {
return nil
}
_, err = os.Stat("/app/.healthcheck.env")
if err != nil {
return nil
}
file, err := os.OpenFile("/app/.healthcheck.env", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return err
}
defer func() {
_ = file.Close()
}()
if host == "0.0.0.0" {
host = "localhost"
}
_, err = file.WriteString(fmt.Sprintf(healthCheckEnv, scheme, host, port, path))
return err
}