/*
* Copyright 2015 floragunn UG (haftungsbeschränkt)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.floragunn.searchguard.filter;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.ActionFilterChain;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.tasks.Task;
import com.floragunn.searchguard.auditlog.AuditLog;
import com.floragunn.searchguard.auth.BackendRegistry;
import com.floragunn.searchguard.configuration.AdminDNs;
import com.floragunn.searchguard.configuration.DlsFlsRequestValve;
import com.floragunn.searchguard.configuration.PrivilegesEvaluator;
import com.floragunn.searchguard.support.ConfigConstants;
import com.floragunn.searchguard.support.HeaderHelper;
import com.floragunn.searchguard.user.User;
public class SearchGuardFilter implements ActionFilter {
// "internal:*",
// "indices:monitor/*",
// "cluster:monitor/*",
// "cluster:admin/reroute",
// "indices:admin/mapping/put"
protected final ESLogger log = Loggers.getLogger(this.getClass());
private final Provider<PrivilegesEvaluator> evalp;
private final Settings settings;
private final AdminDNs adminDns;
private Provider<DlsFlsRequestValve> dlsFlsValve;
private final AuditLog auditLog;
@Inject
public SearchGuardFilter(final Settings settings, final Provider<PrivilegesEvaluator> evalp, final AdminDNs adminDns,
final Provider<BackendRegistry> backendRegistry, Provider<DlsFlsRequestValve> dlsFlsValve, AuditLog auditLog) {
this.settings = settings;
this.evalp = evalp;
this.adminDns = adminDns;
this.dlsFlsValve = dlsFlsValve;
this.auditLog = auditLog;
}
@Override
public int order() {
return Integer.MIN_VALUE;
}
@Override
public void apply(Task task, final String action, final ActionRequest request, final ActionListener listener, final ActionFilterChain chain) {
// - types testen
// - remote address testn
if (log.isTraceEnabled()) {
log.trace("Action {} from {}/{}", action, request.remoteAddress(), listener.getClass().getSimpleName());
log.trace("Context {}", request.getContext());
log.trace("Header {}", request.getHeaders());
}
User user = request.getFromContext(ConfigConstants.SG_USER);
if(user == null && request.remoteAddress() == null) {
user = User.SG_INTERNAL;
}
//LogHelper.logUserTrace("--> Action {} from {}/{}", action, request.remoteAddress(), listener.getClass().getSimpleName());
//LogHelper.logUserTrace("--> Context {}", request.getContext());
//LogHelper.logUserTrace("--> Header {}", request.getHeaders());
if (log.isTraceEnabled()) {
final Object remoteAddress = request.getFromContext(ConfigConstants.SG_REMOTE_ADDRESS);
log.trace("remote address: {}", String.valueOf(remoteAddress));
}
final boolean userIsAdmin = isUserAdmin(user, adminDns);
final boolean interClusterRequest = isInterClusterRequest(request);
final boolean conRequest = "true".equals(HeaderHelper.getSafeFromHeader(request, ConfigConstants.SG_CONF_REQUEST_HEADER));
if(userIsAdmin
|| interClusterRequest
|| conRequest){
if(userIsAdmin && !interClusterRequest && !conRequest) {
auditLog.logAuthenticatedRequest(request, action);
}
if(!dlsFlsValve.get().invoke(request, listener)) {
return;
}
chain.proceed(task, action, request, listener);
return;
}
if(User.SG_INTERNAL.equals(user)) {
//@formatter:off
if ( action.startsWith("internal:gateway")
|| action.startsWith("cluster:monitor/")
|| action.startsWith("indices:monitor/")
|| action.startsWith("cluster:admin/reroute")
|| action.startsWith("indices:admin/mapping/put")
|| action.startsWith("internal:cluster/nodes/indices/shard/store")
|| action.startsWith("indices:admin/exists")
|| action.startsWith("internal:indices/admin/upgrade")
) {
if (log.isTraceEnabled()) {
log.trace("No user, will allow only standard discovery and monitoring actions");
}
chain.proceed(task, action, request, listener);
return;
} else {
log.debug("unauthenticated request {} for user {}", action, user);
auditLog.logFailedLogin(user.getName(), request);
listener.onFailure(new ElasticsearchSecurityException("unauthenticated request "+action +" for user "+user, RestStatus.FORBIDDEN));
return;
}
//@formatter:on
}
final PrivilegesEvaluator eval = evalp.get();
if (!eval.isInitialized()) {
log.error("Search Guard not initialized (SG11) for {}", action);
listener.onFailure(new ElasticsearchSecurityException("Search Guard not initialized (SG11) for "
+ action+". See https://github.com/floragunncom/search-guard-docs/blob/master/sgadmin.md", RestStatus.SERVICE_UNAVAILABLE));
return;
}
if (log.isTraceEnabled()) {
log.trace("Evaluate permissions for user: {}", user.getName());
}
if (eval.evaluate(user, action, request)) {
auditLog.logAuthenticatedRequest(request, action);
if(!dlsFlsValve.get().invoke(request, listener)) {
return;
}
chain.proceed(task, action, request, listener);
return;
} else {
auditLog.logMissingPrivileges(action, request);
log.debug("no permissions for {}", action);
listener.onFailure(new ElasticsearchSecurityException("no permissions for " + action, RestStatus.FORBIDDEN));
return;
}
}
@Override
public void apply(final String action, final ActionResponse response, final ActionListener listener, final ActionFilterChain chain) {
chain.proceed(action, response, listener);
}
/**
*
* @param request
* @return true if request comes from a node with a server certificate
*/
private static boolean isInterClusterRequest(final ActionRequest request) {
return request.getFromContext(ConfigConstants.SG_SSL_TRANSPORT_INTERCLUSTER_REQUEST) == Boolean.TRUE;
}
/**
*
* @param request
* @return the User from header if request is InterClusterRequest and
* contains a user header, otherwise null
*/
private static boolean isUserAdmin(User user, final AdminDNs adminDns) {
if (user != null && adminDns.isAdmin(user.getName())) {
return true;
}
return false;
}
}