From 367f86ecfbbf6320ddfb2a76fec03342099edc7e Mon Sep 17 00:00:00 2001 From: Jens L Date: Fri, 21 Apr 2023 13:32:48 +0300 Subject: [PATCH] root: optimise healthchecks (#5337) * tests: remove redundant healthchecks Signed-off-by: Jens Langhammer * internal: do healthcheck within proxy instead of wget to use correct port Signed-off-by: Jens Langhammer * fix docs Signed-off-by: Jens Langhammer * fix tags Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- cmd/ldap/main.go | 109 +++++++++++++---------- cmd/proxy/main.go | 109 +++++++++++++---------- cmd/radius/main.go | 94 +++++++++++++++++++ cmd/radius/server.go | 78 ---------------- cmd/server/healthcheck.go | 1 - internal/outpost/ak/healthcheck/cmd.go | 38 ++++++++ ldap.Dockerfile | 2 +- proxy.Dockerfile | 2 +- radius.Dockerfile | 2 +- tests/e2e/test_provider_oidc.py | 8 +- tests/e2e/test_provider_oidc_implicit.py | 8 +- tests/e2e/test_provider_saml.py | 8 +- website/docs/flow/context/index.md | 6 +- 13 files changed, 264 insertions(+), 201 deletions(-) create mode 100644 cmd/radius/main.go delete mode 100644 cmd/radius/server.go create mode 100644 internal/outpost/ak/healthcheck/cmd.go diff --git a/cmd/ldap/main.go b/cmd/ldap/main.go index eb16c0ffc..2580b5c15 100644 --- a/cmd/ldap/main.go +++ b/cmd/ldap/main.go @@ -6,11 +6,13 @@ import ( "os" log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" "goauthentik.io/internal/common" "goauthentik.io/internal/config" "goauthentik.io/internal/debug" "goauthentik.io/internal/outpost/ak" + "goauthentik.io/internal/outpost/ak/healthcheck" "goauthentik.io/internal/outpost/ldap" ) @@ -21,59 +23,72 @@ Required environment variables: - AUTHENTIK_TOKEN: Token to authenticate with - AUTHENTIK_INSECURE: Skip SSL Certificate verification` -func main() { - log.SetLevel(log.DebugLevel) - log.SetFormatter(&log.JSONFormatter{ - FieldMap: log.FieldMap{ - log.FieldKeyMsg: "event", - log.FieldKeyTime: "timestamp", - }, - DisableHTMLEscape: true, - }) - debug.EnableDebugServer() - akURL := config.Get().AuthentikHost - if akURL == "" { - fmt.Println("env AUTHENTIK_HOST not set!") - fmt.Println(helpMessage) - os.Exit(1) - } - akToken := config.Get().AuthentikToken - if akToken == "" { - fmt.Println("env AUTHENTIK_TOKEN not set!") - fmt.Println(helpMessage) - os.Exit(1) - } +var rootCmd = &cobra.Command{ + Long: helpMessage, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + log.SetLevel(log.DebugLevel) + log.SetFormatter(&log.JSONFormatter{ + FieldMap: log.FieldMap{ + log.FieldKeyMsg: "event", + log.FieldKeyTime: "timestamp", + }, + DisableHTMLEscape: true, + }) + }, + Run: func(cmd *cobra.Command, args []string) { + debug.EnableDebugServer() + akURL := config.Get().AuthentikHost + if akURL == "" { + fmt.Println("env AUTHENTIK_HOST not set!") + fmt.Println(helpMessage) + os.Exit(1) + } + akToken := config.Get().AuthentikToken + if akToken == "" { + fmt.Println("env AUTHENTIK_TOKEN not set!") + fmt.Println(helpMessage) + os.Exit(1) + } - akURLActual, err := url.Parse(akURL) - if err != nil { - fmt.Println(err) - fmt.Println(helpMessage) - os.Exit(1) - } + akURLActual, err := url.Parse(akURL) + if err != nil { + fmt.Println(err) + fmt.Println(helpMessage) + os.Exit(1) + } + + ex := common.Init() + defer common.Defer() + go func() { + for { + <-ex + os.Exit(0) + } + }() + + ac := ak.NewAPIController(*akURLActual, akToken) + if ac == nil { + os.Exit(1) + } + defer ac.Shutdown() + + ac.Server = ldap.NewServer(ac) + + err = ac.Start() + if err != nil { + log.WithError(err).Panic("Failed to run server") + } - ex := common.Init() - defer common.Defer() - go func() { for { <-ex - os.Exit(0) } - }() + }, +} - ac := ak.NewAPIController(*akURLActual, akToken) - if ac == nil { +func main() { + rootCmd.AddCommand(healthcheck.Command) + err := rootCmd.Execute() + if err != nil { os.Exit(1) } - defer ac.Shutdown() - - ac.Server = ldap.NewServer(ac) - - err = ac.Start() - if err != nil { - log.WithError(err).Panic("Failed to run server") - } - - for { - <-ex - } } diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 52fb6b4ba..e58a161fb 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -6,11 +6,13 @@ import ( "os" log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" "goauthentik.io/internal/common" "goauthentik.io/internal/config" "goauthentik.io/internal/debug" "goauthentik.io/internal/outpost/ak" + "goauthentik.io/internal/outpost/ak/healthcheck" "goauthentik.io/internal/outpost/proxyv2" ) @@ -24,59 +26,72 @@ Required environment variables: Optionally, you can set these: - AUTHENTIK_HOST_BROWSER: URL to use in the browser, when it differs from AUTHENTIK_HOST` -func main() { - log.SetLevel(log.DebugLevel) - log.SetFormatter(&log.JSONFormatter{ - FieldMap: log.FieldMap{ - log.FieldKeyMsg: "event", - log.FieldKeyTime: "timestamp", - }, - DisableHTMLEscape: true, - }) - debug.EnableDebugServer() - akURL := config.Get().AuthentikHost - if akURL == "" { - fmt.Println("env AUTHENTIK_HOST not set!") - fmt.Println(helpMessage) - os.Exit(1) - } - akToken := config.Get().AuthentikToken - if akToken == "" { - fmt.Println("env AUTHENTIK_TOKEN not set!") - fmt.Println(helpMessage) - os.Exit(1) - } +var rootCmd = &cobra.Command{ + Long: helpMessage, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + log.SetLevel(log.DebugLevel) + log.SetFormatter(&log.JSONFormatter{ + FieldMap: log.FieldMap{ + log.FieldKeyMsg: "event", + log.FieldKeyTime: "timestamp", + }, + DisableHTMLEscape: true, + }) + }, + Run: func(cmd *cobra.Command, args []string) { + debug.EnableDebugServer() + akURL := config.Get().AuthentikHost + if akURL == "" { + fmt.Println("env AUTHENTIK_HOST not set!") + fmt.Println(helpMessage) + os.Exit(1) + } + akToken := config.Get().AuthentikToken + if akToken == "" { + fmt.Println("env AUTHENTIK_TOKEN not set!") + fmt.Println(helpMessage) + os.Exit(1) + } - akURLActual, err := url.Parse(akURL) - if err != nil { - fmt.Println(err) - fmt.Println(helpMessage) - os.Exit(1) - } + akURLActual, err := url.Parse(akURL) + if err != nil { + fmt.Println(err) + fmt.Println(helpMessage) + os.Exit(1) + } + + ex := common.Init() + defer common.Defer() + go func() { + for { + <-ex + os.Exit(0) + } + }() + + ac := ak.NewAPIController(*akURLActual, akToken) + if ac == nil { + os.Exit(1) + } + defer ac.Shutdown() + + ac.Server = proxyv2.NewProxyServer(ac) + + err = ac.Start() + if err != nil { + log.WithError(err).Panic("Failed to run server") + } - ex := common.Init() - defer common.Defer() - go func() { for { <-ex - os.Exit(0) } - }() + }, +} - ac := ak.NewAPIController(*akURLActual, akToken) - if ac == nil { +func main() { + rootCmd.AddCommand(healthcheck.Command) + err := rootCmd.Execute() + if err != nil { os.Exit(1) } - defer ac.Shutdown() - - ac.Server = proxyv2.NewProxyServer(ac) - - err = ac.Start() - if err != nil { - log.WithError(err).Panic("Failed to run server") - } - - for { - <-ex - } } diff --git a/cmd/radius/main.go b/cmd/radius/main.go new file mode 100644 index 000000000..f442cb101 --- /dev/null +++ b/cmd/radius/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "fmt" + "net/url" + "os" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "goauthentik.io/internal/common" + "goauthentik.io/internal/debug" + "goauthentik.io/internal/outpost/ak" + "goauthentik.io/internal/outpost/ak/healthcheck" + "goauthentik.io/internal/outpost/radius" +) + +const helpMessage = `authentik radius + +Required environment variables: +- AUTHENTIK_HOST: URL to connect to (format "http://authentik.company") +- AUTHENTIK_TOKEN: Token to authenticate with +- AUTHENTIK_INSECURE: Skip SSL Certificate verification` + +var rootCmd = &cobra.Command{ + Long: helpMessage, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + log.SetLevel(log.DebugLevel) + log.SetFormatter(&log.JSONFormatter{ + FieldMap: log.FieldMap{ + log.FieldKeyMsg: "event", + log.FieldKeyTime: "timestamp", + }, + DisableHTMLEscape: true, + }) + }, + Run: func(cmd *cobra.Command, args []string) { + debug.EnableDebugServer() + akURL, found := os.LookupEnv("AUTHENTIK_HOST") + if !found { + fmt.Println("env AUTHENTIK_HOST not set!") + fmt.Println(helpMessage) + os.Exit(1) + } + akToken, found := os.LookupEnv("AUTHENTIK_TOKEN") + if !found { + fmt.Println("env AUTHENTIK_TOKEN not set!") + fmt.Println(helpMessage) + os.Exit(1) + } + + akURLActual, err := url.Parse(akURL) + if err != nil { + fmt.Println(err) + fmt.Println(helpMessage) + os.Exit(1) + } + + ex := common.Init() + defer common.Defer() + go func() { + for { + <-ex + os.Exit(0) + } + }() + + ac := ak.NewAPIController(*akURLActual, akToken) + if ac == nil { + os.Exit(1) + } + defer ac.Shutdown() + + ac.Server = radius.NewServer(ac) + + err = ac.Start() + if err != nil { + log.WithError(err).Panic("Failed to run server") + } + + for { + <-ex + } + + }, +} + +func main() { + rootCmd.AddCommand(healthcheck.Command) + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} diff --git a/cmd/radius/server.go b/cmd/radius/server.go deleted file mode 100644 index 60ce95f1a..000000000 --- a/cmd/radius/server.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "fmt" - "net/url" - "os" - - log "github.com/sirupsen/logrus" - - "goauthentik.io/internal/common" - "goauthentik.io/internal/debug" - "goauthentik.io/internal/outpost/ak" - "goauthentik.io/internal/outpost/radius" -) - -const helpMessage = `authentik radius - -Required environment variables: -- AUTHENTIK_HOST: URL to connect to (format "http://authentik.company") -- AUTHENTIK_TOKEN: Token to authenticate with -- AUTHENTIK_INSECURE: Skip SSL Certificate verification` - -func main() { - log.SetLevel(log.DebugLevel) - log.SetFormatter(&log.JSONFormatter{ - FieldMap: log.FieldMap{ - log.FieldKeyMsg: "event", - log.FieldKeyTime: "timestamp", - }, - DisableHTMLEscape: true, - }) - go debug.EnableDebugServer() - akURL, found := os.LookupEnv("AUTHENTIK_HOST") - if !found { - fmt.Println("env AUTHENTIK_HOST not set!") - fmt.Println(helpMessage) - os.Exit(1) - } - akToken, found := os.LookupEnv("AUTHENTIK_TOKEN") - if !found { - fmt.Println("env AUTHENTIK_TOKEN not set!") - fmt.Println(helpMessage) - os.Exit(1) - } - - akURLActual, err := url.Parse(akURL) - if err != nil { - fmt.Println(err) - fmt.Println(helpMessage) - os.Exit(1) - } - - ex := common.Init() - defer common.Defer() - go func() { - for { - <-ex - os.Exit(0) - } - }() - - ac := ak.NewAPIController(*akURLActual, akToken) - if ac == nil { - os.Exit(1) - } - defer ac.Shutdown() - - ac.Server = radius.NewServer(ac) - - err = ac.Start() - if err != nil { - log.WithError(err).Panic("Failed to run server") - } - - for { - <-ex - } -} diff --git a/cmd/server/healthcheck.go b/cmd/server/healthcheck.go index 68618052e..57ee0f650 100644 --- a/cmd/server/healthcheck.go +++ b/cmd/server/healthcheck.go @@ -25,7 +25,6 @@ var healthcheckCmd = &cobra.Command{ os.Exit(1) } mode := args[0] - config.Get() exitCode := 1 log.WithField("mode", mode).Debug("checking health") switch strings.ToLower(mode) { diff --git a/internal/outpost/ak/healthcheck/cmd.go b/internal/outpost/ak/healthcheck/cmd.go new file mode 100644 index 000000000..3b12a5f23 --- /dev/null +++ b/internal/outpost/ak/healthcheck/cmd.go @@ -0,0 +1,38 @@ +package healthcheck + +import ( + "fmt" + "net/http" + "os" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "goauthentik.io/internal/config" + "goauthentik.io/internal/utils/web" +) + +var Command = &cobra.Command{ + Use: "healthcheck", + Run: func(cmd *cobra.Command, args []string) { + config.Get() + os.Exit(check()) + }, +} + +func check() int { + h := &http.Client{ + Transport: web.NewUserAgentTransport("goauthentik.io/healthcheck", http.DefaultTransport), + } + url := fmt.Sprintf("http://%s/outpost.goauthentik.io/ping", config.Get().Listen.Metrics) + res, err := h.Head(url) + if err != nil { + log.WithError(err).Warning("failed to send healthcheck request") + return 1 + } + if res.StatusCode >= 400 { + log.WithField("status", res.StatusCode).Warning("unhealthy status code") + return 1 + } + log.Debug("successfully checked health") + return 0 +} diff --git a/ldap.Dockerfile b/ldap.Dockerfile index 53bdb1dc7..e4177ae19 100644 --- a/ldap.Dockerfile +++ b/ldap.Dockerfile @@ -19,7 +19,7 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH COPY --from=builder /go/ldap / -HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "wget", "--spider", "http://localhost:9300/outpost.goauthentik.io/ping" ] +HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/ldap", "healthcheck" ] EXPOSE 3389 6636 9300 diff --git a/proxy.Dockerfile b/proxy.Dockerfile index 92338318e..cb39d09d6 100644 --- a/proxy.Dockerfile +++ b/proxy.Dockerfile @@ -32,7 +32,7 @@ COPY --from=web-builder /static/security.txt /web/security.txt COPY --from=web-builder /static/dist/ /web/dist/ COPY --from=web-builder /static/authentik/ /web/authentik/ -HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "wget", "--spider", "http://localhost:9300/outpost.goauthentik.io/ping" ] +HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/proxy", "healthcheck" ] EXPOSE 9000 9300 9443 diff --git a/radius.Dockerfile b/radius.Dockerfile index 1c5abbf6c..5d8ce345e 100644 --- a/radius.Dockerfile +++ b/radius.Dockerfile @@ -19,7 +19,7 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH COPY --from=builder /go/radius / -HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "wget", "--spider", "http://localhost:9300/outpost.goauthentik.io/ping" ] +HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/radius", "healthcheck" ] EXPOSE 1812/udp 9300 diff --git a/tests/e2e/test_provider_oidc.py b/tests/e2e/test_provider_oidc.py index a828c446f..001059106 100644 --- a/tests/e2e/test_provider_oidc.py +++ b/tests/e2e/test_provider_oidc.py @@ -6,7 +6,6 @@ from unittest.case import skipUnless from docker import DockerClient, from_env from docker.models.containers import Container -from docker.types import Healthcheck from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec @@ -41,14 +40,9 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): sleep(1) client: DockerClient = from_env() container = client.containers.run( - image="ghcr.io/beryju/oidc-test-client:v1", + image="ghcr.io/beryju/oidc-test-client:1.3", detach=True, network_mode="host", - healthcheck=Healthcheck( - test=["CMD", "wget", "--spider", "http://localhost:9009/health"], - interval=5 * 100 * 1000000, - start_period=1 * 100 * 1000000, - ), environment={ "OIDC_CLIENT_ID": self.client_id, "OIDC_CLIENT_SECRET": self.client_secret, diff --git a/tests/e2e/test_provider_oidc_implicit.py b/tests/e2e/test_provider_oidc_implicit.py index 4fbdb92a4..0c8b58fd4 100644 --- a/tests/e2e/test_provider_oidc_implicit.py +++ b/tests/e2e/test_provider_oidc_implicit.py @@ -6,7 +6,6 @@ from unittest.case import skipUnless from docker import DockerClient, from_env from docker.models.containers import Container -from docker.types import Healthcheck from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec @@ -41,14 +40,9 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): sleep(1) client: DockerClient = from_env() container = client.containers.run( - image="ghcr.io/beryju/oidc-test-client:v1", + image="ghcr.io/beryju/oidc-test-client:1.3", detach=True, network_mode="host", - healthcheck=Healthcheck( - test=["CMD", "wget", "--spider", "http://localhost:9009/health"], - interval=5 * 100 * 1000000, - start_period=1 * 100 * 1000000, - ), environment={ "OIDC_CLIENT_ID": self.client_id, "OIDC_CLIENT_SECRET": self.client_secret, diff --git a/tests/e2e/test_provider_saml.py b/tests/e2e/test_provider_saml.py index d54a7304a..378b65e63 100644 --- a/tests/e2e/test_provider_saml.py +++ b/tests/e2e/test_provider_saml.py @@ -6,7 +6,6 @@ from unittest.case import skipUnless from docker import DockerClient, from_env from docker.models.containers import Container -from docker.types import Healthcheck from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec @@ -40,14 +39,9 @@ class TestProviderSAML(SeleniumTestCase): if force_post: metadata_url += f"&force_binding={SAML_BINDING_POST}" container = client.containers.run( - image="ghcr.io/beryju/saml-test-sp:latest", + image="ghcr.io/beryju/saml-test-sp:1.1", detach=True, network_mode="host", - healthcheck=Healthcheck( - test=["CMD", "wget", "--spider", "http://localhost:9009/health"], - interval=5 * 100 * 1000000, - start_period=1 * 100 * 1000000, - ), environment={ "SP_ENTITY_ID": provider.issuer, "SP_SSO_BINDING": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", diff --git a/website/docs/flow/context/index.md b/website/docs/flow/context/index.md index 546267040..b4cc18954 100644 --- a/website/docs/flow/context/index.md +++ b/website/docs/flow/context/index.md @@ -47,9 +47,7 @@ This data can be modified with policies. The data is also used by stages like [U Stores the final redirect URL that the user's browser will be sent to after the flow is finished executing successfully. This is set when an un-authenticated user attempts to access a secured application, and when a user authenticates/enrolls with an external source. -#### Identifications tage - -##### `pending_user_identifier` (string) +#### `pending_user_identifier` (string) If _Show matched user_ is disabled, this key will hold the user identifier entered by the user in the identification stage. @@ -165,7 +163,7 @@ Boolean set to true after the email form the email stage has been sent. Optionally override the email address that the email will be sent to. If not set, defaults to the email of `pending_user`. -#### Identifications tage +#### Identification stage ##### `pending_user_identifier` (string)