Bug: SystemBasedRealm grants the system principal a single malformed permission, breaking broker authorization
Component: pnnl.goss.core — bundle pnnl.goss.core.security-system
File: pnnl.goss.core/src/pnnl/goss/core/security/system/SystemBasedRealm.java
Method: loadSystemAccount()
Affected versions: GOSS core 16.0.1 (the security-system bundle shipped in the GridAPPS-D 16.x release cache)
Severity: High — the embedded ActiveMQ broker rejects the system principal, so GridAPPS-D cannot start.
Summary
SystemBasedRealm.loadSystemAccount() builds the system account's permissions by
passing the entire comma-joined permission list to Shiro's singular
SimpleAccount.addStringPermission(String):
private static final String SYSTEM_PERMISSIONS =
"queue:*,topic:*,temp-queue:*,fusion:*:read,fusion:*:write";
...
SimpleAccount account = new SimpleAccount(managerUser, managerPassword, getName());
account.addStringPermission(SYSTEM_PERMISSIONS); // <-- bug
addStringPermission(String) treats its argument as one WildcardPermission.
In a Shiro wildcard permission, : separates parts and , separates
sub-values within a single part — it does not separate independent
permissions. So the string above is parsed as a single permission whose:
- part 1 =
queue and topic and temp-queue and fusion and fusion (the commas fold into one part's value set), and
- the remaining
:-delimited parts come out garbled (*, *, *, read, *, write do not line up as intended).
The result is a permission that does not imply the concrete permissions the
ActiveMQ Shiro plugin checks at runtime, e.g.
topic:ActiveMQ.Advisory.Connection:create. The broker therefore throws:
org.apache.shiro.authz.UnauthorizedException:
Subject [system] is not authorized to create destination: topic://ActiveMQ.Advisory.Connection
and the OpenWire connector never becomes usable, which in the Docker image
leaves the healthcheck (/dev/tcp/localhost/61616) failing and the container
unhealthy.
Root cause
The intent is to grant five separate permissions. Every other realm in the
same package grants permissions one per addStringPermission call — the
singular method is only ever correct for a single permission string:
GossLDAPRealm — addStringPermission("queue:*"), addStringPermission("temp-queue:*"), addStringPermission("topic:*") (three separate calls)
UnauthTokenBasedRealm — one addStringPermission(...) per concrete permission
GossAuthorizingRealm — uses the plural addStringPermissions(Collection<String>) with a collection of role permissions
Only SystemBasedRealm collapses the whole list into one string and hands it to
the singular method.
There is a second, related consequence: the same malformed single-string blob
was also stored in userPermissions:
Set<String> perms = new HashSet<>();
perms.add(SYSTEM_PERMISSIONS); // set contains ONE element: the whole joined string
userPermissions.put(managerUser, perms);
so getPermissions("system") (part of the GossRealm/PermissionAdapter
contract) returns a set with a single malformed entry rather than the five
intended permissions.
Fix
Split the constant on , into individual permission strings and grant them with
the plural addStringPermissions(Collection<String>). Store the same split
set in userPermissions so getPermissions() is also correct:
Set<String> perms = new HashSet<>(Arrays.asList(SYSTEM_PERMISSIONS.split(",")));
SimpleAccount account = new SimpleAccount(managerUser, managerPassword, getName());
account.addStringPermissions(perms);
userMap.put(managerUser, account);
userPermissions.put(managerUser, perms);
(Add import java.util.Arrays;.)
This makes SystemBasedRealm consistent with GossAuthorizingRealm, which
already uses addStringPermissions(Collection<String>).
Diff
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ loadSystemAccount()
- SimpleAccount account = new SimpleAccount(managerUser, managerPassword, getName());
- account.addStringPermission(SYSTEM_PERMISSIONS);
-
- Set<String> perms = new HashSet<>();
- perms.add(SYSTEM_PERMISSIONS);
+ Set<String> perms = new HashSet<>(Arrays.asList(SYSTEM_PERMISSIONS.split(",")));
+
+ SimpleAccount account = new SimpleAccount(managerUser, managerPassword, getName());
+ account.addStringPermissions(perms);
Reproduction
- Build the GridAPPS-D image using GOSS core
16.0.1
(pnnl.goss.core.security-system-16.0.1.jar).
- Start the container and run
./run-gridappsd.sh.
- Observe the broker log:
Subject [system] is not authorized to create destination: topic://ActiveMQ.Advisory.Connection.
- Observe that TCP port
61616 never becomes usable and the Docker healthcheck
reports unhealthy.
Verification of the fix
After rebuilding the security-system bundle with the fix and deploying it in
place of 16.0.1:
SystemBasedRealm loaded the system account for manager user 'system' on
activation (permissions now split correctly).
- Zero
Subject [system] is not authorized ... errors on a clean startup.
- OpenWire port
61616 (and STOMP 61613) open and stay open.
- The Docker container healthcheck transitions to
healthy.
- GridAPPS-D manager components (
LogManagerImpl, RoleManagerImpl,
AppManagerImpl, SimulationManagerImpl, ConfigurationManagerImpl,
TestManagerImpl) register and log as system.
Scope note (not part of this bug)
A separate set of Subject [app_user] is not authorized to create destination: topic://ActiveMQ.Advisory.Topic warnings remain for the app_user STOMP
client. app_user is served by a different realm (property-file / JWT), not by
SystemBasedRealm, and these warnings are non-fatal — the broker stays up
and the healthcheck stays healthy. They are unrelated to the
SystemBasedRealm fix above and, if undesired, should be tracked separately.
Bug:
SystemBasedRealmgrants thesystemprincipal a single malformed permission, breaking broker authorizationComponent:
pnnl.goss.core— bundlepnnl.goss.core.security-systemFile:
pnnl.goss.core/src/pnnl/goss/core/security/system/SystemBasedRealm.javaMethod:
loadSystemAccount()Affected versions: GOSS core
16.0.1(thesecurity-systembundle shipped in the GridAPPS-D16.xrelease cache)Severity: High — the embedded ActiveMQ broker rejects the
systemprincipal, so GridAPPS-D cannot start.Summary
SystemBasedRealm.loadSystemAccount()builds thesystemaccount's permissions bypassing the entire comma-joined permission list to Shiro's singular
SimpleAccount.addStringPermission(String):addStringPermission(String)treats its argument as oneWildcardPermission.In a Shiro wildcard permission,
:separates parts and,separatessub-values within a single part — it does not separate independent
permissions. So the string above is parsed as a single permission whose:
queueandtopicandtemp-queueandfusionandfusion(the commas fold into one part's value set), and:-delimited parts come out garbled (*,*,*,read,*,writedo not line up as intended).The result is a permission that does not imply the concrete permissions the
ActiveMQ Shiro plugin checks at runtime, e.g.
topic:ActiveMQ.Advisory.Connection:create. The broker therefore throws:and the OpenWire connector never becomes usable, which in the Docker image
leaves the healthcheck (
/dev/tcp/localhost/61616) failing and the containerunhealthy.Root cause
The intent is to grant five separate permissions. Every other realm in the
same package grants permissions one per
addStringPermissioncall — thesingular method is only ever correct for a single permission string:
GossLDAPRealm—addStringPermission("queue:*"),addStringPermission("temp-queue:*"),addStringPermission("topic:*")(three separate calls)UnauthTokenBasedRealm— oneaddStringPermission(...)per concrete permissionGossAuthorizingRealm— uses the pluraladdStringPermissions(Collection<String>)with a collection of role permissionsOnly
SystemBasedRealmcollapses the whole list into one string and hands it tothe singular method.
There is a second, related consequence: the same malformed single-string blob
was also stored in
userPermissions:so
getPermissions("system")(part of theGossRealm/PermissionAdaptercontract) returns a set with a single malformed entry rather than the five
intended permissions.
Fix
Split the constant on
,into individual permission strings and grant them withthe plural
addStringPermissions(Collection<String>). Store the same splitset in
userPermissionssogetPermissions()is also correct:(Add
import java.util.Arrays;.)This makes
SystemBasedRealmconsistent withGossAuthorizingRealm, whichalready uses
addStringPermissions(Collection<String>).Diff
Reproduction
16.0.1(
pnnl.goss.core.security-system-16.0.1.jar)../run-gridappsd.sh.Subject [system] is not authorized to create destination: topic://ActiveMQ.Advisory.Connection.61616never becomes usable and the Docker healthcheckreports
unhealthy.Verification of the fix
After rebuilding the
security-systembundle with the fix and deploying it inplace of
16.0.1:SystemBasedRealm loaded the system account for manager user 'system'onactivation (permissions now split correctly).
Subject [system] is not authorized ...errors on a clean startup.61616(and STOMP61613) open and stay open.healthy.LogManagerImpl,RoleManagerImpl,AppManagerImpl,SimulationManagerImpl,ConfigurationManagerImpl,TestManagerImpl) register and log assystem.Scope note (not part of this bug)
A separate set of
Subject [app_user] is not authorized to create destination: topic://ActiveMQ.Advisory.Topicwarnings remain for theapp_userSTOMPclient.
app_useris served by a different realm (property-file / JWT), not bySystemBasedRealm, and these warnings are non-fatal — the broker stays upand the healthcheck stays
healthy. They are unrelated to theSystemBasedRealmfix above and, if undesired, should be tracked separately.