mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-10-16 09:13:17 +01:00
feat(api): warn at startup when the debug route has no authentication
This commit is contained in:
@@ -383,12 +383,37 @@ func (a *Api) Provision(ctx *gotenberg.Context) error {
|
|||||||
// Logger.
|
// Logger.
|
||||||
a.logger = gotenberg.Logger(a)
|
a.logger = gotenberg.Logger(a)
|
||||||
|
|
||||||
|
a.warnInsecureDebugRoute()
|
||||||
|
|
||||||
// File system.
|
// File system.
|
||||||
a.fs = gotenberg.NewFileSystem(new(gotenberg.OsMkdirAll))
|
a.fs = gotenberg.NewFileSystem(new(gotenberg.OsMkdirAll))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// warnInsecureDebugRoute logs a warning when the debug route is reachable
|
||||||
|
// without authentication.
|
||||||
|
//
|
||||||
|
// The route reports the resolved configuration of every module, which is
|
||||||
|
// useful to an operator and equally useful to anyone else who can reach it.
|
||||||
|
// This warns rather than refuses: an operator may sit behind a gateway that
|
||||||
|
// authenticates on Gotenberg's behalf, and failing startup would break them.
|
||||||
|
func (a *Api) warnInsecureDebugRoute() {
|
||||||
|
if !a.enableDebugRoute || a.basicAuthUsername != "" || a.oidcEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if a.logger == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.logger.WarnContext(
|
||||||
|
context.Background(),
|
||||||
|
"--api-enable-debug-route (API_ENABLE_DEBUG_ROUTE) is enabled but no authentication is configured, so anyone who can reach Gotenberg can read its configuration. Set --api-enable-basic-auth (API_ENABLE_BASIC_AUTH) with GOTENBERG_API_BASIC_AUTH_USERNAME and GOTENBERG_API_BASIC_AUTH_PASSWORD, set --api-enable-oidc-auth (API_ENABLE_OIDC_AUTH), or disable the route.",
|
||||||
|
slog.String("flag", "--api-enable-debug-route"),
|
||||||
|
slog.String("env", "API_ENABLE_DEBUG_ROUTE"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates the module properties.
|
// Validate validates the module properties.
|
||||||
func (a *Api) Validate() error {
|
func (a *Api) Validate() error {
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -65,3 +67,68 @@ func TestApi_Validate_Auth(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApi_warnInsecureDebugRoute(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
scenario string
|
||||||
|
api Api
|
||||||
|
expectWarn bool
|
||||||
|
expectFields []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
scenario: "debug route on with no auth warns",
|
||||||
|
api: Api{enableDebugRoute: true},
|
||||||
|
expectWarn: true,
|
||||||
|
expectFields: []string{"--api-enable-debug-route", "API_ENABLE_DEBUG_ROUTE", "--api-enable-basic-auth", "API_ENABLE_BASIC_AUTH", "--api-enable-oidc-auth", "API_ENABLE_OIDC_AUTH"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: "debug route off is silent",
|
||||||
|
api: Api{enableDebugRoute: false},
|
||||||
|
expectWarn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: "basic auth silences it",
|
||||||
|
api: Api{enableDebugRoute: true, basicAuthUsername: "foo"},
|
||||||
|
expectWarn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: "oidc silences it",
|
||||||
|
api: Api{enableDebugRoute: true, oidcEnabled: true},
|
||||||
|
expectWarn: false,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.scenario, func(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
tc.api.logger = slog.New(slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelWarn}))
|
||||||
|
|
||||||
|
tc.api.warnInsecureDebugRoute()
|
||||||
|
|
||||||
|
logged := buf.String()
|
||||||
|
if !tc.expectWarn {
|
||||||
|
if logged != "" {
|
||||||
|
t.Fatalf("expected no warning, got: %s", logged)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if logged == "" {
|
||||||
|
t.Fatal("expected a warning, got none")
|
||||||
|
}
|
||||||
|
// Every flag named must carry its environment variable.
|
||||||
|
for _, want := range tc.expectFields {
|
||||||
|
if !strings.Contains(logged, want) {
|
||||||
|
t.Fatalf("warning does not mention %q: %s", want, logged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(logged, "—") {
|
||||||
|
t.Fatalf("warning must not contain an em dash: %s", logged)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The warning reads a.logger, which is nil until Provision assigns it.
|
||||||
|
func TestApi_warnInsecureDebugRoute_NilLoggerDoesNotPanic(t *testing.T) {
|
||||||
|
api := Api{enableDebugRoute: true}
|
||||||
|
api.warnInsecureDebugRoute()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user