Compare commits

...

1 Commits

Author SHA1 Message Date
Quentin McGaw
0717578b06 change!(server): auth is now required for all routes 2025-11-14 21:30:42 +00:00
5 changed files with 5 additions and 99 deletions

View File

@@ -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",

View File

@@ -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)
}

View File

@@ -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{

View File

@@ -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()

View File

@@ -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)