diff --git a/Dockerfile b/Dockerfile index f6b2581fb..16d797f6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ WORKDIR /node/src/app COPY web . # Install the dependencies and build -RUN yarn install --frozen-lockfile && yarn build +RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn build # =================================== # ===== Authelia official image ===== diff --git a/Dockerfile.arm32v7 b/Dockerfile.arm32v7 index ca2386ca0..8cccffd3a 100644 --- a/Dockerfile.arm32v7 +++ b/Dockerfile.arm32v7 @@ -39,7 +39,7 @@ WORKDIR /node/src/app COPY web . # Install the dependencies and build -RUN yarn install --frozen-lockfile && yarn build +RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn build # =================================== # ===== Authelia official image ===== diff --git a/Dockerfile.arm64v8 b/Dockerfile.arm64v8 index 25e1e0853..fb40b2a50 100644 --- a/Dockerfile.arm64v8 +++ b/Dockerfile.arm64v8 @@ -39,7 +39,7 @@ WORKDIR /node/src/app COPY web . # Install the dependencies and build -RUN yarn install --frozen-lockfile && yarn build +RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn build # =================================== # ===== Authelia official image ===== diff --git a/Dockerfile.darwin b/Dockerfile.darwin index af39d2b5b..17c3a8bbd 100644 --- a/Dockerfile.darwin +++ b/Dockerfile.darwin @@ -38,7 +38,7 @@ WORKDIR /node/src/app COPY web . # Install the dependencies and build -RUN yarn install --frozen-lockfile && yarn build +RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn build # =================================== # ===== Authelia official image ===== diff --git a/cmd/authelia-scripts/cmd_build.go b/cmd/authelia-scripts/cmd_build.go index 981e48cc4..ee6779fb2 100644 --- a/cmd/authelia-scripts/cmd_build.go +++ b/cmd/authelia-scripts/cmd_build.go @@ -34,6 +34,7 @@ func buildFrontend() { // Then build the frontend cmd = utils.CommandWithStdout("yarn", "build") cmd.Dir = "web" + cmd.Env = append(os.Environ(), "INLINE_RUNTIME_CHUNK=false") if err := cmd.Run(); err != nil { log.Fatal(err) diff --git a/internal/server/index.go b/internal/server/index.go new file mode 100644 index 000000000..4381cf4f9 --- /dev/null +++ b/internal/server/index.go @@ -0,0 +1,46 @@ +package server + +import ( + "fmt" + "html/template" + "io/ioutil" + "os" + + "github.com/valyala/fasthttp" + + "github.com/authelia/authelia/internal/logging" + "github.com/authelia/authelia/internal/utils" +) + +var alphaNumericRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + +// ServeIndex serve the index.html file with nonce generated for supporting +// restrictive CSP while using material-ui. +func ServeIndex(publicDir string) fasthttp.RequestHandler { + f, err := os.Open(publicDir + "/index.html") + if err != nil { + logging.Logger().Fatalf("Unable to open index.html: %v", err) + } + + b, err := ioutil.ReadAll(f) + if err != nil { + logging.Logger().Fatalf("Unable to read index.html: %v", err) + } + + tmpl, err := template.New("index").Parse(string(b)) + if err != nil { + logging.Logger().Fatalf("Unable to parse index.html template: %v", err) + } + + return func(ctx *fasthttp.RequestCtx) { + nonce := utils.RandomString(32, alphaNumericRunes) + ctx.SetContentType("text/html; charset=utf-8") + ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self'; style-src 'self' 'nonce-%s'", nonce)) + err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ CSPNonce string }{CSPNonce: nonce}) + if err != nil { + ctx.Error("An error occurred", 503) + logging.Logger().Errorf("Unable to execute template: %v", err) + return + } + } +} \ No newline at end of file diff --git a/internal/server/server.go b/internal/server/server.go index 84a8d1b13..a1a986d81 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -28,7 +28,8 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi logging.Logger().Infof("Selected public_html directory is %s", publicDir) router := router.New() - router.GET("/", fasthttp.FSHandler(publicDir, 0)) + + router.GET("/", ServeIndex(publicDir)) router.ServeFiles("/static/{filepath:*}", publicDir+"/static") router.GET("/api/state", autheliaMiddleware(handlers.StateGet)) diff --git a/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml b/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml index 02a473f5b..64a236bee 100644 --- a/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml +++ b/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml @@ -23,6 +23,7 @@ services: - 'traefik.http.services.authelia_backend.loadbalancer.server.scheme=https' environment: - ENVIRONMENT=dev + - PUBLIC_DIR=/tmp/authelia-web networks: authelianet: ipv4_address: 192.168.240.50 diff --git a/internal/suites/example/compose/authelia/resources/entrypoint-backend.sh b/internal/suites/example/compose/authelia/resources/entrypoint-backend.sh index f6290ea38..cd1f88943 100755 --- a/internal/suites/example/compose/authelia/resources/entrypoint-backend.sh +++ b/internal/suites/example/compose/authelia/resources/entrypoint-backend.sh @@ -5,6 +5,11 @@ set -x echo "Use hot reloaded version of Authelia backend" go get github.com/cespare/reflex +# Fake index.html because Authelia reads it as a template at startup to inject nonces. +# This prevents a crash of Authelia in dev mode. +mkdir -p /tmp/authelia-web +touch /tmp/authelia-web/index.html + # Sleep 10 seconds to wait the end of npm install updating web directory # and making reflex reload multiple times. sleep 10 diff --git a/internal/suites/example/compose/authelia/resources/nginx.conf b/internal/suites/example/compose/authelia/resources/nginx.conf index 3961e7a3f..faadeaa72 100644 --- a/internal/suites/example/compose/authelia/resources/nginx.conf +++ b/internal/suites/example/compose/authelia/resources/nginx.conf @@ -13,6 +13,10 @@ http { set $backend_endpoint https://authelia-backend:9091; location / { + # We don't want to apply CSP in dev mode because the frontend is served by CRA + # and thus cannot have the nonce injected. + proxy_hide_header Content-Security-Policy; + proxy_set_header Host $http_host; proxy_pass $backend_endpoint; } diff --git a/web/public/index.html b/web/public/index.html index 489973147..df8a64a79 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -1,6 +1,8 @@ + +