CVE-2025-68152
Description
Juju is an open source application orchestration engine that enables any application operation on any infrastructure at any scale through special operators called ‘charms’. From versions 2.9 to before 2.9.56 and 3.6 to before 3.6.19, it is possible that a compromised workload machine under a Juju controller can read any log file for any entity in any model at any level. This issue has been patched in versions 2.9.56 and 3.6.19.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/juju/jujuGo | < 0.0.0-20250623030540-c91a1f404695 | 0.0.0-20250623030540-c91a1f404695 |
Affected products
1Patches
222cdcf6b54c2fix: ensure users uploading tools need admin permission on the model
2 files changed · +35 −13
apiserver/apiserver.go+12 −11 modified@@ -708,11 +708,12 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) { healthHandler := srv.monitoredHandler(http.HandlerFunc(srv.healthHandler), "health") logStreamHandler := srv.monitoredHandler(newLogStreamEndpointHandler(httpCtxt), "logstream") embeddedCLIHandler := srv.monitoredHandler(newEmbeddedCLIHandler(httpCtxt), "commands") + controllerAdminAuthorizer := controllerAdminAuthorizer{ + controllerTag: systemState.ControllerTag(), + } var debuglogAuth httpcontext.CompositeAuthorizer = []authentication.Authorizer{ tagKindAuthorizer{names.MachineTagKind, names.ControllerAgentTagKind}, - controllerAdminAuthorizer{ - controllerTag: systemState.ControllerTag(), - }, + controllerAdminAuthorizer, modelPermissionAuthorizer{ perm: permission.ReadAccess, }, @@ -757,9 +758,7 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) { GetHandler: modelCharmsHandler.ServeGet, }, "charms") var modelCharmsUploadAuthorizer httpcontext.CompositeAuthorizer = []authentication.Authorizer{ - controllerAdminAuthorizer{ - controllerTag: systemState.ControllerTag(), - }, + controllerAdminAuthorizer, modelPermissionAuthorizer{ perm: permission.WriteAccess, }, @@ -779,7 +778,12 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) { ctxt: httpCtxt, stateAuthFunc: httpCtxt.stateForRequestAuthenticatedUser, }, "tools") - modelToolsUploadAuthorizer := tagKindAuthorizer{names.UserTagKind} + var modelToolsUploadAuthorizer httpcontext.CompositeAuthorizer = []authentication.Authorizer{ + controllerAdminAuthorizer, + modelPermissionAuthorizer{ + perm: permission.AdminAccess, + }, + } modelToolsDownloadHandler := srv.monitoredHandler(newToolsDownloadHandler(httpCtxt), "tools") resourcesHandler := srv.monitoredHandler(&ResourcesHandler{ StateAuthFunc: func(req *http.Request, tagKinds ...string) (ResourcesBackend, state.PoolHelper, names.Tag, @@ -826,9 +830,6 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) { }, }, "units") - controllerAdminAuthorizer := controllerAdminAuthorizer{ - controllerTag: systemState.ControllerTag(), - } migrateCharmsHandler := &charmsHandler{ ctxt: httpCtxt, dataDir: srv.dataDir, @@ -983,7 +984,7 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) { }, { pattern: "/tools", handler: modelToolsUploadHandler, - authorizer: modelToolsUploadAuthorizer, + authorizer: controllerAdminAuthorizer, }, { pattern: "/tools/:version", handler: modelToolsDownloadHandler,
apiserver/tools_test.go+23 −2 modified@@ -24,6 +24,7 @@ import ( gc "gopkg.in/check.v1" apitesting "github.com/juju/juju/apiserver/testing" + "github.com/juju/juju/core/permission" "github.com/juju/juju/environs" "github.com/juju/juju/environs/simplestreams" "github.com/juju/juju/environs/storage" @@ -177,7 +178,7 @@ func (s *toolsSuite) TestRequiresPOST(c *gc.C) { s.assertJSONErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "PUT"`) } -func (s *toolsSuite) TestAuthRequiresUser(c *gc.C) { +func (s *toolsSuite) TestAuthRejectsNonsUser(c *gc.C) { // Add a machine and try to login. machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) c.Assert(err, jc.ErrorIsNil) @@ -197,14 +198,34 @@ func (s *toolsSuite) TestAuthRequiresUser(c *gc.C) { }) s.assertPlainErrorResponse( c, resp, http.StatusForbidden, - "authorization failed: tag kind machine not valid", + "authorization failed: permission denied", ) // Now try a user login. resp = s.sendHTTPRequest(c, apitesting.HTTPRequestParams{Method: "POST", URL: s.toolsURI("")}) s.assertJSONErrorResponse(c, resp, http.StatusBadRequest, "expected binaryVersion argument") } +func (s *toolsSuite) TestAuthRejectsUserWithoutPermission(c *gc.C) { + u := s.Factory.MakeUser(c, &factory.UserParams{ + Name: "oryx", + Password: "gardener", + Access: permission.WriteAccess, + }) + + resp := apitesting.SendHTTPRequest(c, apitesting.HTTPRequestParams{ + Tag: u.Tag().String(), + Password: "gardener", + Method: "POST", + URL: s.toolsURI(""), + Nonce: "fake_nonce", + }) + s.assertPlainErrorResponse( + c, resp, http.StatusForbidden, + "authorization failed: permission denied", + ) +} + func (s *toolsSuite) TestUploadRequiresVersion(c *gc.C) { resp := s.sendHTTPRequest(c, apitesting.HTTPRequestParams{Method: "POST", URL: s.toolsURI("")}) s.assertJSONErrorResponse(c, resp, http.StatusBadRequest, "expected binaryVersion argument")
c91a1f404695fix: ensure that users wanting to use debug-log via the /log
3 files changed · +41 −28
apiserver/apiserver.go+11 −6 modified@@ -48,6 +48,7 @@ import ( coredatabase "github.com/juju/juju/core/database" "github.com/juju/juju/core/lease" "github.com/juju/juju/core/multiwatcher" + "github.com/juju/juju/core/permission" "github.com/juju/juju/core/presence" "github.com/juju/juju/core/resources" "github.com/juju/juju/internal/worker/syslogger" @@ -707,15 +708,19 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) { healthHandler := srv.monitoredHandler(http.HandlerFunc(srv.healthHandler), "health") logStreamHandler := srv.monitoredHandler(newLogStreamEndpointHandler(httpCtxt), "logstream") embeddedCLIHandler := srv.monitoredHandler(newEmbeddedCLIHandler(httpCtxt), "commands") + var debuglogAuth httpcontext.CompositeAuthorizer = []authentication.Authorizer{ + tagKindAuthorizer{names.MachineTagKind, names.ControllerAgentTagKind}, + controllerAdminAuthorizer{ + controllerTag: systemState.ControllerTag(), + }, + modelPermissionAuthorizer{ + perm: permission.ReadAccess, + }, + } debugLogHandler := srv.monitoredHandler(newDebugLogDBHandler( httpCtxt, httpAuthenticator, - tagKindAuthorizer{ - names.MachineTagKind, - names.ControllerAgentTagKind, - names.UserTagKind, - names.ApplicationTagKind, - }, + debuglogAuth, ), "log") pubsubHandler := srv.monitoredHandler(newPubSubHandler(httpCtxt, srv.shared.centralHub), "pubsub") logSinkHandler := logsink.NewHTTPHandler(
apiserver/debuglog_db_test.go+18 −2 modified@@ -14,6 +14,7 @@ import ( apitesting "github.com/juju/juju/apiserver/testing" "github.com/juju/juju/apiserver/websocket/websockettest" + "github.com/juju/juju/core/permission" "github.com/juju/juju/rpc/params" "github.com/juju/juju/testing/factory" ) @@ -59,16 +60,17 @@ func (s *debugLogDBSuite) TestUnitLoginsRejected(c *gc.C) { conn, _, err := s.dialWebsocketInternal(c, nil, header) c.Assert(err, jc.ErrorIsNil) - websockettest.AssertJSONError(c, conn, "authorization failed: tag kind unit not valid") + websockettest.AssertJSONError(c, conn, "authorization failed: permission denied") websockettest.AssertWebsocketClosed(c, conn) } var noResultsPlease = url.Values{"maxLines": {"0"}, "noTail": {"true"}} -func (s *debugLogDBSuite) TestUserLoginsAccepted(c *gc.C) { +func (s *debugLogDBSuite) TestUserLoginAccepted(c *gc.C) { u := s.Factory.MakeUser(c, &factory.UserParams{ Name: "oryx", Password: "gardener", + Access: permission.ReadAccess, }) header := jujuhttp.BasicAuthHeader(u.Tag().String(), "gardener") conn, _, err := s.dialWebsocketInternal(c, noResultsPlease, header) @@ -80,6 +82,20 @@ func (s *debugLogDBSuite) TestUserLoginsAccepted(c *gc.C) { c.Assert(result.Error, gc.IsNil) } +func (s *debugLogDBSuite) TestUserLoginRejected(c *gc.C) { + u := s.Factory.MakeUser(c, &factory.UserParams{ + Name: "oryx", + Password: "gardener", + NoModelUser: true, + }) + header := jujuhttp.BasicAuthHeader(u.Tag().String(), "gardener") + conn, _, err := s.dialWebsocketInternal(c, noResultsPlease, header) + c.Assert(err, jc.ErrorIsNil) + + websockettest.AssertJSONError(c, conn, "authorization failed: permission denied") + websockettest.AssertWebsocketClosed(c, conn) +} + func (s *debugLogDBSuite) TestMachineLoginsAccepted(c *gc.C) { m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ Nonce: "foo-nonce",
go.sum+12 −20 modified@@ -18,8 +18,7 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= -cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4= cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= @@ -952,21 +951,17 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -1294,8 +1289,7 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0= -google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= +google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs= google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1349,8 +1343,7 @@ google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRx google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1372,8 +1365,7 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= -google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/juju/juju/commit/22cdcf6b54c2f371822e1c203d4f341be6c9589envdPatchWEB
- github.com/juju/juju/commit/c91a1f4046956874ba77c8b398aecee3d61a2dc3nvdPatchWEB
- github.com/advisories/GHSA-j6f6-jp3p-53mwghsaADVISORY
- github.com/juju/juju/security/advisories/GHSA-j6f6-jp3p-53mwnvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2025-68152ghsaADVISORY
News mentions
0No linked articles in our index yet.