9
9
import org .elasticsearch .Version ;
10
10
import org .elasticsearch .action .ActionListener ;
11
11
import org .elasticsearch .action .support .DestructiveOperations ;
12
+ import org .elasticsearch .cluster .service .ClusterService ;
12
13
import org .elasticsearch .common .CheckedConsumer ;
13
14
import org .elasticsearch .common .component .AbstractComponent ;
14
15
import org .elasticsearch .common .settings .Setting ;
15
16
import org .elasticsearch .common .settings .Settings ;
16
17
import org .elasticsearch .common .util .concurrent .AbstractRunnable ;
17
18
import org .elasticsearch .common .util .concurrent .ThreadContext ;
19
+ import org .elasticsearch .gateway .GatewayService ;
18
20
import org .elasticsearch .license .XPackLicenseState ;
19
21
import org .elasticsearch .tasks .Task ;
20
22
import org .elasticsearch .threadpool .ThreadPool ;
@@ -72,14 +74,17 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
72
74
private final SecurityContext securityContext ;
73
75
private final boolean reservedRealmEnabled ;
74
76
77
+ private volatile boolean isStateNotRecovered = true ;
78
+
75
79
public SecurityServerTransportInterceptor (Settings settings ,
76
80
ThreadPool threadPool ,
77
81
AuthenticationService authcService ,
78
82
AuthorizationService authzService ,
79
83
XPackLicenseState licenseState ,
80
84
SSLService sslService ,
81
85
SecurityContext securityContext ,
82
- DestructiveOperations destructiveOperations ) {
86
+ DestructiveOperations destructiveOperations ,
87
+ ClusterService clusterService ) {
83
88
super (settings );
84
89
this .settings = settings ;
85
90
this .threadPool = threadPool ;
@@ -90,6 +95,7 @@ public SecurityServerTransportInterceptor(Settings settings,
90
95
this .securityContext = securityContext ;
91
96
this .profileFilters = initializeProfileFilters (destructiveOperations );
92
97
this .reservedRealmEnabled = XPackSettings .RESERVED_REALM_ENABLED_SETTING .get (settings );
98
+ clusterService .addListener (e -> isStateNotRecovered = e .state ().blocks ().hasGlobalBlock (GatewayService .STATE_NOT_RECOVERED_BLOCK ));
93
99
}
94
100
95
101
@ Override
@@ -98,7 +104,13 @@ public AsyncSender interceptSender(AsyncSender sender) {
98
104
@ Override
99
105
public <T extends TransportResponse > void sendRequest (Transport .Connection connection , String action , TransportRequest request ,
100
106
TransportRequestOptions options , TransportResponseHandler <T > handler ) {
101
- if (licenseState .isSecurityEnabled () && licenseState .isAuthAllowed ()) {
107
+ // make a local copy of isStateNotRecovered as this is a volatile variable and it
108
+ // is used multiple times in the method. The copy to a local variable allows us to
109
+ // guarantee we use the same value wherever we would check the value for the state
110
+ // being recovered
111
+ final boolean stateNotRecovered = isStateNotRecovered ;
112
+ final boolean sendWithAuth = (licenseState .isSecurityEnabled () && licenseState .isAuthAllowed ()) || stateNotRecovered ;
113
+ if (sendWithAuth ) {
102
114
// the transport in core normally does this check, BUT since we are serializing to a string header we need to do it
103
115
// ourselves otherwise we wind up using a version newer than what we can actually send
104
116
final Version minVersion = Version .min (connection .getVersion (), Version .CURRENT );
@@ -108,20 +120,20 @@ public <T extends TransportResponse> void sendRequest(Transport.Connection conne
108
120
if (AuthorizationUtils .shouldReplaceUserWithSystem (threadPool .getThreadContext (), action )) {
109
121
securityContext .executeAsUser (SystemUser .INSTANCE , (original ) -> sendWithUser (connection , action , request , options ,
110
122
new ContextRestoreResponseHandler <>(threadPool .getThreadContext ().wrapRestorable (original )
111
- , handler ), sender ), minVersion );
123
+ , handler ), sender , stateNotRecovered ), minVersion );
112
124
} else if (AuthorizationUtils .shouldSetUserBasedOnActionOrigin (threadPool .getThreadContext ())) {
113
125
AuthorizationUtils .switchUserBasedOnActionOriginAndExecute (threadPool .getThreadContext (), securityContext ,
114
126
(original ) -> sendWithUser (connection , action , request , options ,
115
127
new ContextRestoreResponseHandler <>(threadPool .getThreadContext ().wrapRestorable (original )
116
- , handler ), sender ));
128
+ , handler ), sender , stateNotRecovered ));
117
129
} else if (securityContext .getAuthentication () != null &&
118
130
securityContext .getAuthentication ().getVersion ().equals (minVersion ) == false ) {
119
131
// re-write the authentication since we want the authentication version to match the version of the connection
120
132
securityContext .executeAfterRewritingAuthentication (original -> sendWithUser (connection , action , request , options ,
121
- new ContextRestoreResponseHandler <>(threadPool .getThreadContext ().wrapRestorable (original ), handler ), sender ) ,
122
- minVersion );
133
+ new ContextRestoreResponseHandler <>(threadPool .getThreadContext ().wrapRestorable (original ), handler ), sender ,
134
+ stateNotRecovered ), minVersion );
123
135
} else {
124
- sendWithUser (connection , action , request , options , handler , sender );
136
+ sendWithUser (connection , action , request , options , handler , sender , stateNotRecovered );
125
137
}
126
138
} else {
127
139
sender .sendRequest (connection , action , request , options , handler );
@@ -132,9 +144,10 @@ public <T extends TransportResponse> void sendRequest(Transport.Connection conne
132
144
133
145
private <T extends TransportResponse > void sendWithUser (Transport .Connection connection , String action , TransportRequest request ,
134
146
TransportRequestOptions options , TransportResponseHandler <T > handler ,
135
- AsyncSender sender ) {
136
- // There cannot be a request outgoing from this node that is not associated with a user.
137
- if (securityContext .getAuthentication () == null ) {
147
+ AsyncSender sender , final boolean stateNotRecovered ) {
148
+ // There cannot be a request outgoing from this node that is not associated with a user
149
+ // unless we do not know the actual license of the cluster
150
+ if (securityContext .getAuthentication () == null && stateNotRecovered == false ) {
138
151
// we use an assertion here to ensure we catch this in our testing infrastructure, but leave the ISE for cases we do not catch
139
152
// in tests and may be hit by a user
140
153
assertNoAuthentication (action );
0 commit comments