|
208 | 208 | import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
209 | 209 | import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
210 | 210 | import org.elasticsearch.xpack.core.security.authc.Subject;
|
| 211 | +import org.elasticsearch.xpack.core.security.authc.service.NodeLocalServiceAccountTokenStore; |
| 212 | +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; |
211 | 213 | import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
|
212 | 214 | import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
|
213 | 215 | import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
|
|
310 | 312 | import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
311 | 313 | import org.elasticsearch.xpack.security.authc.jwt.JwtRealm;
|
312 | 314 | import org.elasticsearch.xpack.security.authc.service.CachingServiceAccountTokenStore;
|
| 315 | +import org.elasticsearch.xpack.security.authc.service.CompositeServiceAccountTokenStore; |
313 | 316 | import org.elasticsearch.xpack.security.authc.service.FileServiceAccountTokenStore;
|
314 | 317 | import org.elasticsearch.xpack.security.authc.service.IndexServiceAccountTokenStore;
|
315 | 318 | import org.elasticsearch.xpack.security.authc.service.ServiceAccountService;
|
@@ -915,12 +918,34 @@ Collection<Object> createComponents(
|
915 | 918 | this.realms.set(realms);
|
916 | 919 |
|
917 | 920 | systemIndices.getMainIndexManager().addStateListener(nativeRoleMappingStore::onSecurityIndexStateChange);
|
918 |
| - |
919 | 921 | final CacheInvalidatorRegistry cacheInvalidatorRegistry = new CacheInvalidatorRegistry();
|
920 |
| - cacheInvalidatorRegistry.registerAlias("service", Set.of("file_service_account_token", "index_service_account_token")); |
921 | 922 | components.add(cacheInvalidatorRegistry);
|
922 |
| - systemIndices.getMainIndexManager().addStateListener(cacheInvalidatorRegistry::onSecurityIndexStateChange); |
923 | 923 |
|
| 924 | + ServiceAccountService serviceAccountService = createServiceAccountService( |
| 925 | + components, |
| 926 | + cacheInvalidatorRegistry, |
| 927 | + extensionComponents, |
| 928 | + () -> new IndexServiceAccountTokenStore( |
| 929 | + settings, |
| 930 | + threadPool, |
| 931 | + getClock(), |
| 932 | + client, |
| 933 | + systemIndices.getMainIndexManager(), |
| 934 | + clusterService, |
| 935 | + cacheInvalidatorRegistry |
| 936 | + ), |
| 937 | + () -> new FileServiceAccountTokenStore( |
| 938 | + environment, |
| 939 | + resourceWatcherService, |
| 940 | + threadPool, |
| 941 | + clusterService, |
| 942 | + cacheInvalidatorRegistry |
| 943 | + ) |
| 944 | + ); |
| 945 | + |
| 946 | + components.add(serviceAccountService); |
| 947 | + |
| 948 | + systemIndices.getMainIndexManager().addStateListener(cacheInvalidatorRegistry::onSecurityIndexStateChange); |
924 | 949 | final NativePrivilegeStore privilegeStore = new NativePrivilegeStore(
|
925 | 950 | settings,
|
926 | 951 | client,
|
@@ -1004,33 +1029,6 @@ Collection<Object> createComponents(
|
1004 | 1029 | );
|
1005 | 1030 | components.add(apiKeyService);
|
1006 | 1031 |
|
1007 |
| - final IndexServiceAccountTokenStore indexServiceAccountTokenStore = new IndexServiceAccountTokenStore( |
1008 |
| - settings, |
1009 |
| - threadPool, |
1010 |
| - getClock(), |
1011 |
| - client, |
1012 |
| - systemIndices.getMainIndexManager(), |
1013 |
| - clusterService, |
1014 |
| - cacheInvalidatorRegistry |
1015 |
| - ); |
1016 |
| - components.add(indexServiceAccountTokenStore); |
1017 |
| - |
1018 |
| - final FileServiceAccountTokenStore fileServiceAccountTokenStore = new FileServiceAccountTokenStore( |
1019 |
| - environment, |
1020 |
| - resourceWatcherService, |
1021 |
| - threadPool, |
1022 |
| - clusterService, |
1023 |
| - cacheInvalidatorRegistry |
1024 |
| - ); |
1025 |
| - components.add(fileServiceAccountTokenStore); |
1026 |
| - |
1027 |
| - final ServiceAccountService serviceAccountService = new ServiceAccountService( |
1028 |
| - client, |
1029 |
| - fileServiceAccountTokenStore, |
1030 |
| - indexServiceAccountTokenStore |
1031 |
| - ); |
1032 |
| - components.add(serviceAccountService); |
1033 |
| - |
1034 | 1032 | final RoleProviders roleProviders = new RoleProviders(
|
1035 | 1033 | reservedRolesStore,
|
1036 | 1034 | fileRolesStore.get(),
|
@@ -1250,6 +1248,74 @@ Collection<Object> createComponents(
|
1250 | 1248 | return components;
|
1251 | 1249 | }
|
1252 | 1250 |
|
| 1251 | + private ServiceAccountService createServiceAccountService( |
| 1252 | + List<Object> components, |
| 1253 | + CacheInvalidatorRegistry cacheInvalidatorRegistry, |
| 1254 | + SecurityExtension.SecurityComponents extensionComponents, |
| 1255 | + Supplier<IndexServiceAccountTokenStore> indexServiceAccountTokenStoreSupplier, |
| 1256 | + Supplier<FileServiceAccountTokenStore> fileServiceAccountTokenStoreSupplier |
| 1257 | + ) { |
| 1258 | + Map<String, ServiceAccountTokenStore> accountTokenStoreByExtension = new HashMap<>(); |
| 1259 | + |
| 1260 | + for (var extension : securityExtensions) { |
| 1261 | + var serviceAccountTokenStore = extension.getServiceAccountTokenStore(extensionComponents); |
| 1262 | + if (serviceAccountTokenStore != null) { |
| 1263 | + if (isInternalExtension(extension) == false) { |
| 1264 | + throw new IllegalStateException( |
| 1265 | + "The [" |
| 1266 | + + extension.getClass().getName() |
| 1267 | + + "] extension tried to install a custom ServiceAccountTokenStore. This functionality is not available to " |
| 1268 | + + "external extensions." |
| 1269 | + ); |
| 1270 | + } |
| 1271 | + accountTokenStoreByExtension.put(extension.extensionName(), serviceAccountTokenStore); |
| 1272 | + } |
| 1273 | + } |
| 1274 | + |
| 1275 | + if (accountTokenStoreByExtension.size() > 1) { |
| 1276 | + throw new IllegalStateException( |
| 1277 | + "More than one extension provided a ServiceAccountTokenStore override: " + accountTokenStoreByExtension.keySet() |
| 1278 | + ); |
| 1279 | + } |
| 1280 | + |
| 1281 | + if (accountTokenStoreByExtension.isEmpty()) { |
| 1282 | + var fileServiceAccountTokenStore = fileServiceAccountTokenStoreSupplier.get(); |
| 1283 | + var indexServiceAccountTokenStore = indexServiceAccountTokenStoreSupplier.get(); |
| 1284 | + |
| 1285 | + components.add(new PluginComponentBinding<>(NodeLocalServiceAccountTokenStore.class, fileServiceAccountTokenStore)); |
| 1286 | + components.add(fileServiceAccountTokenStore); |
| 1287 | + components.add(indexServiceAccountTokenStore); |
| 1288 | + cacheInvalidatorRegistry.registerAlias("service", Set.of("file_service_account_token", "index_service_account_token")); |
| 1289 | + |
| 1290 | + return new ServiceAccountService( |
| 1291 | + client.get(), |
| 1292 | + new CompositeServiceAccountTokenStore( |
| 1293 | + List.of(fileServiceAccountTokenStore, indexServiceAccountTokenStore), |
| 1294 | + client.get().threadPool().getThreadContext() |
| 1295 | + ), |
| 1296 | + indexServiceAccountTokenStore |
| 1297 | + ); |
| 1298 | + } |
| 1299 | + // Completely handover service account token management to the extension if provided, |
| 1300 | + // this will disable the index managed |
| 1301 | + // service account tokens managed through the service account token API |
| 1302 | + var extensionStore = accountTokenStoreByExtension.values().stream().findFirst(); |
| 1303 | + components.add(new PluginComponentBinding<>(NodeLocalServiceAccountTokenStore.class, (token, listener) -> { |
| 1304 | + throw new IllegalStateException("Node local config not supported by [" + extensionStore.get().getClass() + "]"); |
| 1305 | + })); |
| 1306 | + components.add(extensionStore); |
| 1307 | + logger.debug("Service account authentication handled by extension, disabling file and index token stores"); |
| 1308 | + return new ServiceAccountService(client.get(), extensionStore.get()); |
| 1309 | + } |
| 1310 | + |
| 1311 | + private static boolean isInternalExtension(SecurityExtension extension) { |
| 1312 | + final String canonicalName = extension.getClass().getCanonicalName(); |
| 1313 | + if (canonicalName == null) { |
| 1314 | + return false; |
| 1315 | + } |
| 1316 | + return canonicalName.startsWith("org.elasticsearch.xpack.") || canonicalName.startsWith("co.elastic.elasticsearch."); |
| 1317 | + } |
| 1318 | + |
1253 | 1319 | @FixForMultiProject
|
1254 | 1320 | // TODO : The migration task needs to be project aware
|
1255 | 1321 | private void applyPendingSecurityMigrations(ProjectId projectId, SecurityIndexManager.IndexState newState) {
|
|
0 commit comments