Skip to content

Commit

Permalink
First shot at optimized DLS/FLS
Browse files Browse the repository at this point in the history
Signed-off-by: Nils Bandener <[email protected]>
  • Loading branch information
nibix committed Aug 7, 2024
1 parent 0252f5b commit d104c38
Show file tree
Hide file tree
Showing 25 changed files with 2,915 additions and 900 deletions.
41 changes: 26 additions & 15 deletions src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
import org.opensearch.security.privileges.PrivilegesEvaluator;
import org.opensearch.security.privileges.PrivilegesInterceptor;
import org.opensearch.security.privileges.RestLayerPrivilegesEvaluator;
import org.opensearch.security.privileges.dlsfls.DlsFlsBaseContext;
import org.opensearch.security.resolver.IndexResolverReplacer;
import org.opensearch.security.rest.DashboardsInfoAction;
import org.opensearch.security.rest.SecurityConfigUpdateAction;
Expand All @@ -177,6 +178,9 @@
import org.opensearch.security.rest.SecurityWhoAmIAction;
import org.opensearch.security.rest.TenantInfoAction;
import org.opensearch.security.securityconf.DynamicConfigFactory;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration;
import org.opensearch.security.securityconf.impl.v7.RoleV7;
import org.opensearch.security.setting.OpensearchDynamicSetting;
import org.opensearch.security.setting.TransportPassiveAuthSetting;
import org.opensearch.security.ssl.ExternalSecurityKeyStore;
Expand Down Expand Up @@ -263,9 +267,9 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin
private volatile IndexResolverReplacer irr;
private final AtomicReference<NamedXContentRegistry> namedXContentRegistry = new AtomicReference<>(NamedXContentRegistry.EMPTY);;
private volatile DlsFlsRequestValve dlsFlsValve = null;
private volatile Salt salt;
private volatile OpensearchDynamicSetting<Boolean> transportPassiveAuthSetting;
private volatile PasswordHasher passwordHasher;
private volatile DlsFlsBaseContext dlsFlsBaseContext;

public static boolean isActionTraceEnabled() {

Expand Down Expand Up @@ -702,7 +706,8 @@ public void onIndexModule(IndexModule indexModule) {
auditLog,
ciol,
evaluator,
salt
dlsFlsValve::getCurrentConfig,
dlsFlsBaseContext
)
);
indexModule.forceQueryCacheProvider((indexSettings, nodeCache) -> new QueryCache() {
Expand Down Expand Up @@ -1062,7 +1067,6 @@ public Collection<Object> createComponents(

final ClusterInfoHolder cih = new ClusterInfoHolder(this.cs.getClusterName().value());
this.cs.addListener(cih);
this.salt = Salt.from(settings);

final IndexNameExpressionResolver resolver = new IndexNameExpressionResolver(threadPool.getThreadContext());
irr = new IndexResolverReplacer(resolver, clusterService::state, cih);
Expand All @@ -1083,18 +1087,9 @@ public Collection<Object> createComponents(

namedXContentRegistry.set(xContentRegistry);
if (SSLConfig.isSslOnlyMode()) {
dlsFlsValve = new DlsFlsRequestValve.NoopDlsFlsRequestValve();
auditLog = new NullAuditLog();
privilegesInterceptor = new PrivilegesInterceptor(resolver, clusterService, localClient, threadPool);
} else {
dlsFlsValve = new DlsFlsValveImpl(
settings,
localClient,
clusterService,
resolver,
xContentRegistry,
threadPool.getThreadContext()
);
auditLog = new AuditLogImpl(settings, configPath, localClient, threadPool, resolver, clusterService, environment);
privilegesInterceptor = new PrivilegesInterceptorImpl(resolver, clusterService, localClient, threadPool);
}
Expand Down Expand Up @@ -1129,6 +1124,25 @@ public Collection<Object> createComponents(
namedXContentRegistry.get()
);

dlsFlsBaseContext = new DlsFlsBaseContext(evaluator, threadPool.getThreadContext());

if (SSLConfig.isSslOnlyMode()) {
dlsFlsValve = new DlsFlsRequestValve.NoopDlsFlsRequestValve();
} else {
dlsFlsValve = new DlsFlsValveImpl(
settings,
localClient,
clusterService,
resolver,
xContentRegistry,
threadPool.getThreadContext(),
dlsFlsBaseContext
);
cr.subscribeOnChange(configMap -> {
((DlsFlsValveImpl) dlsFlsValve).updateConfiguration(cr.getConfiguration(CType.ROLES));
});
}

sf = new SecurityFilter(settings, evaluator, adminDns, dlsFlsValve, auditLog, threadPool, cs, compatConfig, irr, xffResolver);

final String principalExtractorClass = settings.get(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PRINCIPAL_EXTRACTOR_CLASS, null);
Expand Down Expand Up @@ -1163,9 +1177,6 @@ public Collection<Object> createComponents(
// Don't register if advanced modules is disabled in which case auditlog is instance of NullAuditLog
dcf.registerDCFListener(auditLog);
}
if (dlsFlsValve instanceof DlsFlsValveImpl) {
dcf.registerDCFListener(dlsFlsValve);
}

cr.setDynamicConfigFactory(dcf);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.opensearch.index.mapper.Uid;
import org.opensearch.security.auditlog.AuditLog;
import org.opensearch.security.dlic.rest.support.Utils;
import org.opensearch.security.privileges.dlsfls.FieldMasking;
import org.opensearch.security.support.HeaderHelper;
import org.opensearch.security.support.JsonFlattener;
import org.opensearch.security.support.SourceFieldsContext;
Expand All @@ -49,7 +50,7 @@ public final class FieldReadCallback {
// private final ThreadContext threadContext;
// private final ClusterService clusterService;
private final Index index;
private final WildcardMatcher maskedFieldsMatcher;
private final FieldMasking.FieldMaskingRule fmRule;
private final AuditLog auditLog;
private Function<Map<String, ?>, Map<String, Object>> filterFunction;
private SourceFieldsContext sfc;
Expand All @@ -61,15 +62,15 @@ public FieldReadCallback(
final IndexService indexService,
final ClusterService clusterService,
final AuditLog auditLog,
final WildcardMatcher maskedFieldsMatcher,
final FieldMasking.FieldMaskingRule fmRule,
ShardId shardId
) {
super();
// this.threadContext = Objects.requireNonNull(threadContext);
// this.clusterService = Objects.requireNonNull(clusterService);
this.index = Objects.requireNonNull(indexService).index();
this.auditLog = auditLog;
this.maskedFieldsMatcher = maskedFieldsMatcher;
this.fmRule = fmRule;
this.shardId = shardId;
try {
sfc = (SourceFieldsContext) HeaderHelper.deserializeSafeFromHeader(threadContext, "_opendistro_security_source_field_context");
Expand All @@ -88,7 +89,8 @@ public FieldReadCallback(
}

private boolean recordField(final String fieldName, boolean isStringField) {
return !(isStringField && maskedFieldsMatcher.test(fieldName))
// We do not record fields in read history if they are masked.
return !(isStringField && fmRule.isMasked(fieldName))
&& auditLog.getComplianceConfig().readHistoryEnabledForField(index.getName(), fieldName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ public void singleFailure(Failure failure) {
"Failure {} retrieving configuration for {} (index={})",
failure == null ? null : failure.getMessage(),
Arrays.toString(events),
securityIndex
securityIndex,
failure.getFailure()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
import org.opensearch.search.builder.SearchSourceBuilder;
import org.opensearch.security.privileges.DocumentAllowList;
import org.opensearch.security.privileges.PrivilegesEvaluationContext;
import org.opensearch.security.privileges.dlsfls.DlsRestriction;
import org.opensearch.security.privileges.dlsfls.IndexToRuleMap;
import org.opensearch.security.queries.QueryBuilderTraverser;
import org.opensearch.security.resolver.IndexResolverReplacer.Resolved;
import org.opensearch.security.securityconf.EvaluatedDlsFlsConfig;
Expand All @@ -76,7 +78,7 @@ public class DlsFilterLevelActionHandler {

public static boolean handle(
PrivilegesEvaluationContext context,
EvaluatedDlsFlsConfig evaluatedDlsFlsConfig,
IndexToRuleMap<DlsRestriction> dlsRestrictionMap,
ActionListener<?> listener,
Client nodeClient,
ClusterService clusterService,
Expand Down Expand Up @@ -115,7 +117,7 @@ public static boolean handle(

return new DlsFilterLevelActionHandler(
context,
evaluatedDlsFlsConfig,
dlsRestrictionMap,
listener,
nodeClient,
clusterService,
Expand All @@ -129,7 +131,7 @@ public static boolean handle(
private final String action;
private final ActionRequest request;
private final ActionListener<?> listener;
private final EvaluatedDlsFlsConfig evaluatedDlsFlsConfig;
private final IndexToRuleMap<DlsRestriction> dlsRestrictionMap;
private final Resolved resolved;
private final boolean requiresIndexScoping;
private final Client nodeClient;
Expand All @@ -143,7 +145,7 @@ public static boolean handle(

DlsFilterLevelActionHandler(
PrivilegesEvaluationContext context,
EvaluatedDlsFlsConfig evaluatedDlsFlsConfig,
IndexToRuleMap<DlsRestriction> dlsRestrictionMap,
ActionListener<?> listener,
Client nodeClient,
ClusterService clusterService,
Expand All @@ -155,7 +157,7 @@ public static boolean handle(
this.action = context.getAction();
this.request = context.getRequest();
this.listener = listener;
this.evaluatedDlsFlsConfig = evaluatedDlsFlsConfig;
this.dlsRestrictionMap = dlsRestrictionMap;
this.resolved = context.getResolvedRequest();
this.nodeClient = nodeClient;
this.clusterService = clusterService;
Expand Down Expand Up @@ -464,7 +466,7 @@ private boolean modifyQuery() throws IOException {
}

private boolean modifyQuery(String localClusterAlias) throws IOException {
Map<String, Set<String>> filterLevelQueries = evaluatedDlsFlsConfig.getDlsQueriesByIndex();
Map<String, DlsRestriction> filterLevelQueries = dlsRestrictionMap.getIndexMap();

BoolQueryBuilder dlsQueryBuilder = QueryBuilders.boolQuery().minimumShouldMatch(1);
DocumentAllowList documentAllowlist = new DocumentAllowList();
Expand All @@ -474,8 +476,6 @@ private boolean modifyQuery(String localClusterAlias) throws IOException {
Set<String> indices = resolved.getAllIndicesResolved(clusterService, resolver);

for (String index : indices) {
String dlsEval = SecurityUtils.evalMap(filterLevelQueries, index);

String prefixedIndex;

if (localClusterAlias != null) {
Expand All @@ -484,18 +484,9 @@ private boolean modifyQuery(String localClusterAlias) throws IOException {
prefixedIndex = index;
}

if (dlsEval == null) {
if (requiresIndexScoping) {
// This index has no DLS configured, thus it is unrestricted.
// To allow the index in a complex query, we need to add the query below to let the index pass.
dlsQueryBuilder.should(QueryBuilders.termQuery("_index", prefixedIndex));
}
continue;
}

Set<String> unparsedDlsQueries = filterLevelQueries.get(dlsEval);
DlsRestriction dlsRestriction = filterLevelQueries.get(index);

if (unparsedDlsQueries == null || unparsedDlsQueries.isEmpty()) {
if (dlsRestriction == null || dlsRestriction.isUnrestricted()) {
if (requiresIndexScoping) {
// This index has no DLS configured, thus it is unrestricted.
// To allow the index in a complex query, we need to add the query below to let the index pass.
Expand All @@ -504,11 +495,9 @@ private boolean modifyQuery(String localClusterAlias) throws IOException {
continue;
}

for (String unparsedDlsQuery : unparsedDlsQueries) {
for (QueryBuilder parsedDlsQuery : dlsRestriction.getQueries()) {
queryCount++;

QueryBuilder parsedDlsQuery = dlsQueryParser.parse(unparsedDlsQuery);

if (!requiresIndexScoping) {
dlsQueryBuilder.should(parsedDlsQuery);
} else {
Expand Down
Loading

0 comments on commit d104c38

Please sign in to comment.