From 0717578b06cc2a59f0662064aa8aacfa7fbdc66b Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Fri, 14 Nov 2025 21:30:42 +0000 Subject: [PATCH] change!(server): auth is now required for all routes --- cmd/gluetun/main.go | 2 +- .../server/middlewares/auth/middleware.go | 55 ++----------------- .../middlewares/auth/middleware_test.go | 21 ------- internal/server/middlewares/auth/settings.go | 25 --------- internal/server/server.go | 1 - 5 files changed, 5 insertions(+), 99 deletions(-) diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index cb868dfd..2bb5576e 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -175,7 +175,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, Version: buildInfo.Version, Commit: buildInfo.Commit, Created: buildInfo.Created, - Announcement: "All control server routes will become private by default after the v3.41.0 release", + Announcement: "All control server routes are now private by default", AnnounceExp: announcementExp, // Sponsor information PaypalUser: "qmcgaw", diff --git a/internal/server/middlewares/auth/middleware.go b/internal/server/middlewares/auth/middleware.go index 39e79d6a..b2bf5d06 100644 --- a/internal/server/middlewares/auth/middleware.go +++ b/internal/server/middlewares/auth/middleware.go @@ -18,37 +18,15 @@ func New(settings Settings, debugLogger DebugLogger) ( return &authHandler{ childHandler: handler, routeToRoles: routeToRoles, - unprotectedRoutes: map[string]struct{}{ - http.MethodGet + " /openvpn/actions/restart": {}, - http.MethodGet + " /openvpn/portforwarded": {}, - http.MethodGet + " /unbound/actions/restart": {}, - http.MethodGet + " /updater/restart": {}, - http.MethodGet + " /v1/version": {}, - http.MethodGet + " /v1/vpn/status": {}, - http.MethodPut + " /v1/vpn/status": {}, - // GET /v1/vpn/settings is protected by default - // PUT /v1/vpn/settings is protected by default - http.MethodGet + " /v1/openvpn/status": {}, - http.MethodPut + " /v1/openvpn/status": {}, - http.MethodGet + " /v1/openvpn/portforwarded": {}, - // GET /v1/openvpn/settings is protected by default - http.MethodGet + " /v1/dns/status": {}, - http.MethodPut + " /v1/dns/status": {}, - http.MethodGet + " /v1/updater/status": {}, - http.MethodPut + " /v1/updater/status": {}, - http.MethodGet + " /v1/publicip/ip": {}, - http.MethodGet + " /v1/portforward": {}, - }, - logger: debugLogger, + logger: debugLogger, } }, nil } type authHandler struct { - childHandler http.Handler - routeToRoles map[string][]internalRole - unprotectedRoutes map[string]struct{} // TODO v3.41.0 remove - logger DebugLogger + childHandler http.Handler + routeToRoles map[string][]internalRole + logger DebugLogger } func (h *authHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { @@ -66,8 +44,6 @@ func (h *authHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques continue } - h.warnIfUnprotectedByDefault(role, route) // TODO v3.41.0 remove - h.logger.Debugf("access to route %s authorized for role %s", route, role.name) h.childHandler.ServeHTTP(writer, request) return @@ -88,26 +64,3 @@ func (h *authHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques route, andStrings(allRoleNames)) http.Error(writer, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) } - -func (h *authHandler) warnIfUnprotectedByDefault(role internalRole, route string) { - // TODO v3.41.0 remove - if role.name != "public" { - // custom role name, allow none authentication to be specified - return - } - _, isNoneChecker := role.checker.(*noneMethod) - if !isNoneChecker { - // not the none authentication method - return - } - _, isUnprotectedByDefault := h.unprotectedRoutes[route] - if !isUnprotectedByDefault { - // route is not unprotected by default, so this is a user decision - return - } - h.logger.Warnf("route %s is unprotected by default, "+ - "please set up authentication following the documentation at "+ - "https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication "+ - "since this will become no longer publicly accessible after release v3.40.", - route) -} diff --git a/internal/server/middlewares/auth/middleware_test.go b/internal/server/middlewares/auth/middleware_test.go index 2c039d75..ae731da2 100644 --- a/internal/server/middlewares/auth/middleware_test.go +++ b/internal/server/middlewares/auth/middleware_test.go @@ -40,27 +40,6 @@ func Test_authHandler_ServeHTTP(t *testing.T) { statusCode: http.StatusUnauthorized, responseBody: "Unauthorized\n", }, - "authorized_unprotected_by_default": { - settings: Settings{ - Roles: []Role{ - {Name: "public", Auth: AuthNone, Routes: []string{"GET /v1/vpn/status"}}, - }, - }, - makeLogger: func(ctrl *gomock.Controller) *MockDebugLogger { - logger := NewMockDebugLogger(ctrl) - logger.EXPECT().Warnf("route %s is unprotected by default, "+ - "please set up authentication following the documentation at "+ - "https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication "+ - "since this will become no longer publicly accessible after release v3.40.", - "GET /v1/vpn/status") - logger.EXPECT().Debugf("access to route %s authorized for role %s", - "GET /v1/vpn/status", "public") - return logger - }, - requestMethod: http.MethodGet, - requestPath: "/v1/vpn/status", - statusCode: http.StatusOK, - }, "authorized_none": { settings: Settings{ Roles: []Role{ diff --git a/internal/server/middlewares/auth/settings.go b/internal/server/middlewares/auth/settings.go index 5e08385b..75b48fde 100644 --- a/internal/server/middlewares/auth/settings.go +++ b/internal/server/middlewares/auth/settings.go @@ -63,31 +63,6 @@ func (s *Settings) SetDefaultRole(jsonRole string) error { return nil } -func (s *Settings) SetDefaults() { - s.Roles = gosettings.DefaultSlice(s.Roles, []Role{{ // TODO v3.41.0 leave empty - Name: "public", - Auth: "none", - Routes: []string{ - http.MethodGet + " /openvpn/actions/restart", - http.MethodGet + " /unbound/actions/restart", - http.MethodGet + " /openvpn/portforwarded", - http.MethodGet + " /updater/restart", - http.MethodGet + " /v1/version", - http.MethodGet + " /v1/vpn/status", - http.MethodPut + " /v1/vpn/status", - http.MethodGet + " /v1/openvpn/status", - http.MethodPut + " /v1/openvpn/status", - http.MethodGet + " /v1/openvpn/portforwarded", - http.MethodGet + " /v1/dns/status", - http.MethodPut + " /v1/dns/status", - http.MethodGet + " /v1/updater/status", - http.MethodPut + " /v1/updater/status", - http.MethodGet + " /v1/publicip/ip", - http.MethodGet + " /v1/portforward", - }, - }}) -} - func (s Settings) Validate() (err error) { for i, role := range s.Roles { err = role.Validate() diff --git a/internal/server/server.go b/internal/server/server.go index bf18bba9..55b41548 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -60,7 +60,6 @@ func setupAuthMiddleware(authPath, jsonDefaultRole string, logger Logger) ( if err != nil { return auth.Settings{}, fmt.Errorf("setting default role: %w", err) } - authSettings.SetDefaults() err = authSettings.Validate() if err != nil { return auth.Settings{}, fmt.Errorf("validating auth settings: %w", err)