refactor: configuration agnostic healthcheck (#2231)

This makes the healthcheck simple and configured directly by Authelia's configuration on startup.
This commit is contained in:
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 internal/server/public_html
authelia.service authelia.service
bootstrap.sh bootstrap.sh
# Overrides
!.healthcheck.env

5
.healthcheck.env Normal file
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 \ RUN \
mv public_html internal/server/public_html && \ mv public_html internal/server/public_html && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \ echo ">> Starting go build..." && \
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -tags netgo \ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia -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 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 EXPOSE 9091

View File

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

View File

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

View File

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

View File

@ -53,6 +53,10 @@ server:
## Enables the expvars endpoint. ## Enables the expvars endpoint.
enable_expvars: false 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. ## Authelia by default doesn't accept TLS communication on the server port. This section overrides this behaviour.
tls: tls:
## The path to the DER base64/PEM format private key. ## The path to the DER base64/PEM format private key.

View File

@ -20,6 +20,7 @@ server:
write_buffer_size: 4096 write_buffer_size: 4096
enable_pprof: false enable_pprof: false
enable_expvars: false enable_expvars: false
disable_healthcheck: false
tls: tls:
key: "" key: ""
certificate: "" certificate: ""
@ -134,6 +135,23 @@ required: no
Enables the go expvars endpoints. 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 ### tls
Authelia typically listens for plain unencrypted connections. This is by design as most environments allow to Authelia typically listens for plain unencrypted connections. This is by design as most environments allow to

View File

@ -1,23 +1,21 @@
#!/bin/sh #!/bin/sh
AUTHELIA_CONFIG=$(pgrep -af authelia | awk '{print $NF}') if [ -z "${X_AUTHELIA_HEALTHCHECK}" ]; then
AUTHELIA_SCHEME=$(grep ^tls "${AUTHELIA_CONFIG}") exit 0
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
fi fi
if [ -z "${AUTHELIA_HOST}" ] || [ "${AUTHELIA_HOST}" = "0.0.0.0" ]; then source /app/.healthcheck.env
AUTHELIA_HOST=localhost
if [ -z "${X_AUTHELIA_HEALTHCHECK_SCHEME}" ]; then
X_AUTHELIA_HEALTHCHECK_SCHEME=http
fi fi
if [ -z "${AUTHELIA_PORT}" ]; then if [ -z "${X_AUTHELIA_HEALTHCHECK_HOST}" ]; then
AUTHELIA_PORT=9091 X_AUTHELIA_HEALTHCHECK_HOST=localhost
fi 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. ## Enables the expvars endpoint.
enable_expvars: false 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. ## Authelia by default doesn't accept TLS communication on the server port. This section overrides this behaviour.
tls: tls:
## The path to the DER base64/PEM format private key. ## The path to the DER base64/PEM format private key.

View File

@ -2,13 +2,14 @@ package schema
// ServerConfiguration represents the configuration of the http server. // ServerConfiguration represents the configuration of the http server.
type ServerConfiguration struct { type ServerConfiguration struct {
Host string `koanf:"host"` Host string `koanf:"host"`
Port int `koanf:"port"` Port int `koanf:"port"`
Path string `koanf:"path"` Path string `koanf:"path"`
ReadBufferSize int `koanf:"read_buffer_size"` ReadBufferSize int `koanf:"read_buffer_size"`
WriteBufferSize int `koanf:"write_buffer_size"` WriteBufferSize int `koanf:"write_buffer_size"`
EnablePprof bool `koanf:"enable_endpoint_pprof"` EnablePprof bool `koanf:"enable_endpoint_pprof"`
EnableExpvars bool `koanf:"enable_endpoint_expvars"` EnableExpvars bool `koanf:"enable_endpoint_expvars"`
DisableHealthcheck bool `koanf:"disable_healthcheck"`
TLS ServerTLSConfiguration `koanf:"tls"` TLS ServerTLSConfiguration `koanf:"tls"`
} }

View File

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

View File

@ -6,3 +6,11 @@ const apiFile = "openapi.yml"
const indexFile = "index.html" const indexFile = "index.html"
const dev = "dev" 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 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.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)) logger.Fatal(server.ServeTLS(listener, configuration.Server.TLS.Certificate, configuration.Server.TLS.Key))
} else { } 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.Infof("Listening for non-TLS connections on %s%s", addrPattern, configuration.Server.Path)
logger.Fatal(server.Serve(listener)) 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
}