Skip to content

Commit b8fac3a

Browse files
committed
Preserve request headers in a mixed version cluster
When rewriting authentication for requests crossing nodes of different versions, we now preserve all request headers except the authentication one which needs to be rewritten. Previously all other request headers were dropped and it caused issue like an operator user not being recognised on the remote node. Other now preserved headers include audit and system index access. This new behaviour is more correct because we would never drop these headers if the nodes are on the same version. Resolves: elastic#79354
1 parent d7517d0 commit b8fac3a

File tree

5 files changed

+25
-24
lines changed

5 files changed

+25
-24
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.node.Node;
2222
import org.elasticsearch.xpack.core.security.authc.Authentication;
2323
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
24+
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
2425
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
2526
import org.elasticsearch.xpack.core.security.authc.support.SecondaryAuthentication;
2627
import org.elasticsearch.xpack.core.security.user.User;
@@ -157,12 +158,19 @@ public <T> T executeWithAuthentication(Authentication authentication, Function<S
157158
* The original context is provided to the consumer. When this method returns, the original context is restored.
158159
*/
159160
public void executeAfterRewritingAuthentication(Consumer<StoredContext> consumer, Version version) {
161+
// Preserve request headers other than authentication
162+
final Map<String, String> existingRequestHeaders = threadContext.getRequestHeadersOnly();
160163
final StoredContext original = threadContext.newStoredContext(true);
161164
final Authentication authentication = getAuthentication();
162165
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
163166
setAuthentication(new Authentication(authentication.getUser(), authentication.getAuthenticatedBy(),
164167
authentication.getLookedUpBy(), version, authentication.getAuthenticationType(),
165168
rewriteMetadataForApiKeyRoleDescriptors(version, authentication)));
169+
existingRequestHeaders.forEach((k, v) -> {
170+
if (false == AuthenticationField.AUTHENTICATION_KEY.equals(k)) {
171+
threadContext.putHeader(k, v);
172+
}
173+
});
166174
consumer.accept(original);
167175
}
168176
}

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityContextTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
2222
import org.elasticsearch.xpack.core.security.user.SystemUser;
2323
import org.elasticsearch.xpack.core.security.user.User;
24+
import org.elasticsearch.xpack.security.audit.AuditUtil;
2425
import org.junit.Before;
2526

2627
import java.io.EOFException;
@@ -33,6 +34,7 @@
3334
import static org.elasticsearch.xpack.core.security.authc.Authentication.VERSION_API_KEY_ROLES_AS_BYTES;
3435
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY;
3536
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY;
37+
import static org.hamcrest.Matchers.equalTo;
3638
import static org.hamcrest.Matchers.instanceOf;
3739

3840
public class SecurityContextTests extends ESTestCase {
@@ -119,6 +121,11 @@ public void testExecuteAfterRewritingAuthentication() throws IOException {
119121
RealmRef authBy = new RealmRef("ldap", "foo", "node1");
120122
final Authentication original = new Authentication(user, authBy, authBy);
121123
original.writeToContext(threadContext);
124+
final Map<String, String> requestHeaders = Map.of(
125+
AuthenticationField.PRIVILEGE_CATEGORY_KEY, randomAlphaOfLengthBetween(3, 10),
126+
randomAlphaOfLengthBetween(3, 8), randomAlphaOfLengthBetween(3, 8)
127+
);
128+
threadContext.putHeader(requestHeaders);
122129

123130
final AtomicReference<StoredContext> contextAtomicReference = new AtomicReference<>();
124131
securityContext.executeAfterRewritingAuthentication(originalCtx -> {
@@ -129,6 +136,8 @@ public void testExecuteAfterRewritingAuthentication() throws IOException {
129136
assertEquals(VersionUtils.getPreviousVersion(), authentication.getVersion());
130137
assertEquals(original.getAuthenticationType(), securityContext.getAuthentication().getAuthenticationType());
131138
contextAtomicReference.set(originalCtx);
139+
// Other request headers should be preserved
140+
requestHeaders.forEach((k, v) -> assertThat(threadContext.getHeader(k), equalTo(v)));
132141
}, VersionUtils.getPreviousVersion());
133142

134143
final Authentication authAfterExecution = securityContext.getAuthentication();

x-pack/qa/rolling-upgrade/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ BuildParams.bwcVersions.withWireCompatiple { bwcVersion, baseName ->
6868
}
6969
if (bwcVersion.onOrAfter('7.11.0')) {
7070
extraConfigFile 'operator_users.yml', file("${project.projectDir}/src/test/resources/operator_users.yml")
71-
// setting 'xpack.security.operator_privileges.enabled', "true"
71+
setting 'xpack.security.operator_privileges.enabled', "true"
7272
user username: "non_operator", password: 'x-pack-test-password', role: "superuser"
7373
}
7474

@@ -136,7 +136,8 @@ BuildParams.bwcVersions.withWireCompatiple { bwcVersion, baseName ->
136136
'mixed_cluster/90_ml_data_frame_analytics_crud/Put an outlier_detection job on the mixed cluster',
137137
'mixed_cluster/110_enrich/Enrich stats query smoke test for mixed cluster',
138138
'mixed_cluster/120_api_key/Test API key authentication will work in a mixed cluster',
139-
'mixed_cluster/120_api_key/Create API key with metadata in a mixed cluster'
139+
'mixed_cluster/120_api_key/Create API key with metadata in a mixed cluster',
140+
'mixed_cluster/130_operator_privileges/Test operator pivileges will work in the mixed cluster'
140141
].join(',')
141142
}
142143

x-pack/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/130_operator_privileges.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
- do:
1010
cluster.delete_voting_config_exclusions: { }
1111

12-
# - do:
13-
# catch: forbidden
14-
# headers: # the non_operator user
15-
# Authorization: Basic bm9uX29wZXJhdG9yOngtcGFjay10ZXN0LXBhc3N3b3Jk
16-
# cluster.delete_voting_config_exclusions: { }
12+
- do:
13+
catch: forbidden
14+
headers: # the non_operator user
15+
Authorization: Basic bm9uX29wZXJhdG9yOngtcGFjay10ZXN0LXBhc3N3b3Jk
16+
cluster.delete_voting_config_exclusions: { }

x-pack/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/130_operator_privileges.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)