feat(configuration): env config file discovery (#4618)

This allows Authelia to discover config files and config options via environment variables.

Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
pull/4632/head
James Elliott 2022-12-23 11:03:50 +11:00 committed by GitHub
parent 18032dd05d
commit 0130edb870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 94 additions and 47 deletions

View File

@ -11,15 +11,16 @@ WORKDIR /app
# Set environment variables # Set environment variables
ENV PATH="/app:${PATH}" \ ENV PATH="/app:${PATH}" \
PUID=0 \ PUID=0 \
PGID=0 PGID=0 \
X_AUTHELIA_CONFIG="/config/configuration.yml"
RUN \ RUN \
apk --no-cache add ca-certificates su-exec tzdata apk --no-cache add ca-certificates su-exec tzdata
COPY LICENSE .healthcheck.env entrypoint.sh healthcheck.sh ./ COPY LICENSE .healthcheck.env entrypoint.sh healthcheck.sh ./
RUN \ RUN \
chmod 0666 /app/.healthcheck.env chmod 0666 /app/.healthcheck.env
COPY authelia-${TARGETOS}-${TARGETARCH}-musl ./authelia COPY authelia-${TARGETOS}-${TARGETARCH}-musl ./authelia
@ -28,5 +29,4 @@ EXPOSE 9091
VOLUME /config VOLUME /config
ENTRYPOINT ["/app/entrypoint.sh"] ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["--config", "/config/configuration.yml"]
HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh

View File

@ -20,14 +20,14 @@ FROM golang:1.19.4-alpine AS builder-backend
WORKDIR /go/src/app WORKDIR /go/src/app
RUN \ RUN \
echo ">> Downloading required apk's..." && \ echo ">> Downloading required apk's..." && \
apk --no-cache add gcc musl-dev apk --no-cache add gcc musl-dev
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN \ RUN \
echo ">> Downloading go modules..." && \ echo ">> Downloading go modules..." && \
go mod download go mod download
COPY / ./ COPY / ./
@ -36,12 +36,12 @@ COPY --from=builder-frontend /node/src/internal/server/public_html internal/serv
ARG LDFLAGS_EXTRA 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 && \ 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=1 CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong" CGO_LDFLAGS="-Wl,-z,relro,-z,now" go test -c --tags coverage -covermode=atomic \ CGO_ENABLED=1 CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong" CGO_LDFLAGS="-Wl,-z,relro,-z,now" 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/...
# =================================== # ===================================
# ===== Authelia official image ===== # ===== Authelia official image =====
@ -58,7 +58,8 @@ EXPOSE 9091
VOLUME /config VOLUME /config
ENV PATH="/app:${PATH}" ENV PATH="/app:${PATH}" \
X_AUTHELIA_CONFIG="/config/configuration.yml"
CMD ["authelia", "-test.coverprofile=/authelia/coverage.txt", "COVERAGE", "--config", "/config/configuration.yml"] CMD ["authelia", "-test.coverprofile=/authelia/coverage.txt", "COVERAGE"]
HEALTHCHECK --interval=30s --timeout=3s CMD /app/healthcheck.sh HEALTHCHECK --interval=30s --timeout=3s CMD /app/healthcheck.sh

View File

@ -18,14 +18,14 @@ FROM golang:1.19.4-alpine AS builder-backend
WORKDIR /go/src/app WORKDIR /go/src/app
RUN \ RUN \
echo ">> Downloading required apk's..." && \ echo ">> Downloading required apk's..." && \
apk --no-cache add gcc musl-dev apk --no-cache add gcc musl-dev
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN \ RUN \
echo ">> Downloading go modules..." && \ echo ">> Downloading go modules..." && \
go mod download go mod download
COPY / ./ COPY / ./
@ -34,11 +34,11 @@ COPY --from=builder-frontend /node/src/internal/server/public_html internal/serv
ARG LDFLAGS_EXTRA ARG LDFLAGS_EXTRA
RUN \ RUN \
mv api internal/server/public_html/api && \ mv api internal/server/public_html/api && \
chmod 0666 /go/src/app/.healthcheck.env && \ chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \ echo ">> Starting go build..." && \
CGO_ENABLED=1 CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong" CGO_LDFLAGS="-Wl,-z,relro,-z,now" go build \ CGO_ENABLED=1 CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong" CGO_LDFLAGS="-Wl,-z,relro,-z,now" go build \
-ldflags "-linkmode=external -s -w ${LDFLAGS_EXTRA}" -trimpath -buildmode=pie -o authelia ./cmd/authelia -ldflags "-linkmode=external -s -w ${LDFLAGS_EXTRA}" -trimpath -buildmode=pie -o authelia ./cmd/authelia
# =================================== # ===================================
# ===== Authelia official image ===== # ===== Authelia official image =====
@ -50,20 +50,20 @@ WORKDIR /app
# Set environment variables # Set environment variables
ENV PATH="/app:${PATH}" \ ENV PATH="/app:${PATH}" \
PUID=0 \ PUID=0 \
PGID=0 PGID=0 \
X_AUTHELIA_CONFIG="/config/configuration.yml"
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 /go/src/app/.healthcheck.env ./ 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 ./
RUN \ RUN \
chmod 0666 /app/.healthcheck.env chmod 0666 /app/.healthcheck.env
EXPOSE 9091 EXPOSE 9091
VOLUME /config VOLUME /config
ENTRYPOINT ["/app/entrypoint.sh"] ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["--config", "/config/configuration.yml"]
HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh

View File

@ -12,18 +12,23 @@ weight: 101200
toc: true toc: true
--- ---
## Loading Behaviour ## Loading Behaviour and Discovery
There are several options which affect the loading of files: There are several options which affect the loading of files:
| Name | Argument | Description | | Name | Argument | Environment Variable | Description |
|:-----------------:|:-------------------------------:|:----------------------------------------------------------------------------------:| |:-----------------:|:-------------------------------:|:---------------------------:|:----------------------------------------------------------------------------------:|
| Files/Directories | `--config`, `-c` | A list of file or directory (non-recursive) paths to load configuration files from | | Files/Directories | `--config`, `-c` | `X_AUTHELIA_CONFIG` | A list of file or directory (non-recursive) paths to load configuration files from |
| Filters | `--config.experimental.filters` | A list of filters applied to every file from the Files or Directories options | | Filters | `--config.experimental.filters` | `X_AUTHELIA_CONFIG_FILTERS` | A list of filters applied to every file from the Files or Directories options |
__*Note:* when specifying directories and files, the individual files specified must not be within any of the __*Note:* when specifying directories and files, the individual files specified must not be within any of the
directories specified.__ directories specified.__
Configuration options can be discovered via either the Argument or Environment Variable, but not both at the same time.
If both are specified the Argument takes precedence and the Environment Variable is ignored. It is generally recommended
that if you're using a container that you use the Environment Variable as this will allow you to execute other commands
from the context of the container more easily.
## Formats ## Formats
The only supported configuration file format is [YAML](#yaml). The only supported configuration file format is [YAML](#yaml).

View File

@ -537,9 +537,11 @@ const (
cmdConfigDefaultContainer = "/config/configuration.yml" cmdConfigDefaultContainer = "/config/configuration.yml"
cmdConfigDefaultDaemon = "/etc/authelia/configuration.yml" cmdConfigDefaultDaemon = "/etc/authelia/configuration.yml"
cmdFlagNameConfig = "config" cmdFlagNameConfig = "config"
cmdFlagEnvNameConfig = "X_AUTHELIA_CONFIG"
cmdFlagNameConfigExpFilters = "config.experimental.filters" cmdFlagNameConfigExpFilters = "config.experimental.filters"
cmdFlagEnvNameConfigFilters = "X_AUTHELIA_CONFIG_FILTERS"
cmdFlagNameCharSet = "charset" cmdFlagNameCharSet = "charset"
cmdFlagValueCharSet = "alphanumeric" cmdFlagValueCharSet = "alphanumeric"
@ -655,4 +657,42 @@ The following filters are available:
'expand-env' filter for example. 'expand-env' filter for example.
For a full list of functions see: https://www.authelia.com/configuration/methods/files/#functions` For a full list of functions see: https://www.authelia.com/configuration/methods/files/#functions`
helpTopicConfig = `Configuration can be specified in multiple layers where each layer is a different source from
the last. The layers are loaded in the order below where each layer potentially overrides the individual settings from
previous layers with the individual settings it provides (i.e. if the same setting is specified twice).
Layers:
- File/Directory Paths
- Environment Variables
- Secrets
File/Directory Paths:
File/Directory Paths can be specified either via the '--config' CLI argument or the 'X_AUTHELIA_CONFIG' environment
variable. If both the environment variable AND the CLI argument are specified the environment variable is completely
ignored. These values both take lists separated by commas.
Directories that are loaded via this method load all files with relevant extensions from the directory, this is not
recursive. This means all files with these extensions must be Authelia configuration files with valid syntax.
The paths specified are loaded in order, where individual settings specified by later files potentially overrides
individual settings by later files (i.e. if the same setting is specified twice). Files specified Files in
directories are loaded in lexicographic order.
The files loaded via this method can be interpolated or templated via the configuration filters. Read more about
this topic by running: authelia -h authelia filters
Environment Variables:
Most configuration options in Authelia can be specified via an environment variable. The available options and the
specific environment variable mapping can be found here: https://www.authelia.com/configuration/methods/environment/
Secrets:
Some configuration options in Authelia can be specified via an environment variable which refers to the location of
a file; also known as a secret. Every configuration key that ends with the following strings can be loaded in this
way: 'key', 'secret', 'password', 'token'.
The available options and the specific secret mapping can be found here: https://www.authelia.com/configuration/methods/secrets/`
) )

View File

@ -277,7 +277,7 @@ func (ctx *CmdCtx) ConfigEnsureExistsRunE(cmd *cobra.Command, _ []string) (err e
result XEnvCLIResult result XEnvCLIResult
) )
if configs, result, err = loadXEnvCLIStringSliceValue(cmd, "", cmdFlagNameConfig); err != nil { if configs, result, err = loadXEnvCLIStringSliceValue(cmd, cmdFlagEnvNameConfig, cmdFlagNameConfig); err != nil {
return err return err
} }

View File

@ -45,9 +45,9 @@ func NewRootCmd() (cmd *cobra.Command) {
DisableAutoGenTag: true, DisableAutoGenTag: true,
} }
cmd.PersistentFlags().StringSliceP(cmdFlagNameConfig, "c", []string{"configuration.yml"}, "configuration files or directories to load") cmd.PersistentFlags().StringSliceP(cmdFlagNameConfig, "c", []string{"configuration.yml"}, "configuration files or directories to load, for more information run 'authelia -h authelia config'")
cmd.PersistentFlags().StringSlice(cmdFlagNameConfigExpFilters, nil, "list of filters to apply to all configuration files, for more information: authelia --help authelia filters") cmd.PersistentFlags().StringSlice(cmdFlagNameConfigExpFilters, nil, "list of filters to apply to all configuration files, for more information run 'authelia -h authelia filters'")
cmd.AddCommand( cmd.AddCommand(
newAccessControlCommand(ctx), newAccessControlCommand(ctx),
@ -56,7 +56,8 @@ func NewRootCmd() (cmd *cobra.Command) {
newStorageCmd(ctx), newStorageCmd(ctx),
newValidateConfigCmd(ctx), newValidateConfigCmd(ctx),
newHelpTopic("filters", "Help for the config filters", helpTopicConfigFilters), newHelpTopic("config", "Help for the config file/directory paths", helpTopicConfig),
newHelpTopic("filters", "help topic for the config filters", helpTopicConfigFilters),
) )
return cmd return cmd

View File

@ -246,7 +246,7 @@ func loadXEnvCLIConfigValues(cmd *cobra.Command) (configs []string, filters []co
filterNames []string filterNames []string
) )
if configs, _, err = loadXEnvCLIStringSliceValue(cmd, "", cmdFlagNameConfig); err != nil { if configs, _, err = loadXEnvCLIStringSliceValue(cmd, cmdFlagEnvNameConfig, cmdFlagNameConfig); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -254,7 +254,7 @@ func loadXEnvCLIConfigValues(cmd *cobra.Command) (configs []string, filters []co
return nil, nil, err return nil, nil, err
} }
if filterNames, _, err = loadXEnvCLIStringSliceValue(cmd, "", cmdFlagNameConfigExpFilters); err != nil { if filterNames, _, err = loadXEnvCLIStringSliceValue(cmd, cmdFlagEnvNameConfigFilters, cmdFlagNameConfigExpFilters); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -11,7 +11,8 @@ RUN mkdir -p /config && chown dev:dev /config
USER dev USER dev
ENV PATH="/app:${PATH}" ENV PATH="/app:${PATH}" \
X_AUTHELIA_CONFIG="/config/configuration.yml"
VOLUME /config VOLUME /config

View File

@ -4,7 +4,6 @@ ARG USER_ID
ARG GROUP_ID ARG GROUP_ID
RUN yarn global add pnpm && \ RUN yarn global add pnpm && \
pnpm config set --global store-dir /tmp/.pnpm-store && \
deluser node && \ deluser node && \
addgroup --gid ${GROUP_ID} dev && \ addgroup --gid ${GROUP_ID} dev && \
adduser --uid ${USER_ID} -G dev -D dev adduser --uid ${USER_ID} -G dev -D dev

View File

@ -14,7 +14,7 @@ services:
volumes: volumes:
- './example/compose/authelia/resources/:/resources' - './example/compose/authelia/resources/:/resources'
- '../../web:/app' - '../../web:/app'
- '~/.local/share/pnpm/store:/tmp/.pnpm-store' - '~/.local/share/pnpm/store:/app/.pnpm-store'
labels: labels:
# Traefik 1.x # Traefik 1.x
- 'traefik.frontend.rule=Host:login.example.com' - 'traefik.frontend.rule=Host:login.example.com'

View File

@ -4,6 +4,6 @@ set -e
while true; while true;
do do
AUTHELIA_SERVER_DISABLE_HEALTHCHECK=true CGO_ENABLED=1 dlv --listen 0.0.0.0:2345 --headless=true --output=./authelia --continue --accept-multiclient debug cmd/authelia/*.go -- --config /config/configuration.yml AUTHELIA_SERVER_DISABLE_HEALTHCHECK=true CGO_ENABLED=1 dlv --listen 0.0.0.0:2345 --headless=true --output=./authelia --continue --accept-multiclient debug cmd/authelia/*.go
sleep 10 sleep 10
done done

View File

@ -10,7 +10,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
yaml "gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/authelia/authelia/v4/internal/model" "github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/storage" "github.com/authelia/authelia/v4/internal/storage"
@ -73,7 +73,7 @@ func (s *CLISuite) TestShouldPrintVersion() {
} }
func (s *CLISuite) TestShouldValidateConfig() { func (s *CLISuite) TestShouldValidateConfig() {
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "validate-config", "--config=/config/configuration.yml"}) output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "validate-config"})
s.Assert().NoError(err) s.Assert().NoError(err)
s.Assert().Contains(output, "Configuration parsed and loaded successfully without errors.") s.Assert().Contains(output, "Configuration parsed and loaded successfully without errors.")
} }