Skip to content

Commit 8de3a88

Browse files
authored
Log the status of security on license change (#42741)
Whether security is enabled/disabled is dependent on the combination of the node settings and the cluster license. This commit adds a license state listener that logs when the license change causes security to switch state (or to be initialised). This is primarily useful for diagnosing cluster formation issues. Backport of: #42488
1 parent 9035e61 commit 8de3a88

File tree

4 files changed

+163
-1
lines changed

4 files changed

+163
-1
lines changed

test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public UnseenEventExpectation(String name, String logger, Level level, String me
117117

118118
@Override
119119
public void assertMatched() {
120-
assertThat("expected to see " + name + " but did not", saw, equalTo(false));
120+
assertThat("expected not to see " + name + " but did", saw, equalTo(false));
121121
}
122122
}
123123

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java

+2
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@
226226
import org.elasticsearch.xpack.security.rest.action.user.RestPutUserAction;
227227
import org.elasticsearch.xpack.security.rest.action.user.RestSetEnabledAction;
228228
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
229+
import org.elasticsearch.xpack.security.support.SecurityStatusChangeListener;
229230
import org.elasticsearch.xpack.security.transport.SecurityHttpSettings;
230231
import org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor;
231232
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@@ -461,6 +462,7 @@ Collection<Object> createComponents(Client client, ThreadPool threadPool, Cluste
461462
// to keep things simple, just invalidate all cached entries on license change. this happens so rarely that the impact should be
462463
// minimal
463464
getLicenseState().addListener(allRolesStore::invalidateAll);
465+
getLicenseState().addListener(new SecurityStatusChangeListener(getLicenseState()));
464466

465467
final AuthenticationFailureHandler failureHandler = createAuthenticationFailureHandler(realms);
466468
authcService.set(new AuthenticationService(settings, realms, auditTrailService, failureHandler, threadPool,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.security.support;
8+
9+
import org.apache.logging.log4j.LogManager;
10+
import org.apache.logging.log4j.Logger;
11+
import org.elasticsearch.license.LicenseStateListener;
12+
import org.elasticsearch.license.XPackLicenseState;
13+
14+
import java.util.Objects;
15+
16+
/**
17+
* A listener for license state changes that provides log messages when a license change
18+
* causes security to switch between enable and disabled (or vice versa).
19+
*/
20+
public class SecurityStatusChangeListener implements LicenseStateListener {
21+
22+
private final Logger logger;
23+
private final XPackLicenseState licenseState;
24+
private Boolean securityEnabled;
25+
26+
public SecurityStatusChangeListener(XPackLicenseState licenseState) {
27+
this.logger = LogManager.getLogger(getClass());
28+
this.licenseState = licenseState;
29+
this.securityEnabled = null;
30+
}
31+
32+
/**
33+
* This listener will not be registered if security has been explicitly disabled, so we only need to account for dynamic changes due
34+
* to changes in the applied license.
35+
*/
36+
@Override
37+
public synchronized void licenseStateChanged() {
38+
final boolean newState = licenseState.isSecurityAvailable() && licenseState.isSecurityDisabledByLicenseDefaults() == false;
39+
// old state might be null (undefined) so do Object comparison
40+
if (Objects.equals(newState, securityEnabled) == false) {
41+
logger.info("Active license is now [{}]; Security is {}", licenseState.getOperationMode(), newState ? "enabled" : "disabled");
42+
this.securityEnabled = newState;
43+
}
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.security.support;
8+
9+
import org.apache.logging.log4j.Level;
10+
import org.apache.logging.log4j.LogManager;
11+
import org.apache.logging.log4j.Logger;
12+
import org.elasticsearch.common.logging.Loggers;
13+
import org.elasticsearch.license.License;
14+
import org.elasticsearch.license.XPackLicenseState;
15+
import org.elasticsearch.test.ESTestCase;
16+
import org.elasticsearch.test.MockLogAppender;
17+
import org.junit.After;
18+
import org.junit.Before;
19+
import org.mockito.Mockito;
20+
21+
import static org.mockito.Mockito.when;
22+
23+
public class SecurityStatusChangeListenerTests extends ESTestCase {
24+
25+
private XPackLicenseState licenseState;
26+
private SecurityStatusChangeListener listener;
27+
private MockLogAppender logAppender;
28+
private Logger listenerLogger;
29+
30+
@Before
31+
public void setup() throws IllegalAccessException {
32+
licenseState = Mockito.mock(XPackLicenseState.class);
33+
when(licenseState.isSecurityAvailable()).thenReturn(true);
34+
35+
listener = new SecurityStatusChangeListener(licenseState);
36+
37+
logAppender = new MockLogAppender();
38+
logAppender.start();
39+
listenerLogger = LogManager.getLogger(listener.getClass());
40+
Loggers.addAppender(listenerLogger, logAppender);
41+
}
42+
43+
@After
44+
public void cleanup() {
45+
Loggers.removeAppender(listenerLogger, logAppender);
46+
logAppender.stop();
47+
}
48+
49+
public void testSecurityEnabledToDisabled() {
50+
when(licenseState.isSecurityDisabledByLicenseDefaults()).thenReturn(false);
51+
52+
when(licenseState.getOperationMode()).thenReturn(License.OperationMode.GOLD);
53+
logAppender.addExpectation(new MockLogAppender.SeenEventExpectation(
54+
"initial change",
55+
listener.getClass().getName(),
56+
Level.INFO,
57+
"Active license is now [GOLD]; Security is enabled"
58+
));
59+
listener.licenseStateChanged();
60+
61+
when(licenseState.getOperationMode()).thenReturn(License.OperationMode.PLATINUM);
62+
logAppender.addExpectation(new MockLogAppender.UnseenEventExpectation(
63+
"no-op change",
64+
listener.getClass().getName(),
65+
Level.INFO,
66+
"Active license is now [PLATINUM]; Security is enabled"
67+
));
68+
69+
when(licenseState.isSecurityDisabledByLicenseDefaults()).thenReturn(true);
70+
when(licenseState.getOperationMode()).thenReturn(License.OperationMode.BASIC);
71+
logAppender.addExpectation(new MockLogAppender.SeenEventExpectation(
72+
"change to basic",
73+
listener.getClass().getName(),
74+
Level.INFO,
75+
"Active license is now [BASIC]; Security is disabled"
76+
));
77+
listener.licenseStateChanged();
78+
79+
logAppender.assertAllExpectationsMatched();
80+
}
81+
82+
public void testSecurityDisabledToEnabled() {
83+
when(licenseState.isSecurityDisabledByLicenseDefaults()).thenReturn(true);
84+
85+
when(licenseState.getOperationMode()).thenReturn(License.OperationMode.TRIAL);
86+
logAppender.addExpectation(new MockLogAppender.SeenEventExpectation(
87+
"initial change",
88+
listener.getClass().getName(),
89+
Level.INFO,
90+
"Active license is now [TRIAL]; Security is disabled"
91+
));
92+
listener.licenseStateChanged();
93+
94+
when(licenseState.getOperationMode()).thenReturn(License.OperationMode.BASIC);
95+
logAppender.addExpectation(new MockLogAppender.UnseenEventExpectation(
96+
"no-op change",
97+
listener.getClass().getName(),
98+
Level.INFO,
99+
"Active license is now [BASIC]; Security is disabled"
100+
));
101+
102+
when(licenseState.isSecurityDisabledByLicenseDefaults()).thenReturn(false);
103+
when(licenseState.getOperationMode()).thenReturn(License.OperationMode.PLATINUM);
104+
logAppender.addExpectation(new MockLogAppender.SeenEventExpectation(
105+
"change to platinum",
106+
listener.getClass().getName(),
107+
Level.INFO,
108+
"Active license is now [PLATINUM]; Security is enabled"
109+
));
110+
listener.licenseStateChanged();
111+
112+
logAppender.assertAllExpectationsMatched();
113+
}
114+
115+
}

0 commit comments

Comments
 (0)