mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-25 22:36:20 +00:00
remove signing of authentication information
Now that TLS is required for node to node communication, we no longer need to sign the authentication information to prevent tampering. Original commit: elastic/x-pack-elasticsearch@1f86cf2395
This commit is contained in:
parent
b3d72af644
commit
c8b5be186d
@ -245,7 +245,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
|||||||
List<Object> components = new ArrayList<>();
|
List<Object> components = new ArrayList<>();
|
||||||
components.add(sslService);
|
components.add(sslService);
|
||||||
|
|
||||||
final InternalClient internalClient = new InternalClient(settings, threadPool, client, security.getCryptoService());
|
final InternalClient internalClient = new InternalClient(settings, threadPool, client);
|
||||||
components.add(internalClient);
|
components.add(internalClient);
|
||||||
|
|
||||||
LicenseService licenseService = new LicenseService(settings, clusterService, getClock(),
|
LicenseService licenseService = new LicenseService(settings, clusterService, getClock(),
|
||||||
|
@ -44,6 +44,6 @@ public class ClientProxy {
|
|||||||
|
|
||||||
public static InternalClient fromClient(Client client) {
|
public static InternalClient fromClient(Client client) {
|
||||||
return client instanceof InternalClient ? (InternalClient) client :
|
return client instanceof InternalClient ? (InternalClient) client :
|
||||||
new InternalClient(client.settings(), client.threadPool(), client, null);
|
new InternalClient(client.settings(), client.threadPool(), client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,8 @@ import org.elasticsearch.index.IndexNotFoundException;
|
|||||||
import org.elasticsearch.node.Node;
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication;
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.XPackUser;
|
import org.elasticsearch.xpack.security.user.XPackUser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -47,19 +46,17 @@ import java.util.function.Supplier;
|
|||||||
*/
|
*/
|
||||||
public class InternalClient extends FilterClient {
|
public class InternalClient extends FilterClient {
|
||||||
|
|
||||||
private final CryptoService cryptoService;
|
|
||||||
private final boolean signUserHeader;
|
|
||||||
private final String nodeName;
|
private final String nodeName;
|
||||||
|
private final boolean securityEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an InternalClient.
|
* Constructs an InternalClient.
|
||||||
* If {@code cryptoService} is non-null, the client is secure. Otherwise this client is a passthrough.
|
* If {@code cryptoService} is non-null, the client is secure. Otherwise this client is a passthrough.
|
||||||
*/
|
*/
|
||||||
public InternalClient(Settings settings, ThreadPool threadPool, Client in, CryptoService cryptoService) {
|
public InternalClient(Settings settings, ThreadPool threadPool, Client in) {
|
||||||
super(settings, threadPool, in);
|
super(settings, threadPool, in);
|
||||||
this.cryptoService = cryptoService;
|
|
||||||
this.signUserHeader = AuthenticationService.SIGN_USER_HEADER.get(settings);
|
|
||||||
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
||||||
|
this.securityEnabled = XPackSettings.SECURITY_ENABLED.get(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,11 +64,7 @@ public class InternalClient extends FilterClient {
|
|||||||
ActionRequestBuilder<Request, Response, RequestBuilder>> void doExecute(
|
ActionRequestBuilder<Request, Response, RequestBuilder>> void doExecute(
|
||||||
Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) {
|
Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) {
|
||||||
|
|
||||||
if (cryptoService == null) {
|
if (securityEnabled) {
|
||||||
super.doExecute(action, request, listener);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ThreadContext threadContext = threadPool().getThreadContext();
|
final ThreadContext threadContext = threadPool().getThreadContext();
|
||||||
final Supplier<ThreadContext.StoredContext> storedContext = threadContext.newRestorableContext(true);
|
final Supplier<ThreadContext.StoredContext> storedContext = threadContext.newRestorableContext(true);
|
||||||
// we need to preserve the context here otherwise we execute the response with the XPack user which we can cause problems
|
// we need to preserve the context here otherwise we execute the response with the XPack user which we can cause problems
|
||||||
@ -80,13 +73,16 @@ public class InternalClient extends FilterClient {
|
|||||||
processContext(threadContext);
|
processContext(threadContext);
|
||||||
super.doExecute(action, request, new ContextPreservingActionListener<>(storedContext, listener));
|
super.doExecute(action, request, new ContextPreservingActionListener<>(storedContext, listener));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
super.doExecute(action, request, listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processContext(ThreadContext threadContext) {
|
protected void processContext(ThreadContext threadContext) {
|
||||||
try {
|
try {
|
||||||
Authentication authentication = new Authentication(XPackUser.INSTANCE,
|
Authentication authentication = new Authentication(XPackUser.INSTANCE,
|
||||||
new Authentication.RealmRef("__attach", "__attach", nodeName), null);
|
new Authentication.RealmRef("__attach", "__attach", nodeName), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
|
authentication.writeToContext(threadContext);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new ElasticsearchException("failed to attach internal user to request", ioe);
|
throw new ElasticsearchException("failed to attach internal user to request", ioe);
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
|||||||
}
|
}
|
||||||
threadContext.set(threadPool.getThreadContext());
|
threadContext.set(threadPool.getThreadContext());
|
||||||
List<Object> components = new ArrayList<>();
|
List<Object> components = new ArrayList<>();
|
||||||
securityContext.set(new SecurityContext(settings, threadPool.getThreadContext(), cryptoService));
|
securityContext.set(new SecurityContext(settings, threadPool.getThreadContext()));
|
||||||
components.add(securityContext.get());
|
components.add(securityContext.get());
|
||||||
|
|
||||||
// realms construction
|
// realms construction
|
||||||
@ -321,8 +321,7 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
|||||||
logger.debug("Using authentication failure handler from extension [" + extensionName + "]");
|
logger.debug("Using authentication failure handler from extension [" + extensionName + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
authcService.set(new AuthenticationService(settings, realms, auditTrailService,
|
authcService.set(new AuthenticationService(settings, realms, auditTrailService, failureHandler, threadPool, anonymousUser));
|
||||||
cryptoService, failureHandler, threadPool, anonymousUser));
|
|
||||||
components.add(authcService.get());
|
components.add(authcService.get());
|
||||||
|
|
||||||
final FileRolesStore fileRolesStore = new FileRolesStore(settings, env, resourceWatcherService, licenseState);
|
final FileRolesStore fileRolesStore = new FileRolesStore(settings, env, resourceWatcherService, licenseState);
|
||||||
|
@ -12,8 +12,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
|
|||||||
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
|
||||||
import org.elasticsearch.node.Node;
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication;
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -27,8 +25,6 @@ public class SecurityContext {
|
|||||||
|
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final ThreadContext threadContext;
|
private final ThreadContext threadContext;
|
||||||
private final CryptoService cryptoService;
|
|
||||||
private final boolean signUserHeader;
|
|
||||||
private final String nodeName;
|
private final String nodeName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,11 +32,9 @@ public class SecurityContext {
|
|||||||
* If cryptoService is null, security is disabled and {@link #getUser()}
|
* If cryptoService is null, security is disabled and {@link #getUser()}
|
||||||
* and {@link #getAuthentication()} will always return null.
|
* and {@link #getAuthentication()} will always return null.
|
||||||
*/
|
*/
|
||||||
public SecurityContext(Settings settings, ThreadContext threadContext, CryptoService cryptoService) {
|
public SecurityContext(Settings settings, ThreadContext threadContext) {
|
||||||
this.logger = Loggers.getLogger(getClass(), settings);
|
this.logger = Loggers.getLogger(getClass(), settings);
|
||||||
this.threadContext = threadContext;
|
this.threadContext = threadContext;
|
||||||
this.cryptoService = cryptoService;
|
|
||||||
this.signUserHeader = AuthenticationService.SIGN_USER_HEADER.get(settings);
|
|
||||||
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +47,7 @@ public class SecurityContext {
|
|||||||
/** Returns the authentication information, or null if the current request has no authentication info. */
|
/** Returns the authentication information, or null if the current request has no authentication info. */
|
||||||
public Authentication getAuthentication() {
|
public Authentication getAuthentication() {
|
||||||
try {
|
try {
|
||||||
return Authentication.readFromContext(threadContext, cryptoService, signUserHeader);
|
return Authentication.readFromContext(threadContext);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO: this seems bogus, the only way to get an ioexception here is from a corrupt or tampered
|
// TODO: this seems bogus, the only way to get an ioexception here is from a corrupt or tampered
|
||||||
// auth header, which should be be audited?
|
// auth header, which should be be audited?
|
||||||
@ -78,7 +72,7 @@ public class SecurityContext {
|
|||||||
try {
|
try {
|
||||||
Authentication authentication =
|
Authentication authentication =
|
||||||
new Authentication(user, new Authentication.RealmRef("__attach", "__attach", nodeName), lookedUpBy);
|
new Authentication(user, new Authentication.RealmRef("__attach", "__attach", nodeName), lookedUpBy);
|
||||||
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
|
authentication.writeToContext(threadContext);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError("how can we have a IOException with a user we set", e);
|
throw new AssertionError("how can we have a IOException with a user we set", e);
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,6 @@ import org.elasticsearch.xpack.security.user.User;
|
|||||||
|
|
||||||
public class AuditTrailService extends AbstractComponent implements AuditTrail {
|
public class AuditTrailService extends AbstractComponent implements AuditTrail {
|
||||||
|
|
||||||
public static final Map<String, Object> DISABLED_USAGE_STATS = Collections.singletonMap("enabled", false);
|
|
||||||
|
|
||||||
private final XPackLicenseState licenseState;
|
private final XPackLicenseState licenseState;
|
||||||
final List<AuditTrail> auditTrails;
|
final List<AuditTrail> auditTrails;
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
|||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -77,7 +76,7 @@ public class Authentication {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Authentication readFromContext(ThreadContext ctx, CryptoService cryptoService, boolean sign)
|
public static Authentication readFromContext(ThreadContext ctx)
|
||||||
throws IOException, IllegalArgumentException {
|
throws IOException, IllegalArgumentException {
|
||||||
Authentication authentication = ctx.getTransient(AUTHENTICATION_KEY);
|
Authentication authentication = ctx.getTransient(AUTHENTICATION_KEY);
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
@ -89,19 +88,16 @@ public class Authentication {
|
|||||||
if (authenticationHeader == null) {
|
if (authenticationHeader == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return deserializeHeaderAndPutInContext(authenticationHeader, ctx, cryptoService, sign);
|
return deserializeHeaderAndPutInContext(authenticationHeader, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Authentication getAuthentication(ThreadContext context) {
|
public static Authentication getAuthentication(ThreadContext context) {
|
||||||
return context.getTransient(Authentication.AUTHENTICATION_KEY);
|
return context.getTransient(Authentication.AUTHENTICATION_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Authentication deserializeHeaderAndPutInContext(String header, ThreadContext ctx, CryptoService cryptoService, boolean sign)
|
static Authentication deserializeHeaderAndPutInContext(String header, ThreadContext ctx)
|
||||||
throws IOException, IllegalArgumentException {
|
throws IOException, IllegalArgumentException {
|
||||||
assert ctx.getTransient(AUTHENTICATION_KEY) == null;
|
assert ctx.getTransient(AUTHENTICATION_KEY) == null;
|
||||||
if (sign) {
|
|
||||||
header = cryptoService.unsignAndVerify(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = Base64.getDecoder().decode(header);
|
byte[] bytes = Base64.getDecoder().decode(header);
|
||||||
StreamInput input = StreamInput.wrap(bytes);
|
StreamInput input = StreamInput.wrap(bytes);
|
||||||
@ -112,7 +108,7 @@ public class Authentication {
|
|||||||
return authentication;
|
return authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeToContextIfMissing(ThreadContext context, CryptoService cryptoService, boolean sign)
|
void writeToContextIfMissing(ThreadContext context)
|
||||||
throws IOException, IllegalArgumentException {
|
throws IOException, IllegalArgumentException {
|
||||||
if (context.getTransient(AUTHENTICATION_KEY) != null) {
|
if (context.getTransient(AUTHENTICATION_KEY) != null) {
|
||||||
if (context.getHeader(AUTHENTICATION_KEY) == null) {
|
if (context.getHeader(AUTHENTICATION_KEY) == null) {
|
||||||
@ -122,9 +118,9 @@ public class Authentication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.getHeader(AUTHENTICATION_KEY) != null) {
|
if (context.getHeader(AUTHENTICATION_KEY) != null) {
|
||||||
deserializeHeaderAndPutInContext(context.getHeader(AUTHENTICATION_KEY), context, cryptoService, sign);
|
deserializeHeaderAndPutInContext(context.getHeader(AUTHENTICATION_KEY), context);
|
||||||
} else {
|
} else {
|
||||||
writeToContext(context, cryptoService, sign);
|
writeToContext(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,13 +128,10 @@ public class Authentication {
|
|||||||
* Writes the authentication to the context. There must not be an existing authentication in the context and if there is an
|
* Writes the authentication to the context. There must not be an existing authentication in the context and if there is an
|
||||||
* {@link IllegalStateException} will be thrown
|
* {@link IllegalStateException} will be thrown
|
||||||
*/
|
*/
|
||||||
public void writeToContext(ThreadContext ctx, CryptoService cryptoService, boolean sign)
|
public void writeToContext(ThreadContext ctx)
|
||||||
throws IOException, IllegalArgumentException {
|
throws IOException, IllegalArgumentException {
|
||||||
ensureContextDoesNotContainAuthentication(ctx);
|
ensureContextDoesNotContainAuthentication(ctx);
|
||||||
String header = encode();
|
String header = encode();
|
||||||
if (sign) {
|
|
||||||
header = cryptoService.sign(header);
|
|
||||||
}
|
|
||||||
ctx.putTransient(AUTHENTICATION_KEY, this);
|
ctx.putTransient(AUTHENTICATION_KEY, this);
|
||||||
ctx.putHeader(AUTHENTICATION_KEY, header);
|
ctx.putHeader(AUTHENTICATION_KEY, header);
|
||||||
}
|
}
|
||||||
|
@ -50,26 +50,22 @@ public class AuthenticationService extends AbstractComponent {
|
|||||||
|
|
||||||
private final Realms realms;
|
private final Realms realms;
|
||||||
private final AuditTrail auditTrail;
|
private final AuditTrail auditTrail;
|
||||||
private final CryptoService cryptoService;
|
|
||||||
private final AuthenticationFailureHandler failureHandler;
|
private final AuthenticationFailureHandler failureHandler;
|
||||||
private final ThreadContext threadContext;
|
private final ThreadContext threadContext;
|
||||||
private final String nodeName;
|
private final String nodeName;
|
||||||
private final AnonymousUser anonymousUser;
|
private final AnonymousUser anonymousUser;
|
||||||
private final boolean signUserHeader;
|
|
||||||
private final boolean runAsEnabled;
|
private final boolean runAsEnabled;
|
||||||
private final boolean isAnonymousUserEnabled;
|
private final boolean isAnonymousUserEnabled;
|
||||||
|
|
||||||
public AuthenticationService(Settings settings, Realms realms, AuditTrailService auditTrail, CryptoService cryptoService,
|
public AuthenticationService(Settings settings, Realms realms, AuditTrailService auditTrail,
|
||||||
AuthenticationFailureHandler failureHandler, ThreadPool threadPool, AnonymousUser anonymousUser) {
|
AuthenticationFailureHandler failureHandler, ThreadPool threadPool, AnonymousUser anonymousUser) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
||||||
this.realms = realms;
|
this.realms = realms;
|
||||||
this.auditTrail = auditTrail;
|
this.auditTrail = auditTrail;
|
||||||
this.cryptoService = cryptoService;
|
|
||||||
this.failureHandler = failureHandler;
|
this.failureHandler = failureHandler;
|
||||||
this.threadContext = threadPool.getThreadContext();
|
this.threadContext = threadPool.getThreadContext();
|
||||||
this.anonymousUser = anonymousUser;
|
this.anonymousUser = anonymousUser;
|
||||||
this.signUserHeader = SIGN_USER_HEADER.get(settings);
|
|
||||||
this.runAsEnabled = RUN_AS_ENABLED.get(settings);
|
this.runAsEnabled = RUN_AS_ENABLED.get(settings);
|
||||||
this.isAnonymousUserEnabled = AnonymousUser.isAnonymousEnabled(settings);
|
this.isAnonymousUserEnabled = AnonymousUser.isAnonymousEnabled(settings);
|
||||||
}
|
}
|
||||||
@ -110,7 +106,7 @@ public class AuthenticationService extends AbstractComponent {
|
|||||||
*/
|
*/
|
||||||
void attachUserIfMissing(User user) throws IOException {
|
void attachUserIfMissing(User user) throws IOException {
|
||||||
Authentication authentication = new Authentication(user, new RealmRef("__attach", "__attach", nodeName), null);
|
Authentication authentication = new Authentication(user, new RealmRef("__attach", "__attach", nodeName), null);
|
||||||
authentication.writeToContextIfMissing(threadContext, cryptoService, signUserHeader);
|
authentication.writeToContextIfMissing(threadContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pkg private method for testing
|
// pkg private method for testing
|
||||||
@ -182,7 +178,7 @@ public class AuthenticationService extends AbstractComponent {
|
|||||||
private void lookForExistingAuthentication(Consumer<Authentication> authenticationConsumer) {
|
private void lookForExistingAuthentication(Consumer<Authentication> authenticationConsumer) {
|
||||||
Runnable action;
|
Runnable action;
|
||||||
try {
|
try {
|
||||||
final Authentication authentication = Authentication.readFromContext(threadContext, cryptoService, signUserHeader);
|
final Authentication authentication = Authentication.readFromContext(threadContext);
|
||||||
if (authentication != null && request instanceof AuditableRestRequest) {
|
if (authentication != null && request instanceof AuditableRestRequest) {
|
||||||
action = () -> listener.onFailure(request.tamperedRequest());
|
action = () -> listener.onFailure(request.tamperedRequest());
|
||||||
} else {
|
} else {
|
||||||
@ -293,7 +289,7 @@ public class AuthenticationService extends AbstractComponent {
|
|||||||
Runnable action;
|
Runnable action;
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
try {
|
try {
|
||||||
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
|
authentication.writeToContext(threadContext);
|
||||||
request.authenticationSuccess(authentication.getAuthenticatedBy().getName(), authentication.getUser());
|
request.authenticationSuccess(authentication.getAuthenticatedBy().getName(), authentication.getUser());
|
||||||
action = () -> listener.onResponse(authentication);
|
action = () -> listener.onResponse(authentication);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -377,7 +373,7 @@ public class AuthenticationService extends AbstractComponent {
|
|||||||
final Authentication finalAuth = new Authentication(finalUser, authenticatedBy, lookedupBy);
|
final Authentication finalAuth = new Authentication(finalUser, authenticatedBy, lookedupBy);
|
||||||
Runnable action = () -> listener.onResponse(finalAuth);
|
Runnable action = () -> listener.onResponse(finalAuth);
|
||||||
try {
|
try {
|
||||||
finalAuth.writeToContext(threadContext, cryptoService, signUserHeader);
|
finalAuth.writeToContext(threadContext);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
action = () -> listener.onFailure(request.exceptionProcessingRequest(e, authenticationToken));
|
action = () -> listener.onFailure(request.exceptionProcessingRequest(e, authenticationToken));
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,18 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.common.Nullable;
|
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Setting.Property;
|
import org.elasticsearch.common.settings.Setting.Property;
|
||||||
@ -56,10 +52,8 @@ public class CryptoService extends AbstractComponent {
|
|||||||
static final String DEFAULT_KEY_ALGORITH = "AES";
|
static final String DEFAULT_KEY_ALGORITH = "AES";
|
||||||
static final String ENCRYPTED_TEXT_PREFIX = "::es_encrypted::";
|
static final String ENCRYPTED_TEXT_PREFIX = "::es_encrypted::";
|
||||||
static final int DEFAULT_KEY_LENGTH = 128;
|
static final int DEFAULT_KEY_LENGTH = 128;
|
||||||
static final int RANDOM_KEY_SIZE = 128;
|
|
||||||
|
|
||||||
private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$[^\\$]*\\$\\$.+");
|
private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$[^\\$]*\\$\\$.+");
|
||||||
private static final byte[] HKDF_APP_INFO = "es-security-crypto-service".getBytes(StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
private static final Setting<Boolean> SYSTEM_KEY_REQUIRED_SETTING =
|
private static final Setting<Boolean> SYSTEM_KEY_REQUIRED_SETTING =
|
||||||
Setting.boolSetting(setting("system_key.required"), false, Property.NodeScope);
|
Setting.boolSetting(setting("system_key.required"), false, Property.NodeScope);
|
||||||
@ -72,36 +66,27 @@ public class CryptoService extends AbstractComponent {
|
|||||||
|
|
||||||
private final SecureRandom secureRandom = new SecureRandom();
|
private final SecureRandom secureRandom = new SecureRandom();
|
||||||
private final String encryptionAlgorithm;
|
private final String encryptionAlgorithm;
|
||||||
private final String keyAlgorithm;
|
|
||||||
private final int keyLength;
|
|
||||||
private final int ivLength;
|
private final int ivLength;
|
||||||
|
/*
|
||||||
private final Path keyFile;
|
* Scroll ids are signed using the system key to authenticate them as we currently do not have a proper way to authorize these requests
|
||||||
|
* and the key is also used for encrypting sensitive data stored in watches. The encryption key is derived from the system key.
|
||||||
private final SecretKey randomKey;
|
*/
|
||||||
private final String randomKeyBase64;
|
|
||||||
|
|
||||||
private final SecretKey encryptionKey;
|
private final SecretKey encryptionKey;
|
||||||
private final SecretKey systemKey;
|
private final SecretKey systemKey;
|
||||||
private final SecretKey signingKey;
|
|
||||||
|
|
||||||
public CryptoService(Settings settings, Environment env) throws IOException {
|
public CryptoService(Settings settings, Environment env) throws IOException {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings);
|
this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings);
|
||||||
this.keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings);
|
final int keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings);
|
||||||
this.ivLength = keyLength / 8;
|
this.ivLength = keyLength / 8;
|
||||||
this.keyAlgorithm = ENCRYPTION_KEY_ALGO_SETTING.get(settings);
|
String keyAlgorithm = ENCRYPTION_KEY_ALGO_SETTING.get(settings);
|
||||||
|
|
||||||
if (keyLength % 8 != 0) {
|
if (keyLength % 8 != 0) {
|
||||||
throw new IllegalArgumentException("invalid key length [" + keyLength + "]. value must be a multiple of 8");
|
throw new IllegalArgumentException("invalid key length [" + keyLength + "]. value must be a multiple of 8");
|
||||||
}
|
}
|
||||||
|
|
||||||
keyFile = resolveSystemKey(env);
|
Path keyFile = resolveSystemKey(env);
|
||||||
systemKey = readSystemKey(keyFile, SYSTEM_KEY_REQUIRED_SETTING.get(settings));
|
systemKey = readSystemKey(keyFile, SYSTEM_KEY_REQUIRED_SETTING.get(settings));
|
||||||
randomKey = generateSecretKey(RANDOM_KEY_SIZE);
|
|
||||||
randomKeyBase64 = Base64.getUrlEncoder().encodeToString(randomKey.getEncoded());
|
|
||||||
|
|
||||||
signingKey = createSigningKey(systemKey, randomKey);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
|
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
|
||||||
@ -131,18 +116,6 @@ public class CryptoService extends AbstractComponent {
|
|||||||
return XPackPlugin.resolveConfigFile(env, FILE_NAME);
|
return XPackPlugin.resolveConfigFile(env, FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SecretKey createSigningKey(@Nullable SecretKey systemKey, SecretKey randomKey) {
|
|
||||||
assert randomKey != null;
|
|
||||||
if (systemKey != null) {
|
|
||||||
return systemKey;
|
|
||||||
} else {
|
|
||||||
// the random key is only 128 bits so we use HKDF to expand to 1024 bits with some application specific data mixed in
|
|
||||||
byte[] keyBytes = HmacSHA1HKDF.extractAndExpand(null, randomKey.getEncoded(), HKDF_APP_INFO, (KEY_SIZE / 8));
|
|
||||||
assert keyBytes.length * 8 == KEY_SIZE;
|
|
||||||
return new SecretKeySpec(keyBytes, KEY_ALGO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SecretKey readSystemKey(Path file, boolean required) throws IOException {
|
private static SecretKey readSystemKey(Path file, boolean required) throws IOException {
|
||||||
if (Files.exists(file)) {
|
if (Files.exists(file)) {
|
||||||
byte[] bytes = Files.readAllBytes(file);
|
byte[] bytes = Files.readAllBytes(file);
|
||||||
@ -161,8 +134,12 @@ public class CryptoService extends AbstractComponent {
|
|||||||
* @param text the string to sign
|
* @param text the string to sign
|
||||||
*/
|
*/
|
||||||
public String sign(String text) throws IOException {
|
public String sign(String text) throws IOException {
|
||||||
String sigStr = signInternal(text, signingKey);
|
if (systemKey != null) {
|
||||||
return "$$" + sigStr.length() + "$$" + (systemKey == signingKey ? "" : randomKeyBase64) + "$$" + sigStr + text;
|
String sigStr = signInternal(text, systemKey);
|
||||||
|
return "$$" + sigStr.length() + "$$$$" + sigStr + text;
|
||||||
|
} else {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,22 +148,24 @@ public class CryptoService extends AbstractComponent {
|
|||||||
* @param signedText the string to unsign and verify
|
* @param signedText the string to unsign and verify
|
||||||
*/
|
*/
|
||||||
public String unsignAndVerify(String signedText) {
|
public String unsignAndVerify(String signedText) {
|
||||||
|
if (systemKey == null) {
|
||||||
|
return signedText;
|
||||||
|
}
|
||||||
|
|
||||||
if (!signedText.startsWith("$$") || signedText.length() < 2) {
|
if (!signedText.startsWith("$$") || signedText.length() < 2) {
|
||||||
throw new IllegalArgumentException("tampered signed text");
|
throw new IllegalArgumentException("tampered signed text");
|
||||||
}
|
}
|
||||||
|
|
||||||
// $$34$$randomKeyBase64$$sigtext
|
// $$34$$$$sigtext
|
||||||
String[] pieces = signedText.split("\\$\\$");
|
String[] pieces = signedText.split("\\$\\$");
|
||||||
if (pieces.length != 4 || !pieces[0].equals("")) {
|
if (pieces.length != 4 || pieces[0].isEmpty() == false || pieces[2].isEmpty() == false) {
|
||||||
logger.debug("received signed text [{}] with [{}] parts", signedText, pieces.length);
|
logger.debug("received signed text [{}] with [{}] parts", signedText, pieces.length);
|
||||||
throw new IllegalArgumentException("tampered signed text");
|
throw new IllegalArgumentException("tampered signed text");
|
||||||
}
|
}
|
||||||
String text;
|
String text;
|
||||||
String base64RandomKey;
|
|
||||||
String receivedSignature;
|
String receivedSignature;
|
||||||
try {
|
try {
|
||||||
int length = Integer.parseInt(pieces[1]);
|
int length = Integer.parseInt(pieces[1]);
|
||||||
base64RandomKey = pieces[2];
|
|
||||||
receivedSignature = pieces[3].substring(0, length);
|
receivedSignature = pieces[3].substring(0, length);
|
||||||
text = pieces[3].substring(length);
|
text = pieces[3].substring(length);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -194,36 +173,8 @@ public class CryptoService extends AbstractComponent {
|
|||||||
throw new IllegalArgumentException("tampered signed text");
|
throw new IllegalArgumentException("tampered signed text");
|
||||||
}
|
}
|
||||||
|
|
||||||
SecretKey signingKey;
|
|
||||||
// no random key, so we must have a system key
|
|
||||||
if (base64RandomKey.isEmpty()) {
|
|
||||||
if (systemKey == null) {
|
|
||||||
logger.debug("received signed text without random key information and no system key is present");
|
|
||||||
throw new IllegalArgumentException("tampered signed text");
|
|
||||||
}
|
|
||||||
signingKey = systemKey;
|
|
||||||
} else if (systemKey != null) {
|
|
||||||
// we have a system key and there is some random key data, this is an error
|
|
||||||
logger.debug("received signed text with random key information but a system key is present");
|
|
||||||
throw new IllegalArgumentException("tampered signed text");
|
|
||||||
} else {
|
|
||||||
byte[] randomKeyBytes;
|
|
||||||
try {
|
try {
|
||||||
randomKeyBytes = Base64.getUrlDecoder().decode(base64RandomKey);
|
String sig = signInternal(text, systemKey);
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
logger.error("error occurred while decoding key data", e);
|
|
||||||
throw new IllegalStateException("error while verifying the signed text");
|
|
||||||
}
|
|
||||||
if (randomKeyBytes.length * 8 != RANDOM_KEY_SIZE) {
|
|
||||||
logger.debug("incorrect random key data length. received [{}] bytes", randomKeyBytes.length);
|
|
||||||
throw new IllegalArgumentException("tampered signed text");
|
|
||||||
}
|
|
||||||
SecretKey randomKey = new SecretKeySpec(randomKeyBytes, KEY_ALGO);
|
|
||||||
signingKey = createSigningKey(systemKey, randomKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String sig = signInternal(text, signingKey);
|
|
||||||
if (constantTimeEquals(sig, receivedSignature)) {
|
if (constantTimeEquals(sig, receivedSignature)) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@ -409,90 +360,6 @@ public class CryptoService extends AbstractComponent {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplified implementation of HKDF using the HmacSHA1 algortihm.
|
|
||||||
*
|
|
||||||
* @see <a href=https://tools.ietf.org/html/rfc5869>RFC 5869</a>
|
|
||||||
*/
|
|
||||||
private static class HmacSHA1HKDF {
|
|
||||||
private static final int HMAC_SHA1_BYTE_LENGTH = 20;
|
|
||||||
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method performs the <code>extract</code> and <code>expand</code> steps of HKDF in one call with the given
|
|
||||||
* data. The output of the extract step is used as the input to the expand step
|
|
||||||
*
|
|
||||||
* @param salt optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros.
|
|
||||||
* @param ikm the input keying material
|
|
||||||
* @param info optional context and application specific information; if not provided a zero length byte[] is used
|
|
||||||
* @param outputLength length of output keying material in octets (<= 255*HashLen)
|
|
||||||
* @return the output keying material
|
|
||||||
*/
|
|
||||||
static byte[] extractAndExpand(@Nullable SecretKey salt, byte[] ikm, @Nullable byte[] info, int outputLength) {
|
|
||||||
// arg checking
|
|
||||||
Objects.requireNonNull(ikm, "the input keying material must not be null");
|
|
||||||
if (outputLength < 1) {
|
|
||||||
throw new IllegalArgumentException("output length must be positive int >= 1");
|
|
||||||
}
|
|
||||||
if (outputLength > 255 * HMAC_SHA1_BYTE_LENGTH) {
|
|
||||||
throw new IllegalArgumentException("output length must be <= 255*" + HMAC_SHA1_BYTE_LENGTH);
|
|
||||||
}
|
|
||||||
if (salt == null) {
|
|
||||||
salt = new SecretKeySpec(new byte[HMAC_SHA1_BYTE_LENGTH], HMAC_SHA1_ALGORITHM);
|
|
||||||
}
|
|
||||||
if (info == null) {
|
|
||||||
info = new byte[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract
|
|
||||||
Mac mac = createMac(salt);
|
|
||||||
byte[] keyBytes = mac.doFinal(ikm);
|
|
||||||
final SecretKey pseudoRandomKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The output OKM is calculated as follows:
|
|
||||||
* N = ceil(L/HashLen)
|
|
||||||
* T = T(1) | T(2) | T(3) | ... | T(N)
|
|
||||||
* OKM = first L octets of T
|
|
||||||
*
|
|
||||||
* where:
|
|
||||||
* T(0) = empty string (zero length)
|
|
||||||
* T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
|
||||||
* T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
|
|
||||||
* T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
|
|
||||||
* ...
|
|
||||||
*
|
|
||||||
* (where the constant concatenated to the end of each T(n) is a single octet.)
|
|
||||||
*/
|
|
||||||
int n = (outputLength % HMAC_SHA1_BYTE_LENGTH == 0) ?
|
|
||||||
outputLength / HMAC_SHA1_BYTE_LENGTH :
|
|
||||||
(outputLength / HMAC_SHA1_BYTE_LENGTH) + 1;
|
|
||||||
|
|
||||||
byte[] hashRound = new byte[0];
|
|
||||||
|
|
||||||
ByteBuffer generatedBytes = ByteBuffer.allocate(Math.multiplyExact(n, HMAC_SHA1_BYTE_LENGTH));
|
|
||||||
try {
|
|
||||||
// initiliaze the mac with the new key
|
|
||||||
mac.init(pseudoRandomKey);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new ElasticsearchException("failed to initialize the mac", e);
|
|
||||||
}
|
|
||||||
for (int roundNum = 1; roundNum <= n; roundNum++) {
|
|
||||||
mac.reset();
|
|
||||||
mac.update(hashRound);
|
|
||||||
mac.update(info);
|
|
||||||
mac.update((byte) roundNum);
|
|
||||||
hashRound = mac.doFinal();
|
|
||||||
generatedBytes.put(hashRound);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] result = new byte[outputLength];
|
|
||||||
generatedBytes.rewind();
|
|
||||||
generatedBytes.get(result, 0, outputLength);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addSettings(List<Setting<?>> settings) {
|
public static void addSettings(List<Setting<?>> settings) {
|
||||||
settings.add(ENCRYPTION_KEY_LENGTH_SETTING);
|
settings.add(ENCRYPTION_KEY_LENGTH_SETTING);
|
||||||
settings.add(ENCRYPTION_KEY_ALGO_SETTING);
|
settings.add(ENCRYPTION_KEY_ALGO_SETTING);
|
||||||
|
@ -64,7 +64,7 @@ public class WatcherClientProxy extends ClientProxy {
|
|||||||
*/
|
*/
|
||||||
public static WatcherClientProxy of(Client client) {
|
public static WatcherClientProxy of(Client client) {
|
||||||
return new WatcherClientProxy(Settings.EMPTY, client instanceof InternalClient ? (InternalClient) client :
|
return new WatcherClientProxy(Settings.EMPTY, client instanceof InternalClient ? (InternalClient) client :
|
||||||
new InternalClient(client.settings(), client.threadPool(), client, null));
|
new InternalClient(client.settings(), client.threadPool(), client));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexResponse index(IndexRequest request, TimeValue timeout) {
|
public IndexResponse index(IndexRequest request, TimeValue timeout) {
|
||||||
|
@ -14,14 +14,11 @@ import org.elasticsearch.action.search.SearchResponse;
|
|||||||
import org.elasticsearch.client.FilterClient;
|
import org.elasticsearch.client.FilterClient;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.env.Environment;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.threadpool.TestThreadPool;
|
import org.elasticsearch.threadpool.TestThreadPool;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -51,9 +48,7 @@ public class InternalClientTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Path tempDir = createTempDir();
|
InternalClient client = new InternalClient(Settings.EMPTY, threadPool, dummy) {
|
||||||
InternalClient client = new InternalClient(Settings.EMPTY, threadPool, dummy, new CryptoService(Settings.EMPTY,
|
|
||||||
new Environment(Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), tempDir.toString()).build()))) {
|
|
||||||
@Override
|
@Override
|
||||||
protected void processContext(ThreadContext threadContext) {
|
protected void processContext(ThreadContext threadContext) {
|
||||||
threadContext.putTransient("foo", "boom");
|
threadContext.putTransient("foo", "boom");
|
||||||
@ -104,9 +99,7 @@ public class InternalClientTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Path tempDir = createTempDir();
|
InternalClient client = new InternalClient(Settings.EMPTY, threadPool, dummy) {
|
||||||
InternalClient client = new InternalClient(Settings.EMPTY, threadPool, dummy, new CryptoService(Settings.EMPTY,
|
|
||||||
new Environment(Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), tempDir.toString()).build()))) {
|
|
||||||
@Override
|
@Override
|
||||||
protected void processContext(ThreadContext threadContext) {
|
protected void processContext(ThreadContext threadContext) {
|
||||||
threadContext.putTransient("foo", "boom");
|
threadContext.putTransient("foo", "boom");
|
||||||
|
@ -8,12 +8,9 @@ package org.elasticsearch.xpack.security;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
|
||||||
import org.elasticsearch.env.Environment;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication;
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -23,22 +20,17 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
public class SecurityContextTests extends ESTestCase {
|
public class SecurityContextTests extends ESTestCase {
|
||||||
|
|
||||||
private boolean signHeader;
|
|
||||||
private Settings settings;
|
private Settings settings;
|
||||||
private ThreadContext threadContext;
|
private ThreadContext threadContext;
|
||||||
private CryptoService cryptoService;
|
|
||||||
private SecurityContext securityContext;
|
private SecurityContext securityContext;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void buildSecurityContext() throws IOException {
|
public void buildSecurityContext() throws IOException {
|
||||||
signHeader = randomBoolean();
|
|
||||||
settings = Settings.builder()
|
settings = Settings.builder()
|
||||||
.put("path.home", createTempDir())
|
.put("path.home", createTempDir())
|
||||||
.put(AuthenticationService.SIGN_USER_HEADER.getKey(), signHeader)
|
|
||||||
.build();
|
.build();
|
||||||
threadContext = new ThreadContext(settings);
|
threadContext = new ThreadContext(settings);
|
||||||
cryptoService = new CryptoService(settings, new Environment(settings));
|
securityContext = new SecurityContext(settings, threadContext);
|
||||||
securityContext = new SecurityContext(settings, threadContext, cryptoService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetAuthenticationAndUserInEmptyContext() throws IOException {
|
public void testGetAuthenticationAndUserInEmptyContext() throws IOException {
|
||||||
@ -49,7 +41,7 @@ public class SecurityContextTests extends ESTestCase {
|
|||||||
public void testGetAuthenticationAndUser() throws IOException {
|
public void testGetAuthenticationAndUser() throws IOException {
|
||||||
final User user = new User("test");
|
final User user = new User("test");
|
||||||
final Authentication authentication = new Authentication(user, new RealmRef("ldap", "foo", "node1"), null);
|
final Authentication authentication = new Authentication(user, new RealmRef("ldap", "foo", "node1"), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, signHeader);
|
authentication.writeToContext(threadContext);
|
||||||
|
|
||||||
assertEquals(authentication, securityContext.getAuthentication());
|
assertEquals(authentication, securityContext.getAuthentication());
|
||||||
assertEquals(user, securityContext.getUser());
|
assertEquals(user, securityContext.getUser());
|
||||||
@ -72,7 +64,7 @@ public class SecurityContextTests extends ESTestCase {
|
|||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
original = new User("test");
|
original = new User("test");
|
||||||
final Authentication authentication = new Authentication(original, new RealmRef("ldap", "foo", "node1"), null);
|
final Authentication authentication = new Authentication(original, new RealmRef("ldap", "foo", "node1"), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, signHeader);
|
authentication.writeToContext(threadContext);
|
||||||
} else {
|
} else {
|
||||||
original = null;
|
original = null;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public class SecurityTemplateServiceTests extends ESTestCase {
|
|||||||
transportClient = new MockTransportClient(Settings.EMPTY);
|
transportClient = new MockTransportClient(Settings.EMPTY);
|
||||||
class IClient extends InternalClient {
|
class IClient extends InternalClient {
|
||||||
IClient(Client transportClient) {
|
IClient(Client transportClient) {
|
||||||
super(Settings.EMPTY, null, transportClient, null);
|
super(Settings.EMPTY, null, transportClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,7 +82,7 @@ public class SecurityActionFilterTests extends ESTestCase {
|
|||||||
DestructiveOperations destructiveOperations = new DestructiveOperations(settings,
|
DestructiveOperations destructiveOperations = new DestructiveOperations(settings,
|
||||||
new ClusterSettings(settings, Collections.singleton(DestructiveOperations.REQUIRES_NAME_SETTING)));
|
new ClusterSettings(settings, Collections.singleton(DestructiveOperations.REQUIRES_NAME_SETTING)));
|
||||||
|
|
||||||
SecurityContext securityContext = new SecurityContext(settings, threadContext, cryptoService);
|
SecurityContext securityContext = new SecurityContext(settings, threadContext);
|
||||||
filter = new SecurityActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail,
|
filter = new SecurityActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail,
|
||||||
licenseState, new HashSet<>(), threadPool, securityContext, destructiveOperations);
|
licenseState, new HashSet<>(), threadPool, securityContext, destructiveOperations);
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
|
|||||||
clientCalled = new AtomicBoolean(false);
|
clientCalled = new AtomicBoolean(false);
|
||||||
class IClient extends InternalClient {
|
class IClient extends InternalClient {
|
||||||
IClient(Client transportClient){
|
IClient(Client transportClient){
|
||||||
super(Settings.EMPTY, null, transportClient, null);
|
super(Settings.EMPTY, null, transportClient);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends
|
protected <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends
|
||||||
|
@ -33,7 +33,6 @@ import org.elasticsearch.xpack.security.authc.Realm.Factory;
|
|||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
@ -74,7 +73,6 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
private Realm secondRealm;
|
private Realm secondRealm;
|
||||||
private AuditTrailService auditTrail;
|
private AuditTrailService auditTrail;
|
||||||
private AuthenticationToken token;
|
private AuthenticationToken token;
|
||||||
private CryptoService cryptoService;
|
|
||||||
private ThreadPool threadPool;
|
private ThreadPool threadPool;
|
||||||
private ThreadContext threadContext;
|
private ThreadContext threadContext;
|
||||||
|
|
||||||
@ -98,14 +96,12 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
when(licenseState.isAuthAllowed()).thenReturn(true);
|
when(licenseState.isAuthAllowed()).thenReturn(true);
|
||||||
realms = new TestRealms(Settings.EMPTY, new Environment(settings), Collections.<String, Realm.Factory>emptyMap(),
|
realms = new TestRealms(Settings.EMPTY, new Environment(settings), Collections.<String, Realm.Factory>emptyMap(),
|
||||||
licenseState, mock(ReservedRealm.class), Arrays.asList(firstRealm, secondRealm), Collections.singletonList(firstRealm));
|
licenseState, mock(ReservedRealm.class), Arrays.asList(firstRealm, secondRealm), Collections.singletonList(firstRealm));
|
||||||
cryptoService = mock(CryptoService.class);
|
|
||||||
|
|
||||||
auditTrail = mock(AuditTrailService.class);
|
auditTrail = mock(AuditTrailService.class);
|
||||||
threadPool = mock(ThreadPool.class);
|
threadPool = mock(ThreadPool.class);
|
||||||
threadContext = new ThreadContext(Settings.EMPTY);
|
threadContext = new ThreadContext(Settings.EMPTY);
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
||||||
when(cryptoService.sign(any(String.class))).thenReturn("_signed_auth");
|
service = new AuthenticationService(settings, realms, auditTrail,
|
||||||
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
|
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(settings));
|
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +152,6 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
assertThat(result.getAuthenticatedBy(), is(notNullValue())); // TODO implement equals
|
assertThat(result.getAuthenticatedBy(), is(notNullValue())); // TODO implement equals
|
||||||
verify(auditTrail).authenticationFailed(firstRealm.name(), token, "_action", message);
|
verify(auditTrail).authenticationFailed(firstRealm.name(), token, "_action", message);
|
||||||
verify(cryptoService).sign(any(String.class));
|
|
||||||
assertThreadContextContainsAuthentication(result);
|
assertThreadContextContainsAuthentication(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +173,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testAuthenticateCached() throws Exception {
|
public void testAuthenticateCached() throws Exception {
|
||||||
final Authentication authentication = new Authentication(new User("_username", "r1"), new RealmRef("test", "cached", "foo"), null);
|
final Authentication authentication = new Authentication(new User("_username", "r1"), new RealmRef("test", "cached", "foo"), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, true);
|
authentication.writeToContext(threadContext);
|
||||||
|
|
||||||
Authentication result = authenticateBlocking("_action", message, null);
|
Authentication result = authenticateBlocking("_action", message, null);
|
||||||
|
|
||||||
@ -187,7 +182,6 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
verifyZeroInteractions(auditTrail);
|
verifyZeroInteractions(auditTrail);
|
||||||
verifyZeroInteractions(firstRealm);
|
verifyZeroInteractions(firstRealm);
|
||||||
verifyZeroInteractions(secondRealm);
|
verifyZeroInteractions(secondRealm);
|
||||||
verify(cryptoService).sign(any(String.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAuthenticateNonExistentRestRequestUserThrowsAuthenticationException() throws Exception {
|
public void testAuthenticateNonExistentRestRequestUserThrowsAuthenticationException() throws Exception {
|
||||||
@ -337,7 +331,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
InternalMessage message1 = new InternalMessage();
|
InternalMessage message1 = new InternalMessage();
|
||||||
ThreadContext threadContext1 = new ThreadContext(Settings.EMPTY);
|
ThreadContext threadContext1 = new ThreadContext(Settings.EMPTY);
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
||||||
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
|
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail,
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
||||||
|
|
||||||
threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY));
|
threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY));
|
||||||
@ -350,57 +344,9 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
// checking authentication from the user header
|
// checking authentication from the user header
|
||||||
threadContext1 = new ThreadContext(Settings.EMPTY);
|
threadContext1 = new ThreadContext(Settings.EMPTY);
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
||||||
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
|
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail,
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
||||||
threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY));
|
threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY));
|
||||||
when(cryptoService.unsignAndVerify("_signed_auth")).thenReturn(authentication.encode());
|
|
||||||
|
|
||||||
BytesStreamOutput output = new BytesStreamOutput();
|
|
||||||
threadContext1.writeTo(output);
|
|
||||||
StreamInput input = output.bytes().streamInput();
|
|
||||||
threadContext1 = new ThreadContext(Settings.EMPTY);
|
|
||||||
threadContext1.readHeaders(input);
|
|
||||||
|
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
|
||||||
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
|
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
|
||||||
Authentication result = authenticateBlocking("_action", new InternalMessage(), SystemUser.INSTANCE);
|
|
||||||
assertThat(result, notNullValue());
|
|
||||||
assertThat(result.getUser(), equalTo(user1));
|
|
||||||
verifyZeroInteractions(firstRealm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAuthenticateTransportContextAndHeaderNoSigning() throws Exception {
|
|
||||||
Settings settings = Settings.builder().put(AuthenticationService.SIGN_USER_HEADER.getKey(), false).build();
|
|
||||||
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
|
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
|
||||||
|
|
||||||
User user1 = new User("username", "r1", "r2");
|
|
||||||
when(firstRealm.supports(token)).thenReturn(true);
|
|
||||||
when(firstRealm.token(threadContext)).thenReturn(token);
|
|
||||||
mockAuthenticate(firstRealm, token, user1);
|
|
||||||
Authentication authentication = authenticateBlocking("_action", message, SystemUser.INSTANCE);
|
|
||||||
assertThat(authentication, notNullValue());
|
|
||||||
assertThat(authentication.getUser(), sameInstance(user1));
|
|
||||||
assertThreadContextContainsAuthentication(authentication, false);
|
|
||||||
reset(firstRealm);
|
|
||||||
|
|
||||||
// checking authentication from the context
|
|
||||||
InternalMessage message1 = new InternalMessage();
|
|
||||||
ThreadContext threadContext1 = new ThreadContext(Settings.EMPTY);
|
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
|
||||||
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
|
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
|
||||||
threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY));
|
|
||||||
threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY));
|
|
||||||
Authentication ctxAuth = authenticateBlocking("_action", message1, SystemUser.INSTANCE);
|
|
||||||
assertThat(ctxAuth, sameInstance(authentication));
|
|
||||||
verifyZeroInteractions(firstRealm);
|
|
||||||
reset(firstRealm);
|
|
||||||
|
|
||||||
// checking authentication from the user header
|
|
||||||
threadContext1 = new ThreadContext(Settings.EMPTY);
|
|
||||||
threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY));
|
|
||||||
|
|
||||||
BytesStreamOutput output = new BytesStreamOutput();
|
BytesStreamOutput output = new BytesStreamOutput();
|
||||||
threadContext1.writeTo(output);
|
threadContext1.writeTo(output);
|
||||||
@ -409,21 +355,17 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
threadContext1.readHeaders(input);
|
threadContext1.readHeaders(input);
|
||||||
|
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
when(threadPool.getThreadContext()).thenReturn(threadContext1);
|
||||||
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
|
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail,
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
|
||||||
Authentication result = authenticateBlocking("_action", new InternalMessage(), SystemUser.INSTANCE);
|
Authentication result = authenticateBlocking("_action", new InternalMessage(), SystemUser.INSTANCE);
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), equalTo(user1));
|
assertThat(result.getUser(), equalTo(user1));
|
||||||
verifyZeroInteractions(firstRealm);
|
verifyZeroInteractions(firstRealm);
|
||||||
|
|
||||||
verifyZeroInteractions(cryptoService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAuthenticateTamperedUser() throws Exception {
|
public void testAuthenticateTamperedUser() throws Exception {
|
||||||
InternalMessage message = new InternalMessage();
|
InternalMessage message = new InternalMessage();
|
||||||
threadContext.putHeader(Authentication.AUTHENTICATION_KEY, "_signed_auth");
|
threadContext.putHeader(Authentication.AUTHENTICATION_KEY, "_signed_auth");
|
||||||
when(cryptoService.unsignAndVerify("_signed_auth")).thenThrow(
|
|
||||||
randomFrom(new RuntimeException(), new IllegalArgumentException(), new IllegalStateException()));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authenticateBlocking("_action", message, randomBoolean() ? SystemUser.INSTANCE : null);
|
authenticateBlocking("_action", message, randomBoolean() ? SystemUser.INSTANCE : null);
|
||||||
@ -452,13 +394,13 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
assertThat(authentication.getAuthenticatedBy().getName(), is("__attach"));
|
assertThat(authentication.getAuthenticatedBy().getName(), is("__attach"));
|
||||||
assertThat(authentication.getAuthenticatedBy().getType(), is("__attach"));
|
assertThat(authentication.getAuthenticatedBy().getType(), is("__attach"));
|
||||||
assertThat(authentication.getAuthenticatedBy().getNodeName(), is("authc_test"));
|
assertThat(authentication.getAuthenticatedBy().getNodeName(), is("authc_test"));
|
||||||
assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) "_signed_auth"));
|
assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) authentication.encode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAttachIfMissingExists() throws Exception {
|
public void testAttachIfMissingExists() throws Exception {
|
||||||
Authentication authentication = new Authentication(new User("username", "r1", "r2"), new RealmRef("test", "test", "foo"), null);
|
Authentication authentication = new Authentication(new User("username", "r1", "r2"), new RealmRef("test", "test", "foo"), null);
|
||||||
threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication);
|
threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication);
|
||||||
threadContext.putHeader(Authentication.AUTHENTICATION_KEY, "_signed_auth");
|
threadContext.putHeader(Authentication.AUTHENTICATION_KEY, authentication.encode());
|
||||||
service.attachUserIfMissing(new User("username2", "r3", "r4"));
|
service.attachUserIfMissing(new User("username2", "r3", "r4"));
|
||||||
assertThreadContextContainsAuthentication(authentication);
|
assertThreadContextContainsAuthentication(authentication);
|
||||||
}
|
}
|
||||||
@ -472,7 +414,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
Settings settings = builder.build();
|
Settings settings = builder.build();
|
||||||
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
||||||
service = new AuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(),
|
service = new AuthenticationService(settings, realms, auditTrail, new DefaultAuthenticationFailureHandler(),
|
||||||
threadPool, anonymousUser);
|
threadPool, anonymousUser);
|
||||||
RestRequest request = new FakeRestRequest();
|
RestRequest request = new FakeRestRequest();
|
||||||
|
|
||||||
@ -490,7 +432,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
.putArray(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
|
.putArray(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
|
||||||
.build();
|
.build();
|
||||||
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
||||||
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
|
service = new AuthenticationService(settings, realms, auditTrail,
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
|
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
|
||||||
InternalMessage message = new InternalMessage();
|
InternalMessage message = new InternalMessage();
|
||||||
|
|
||||||
@ -505,7 +447,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
.putArray(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
|
.putArray(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
|
||||||
.build();
|
.build();
|
||||||
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
||||||
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
|
service = new AuthenticationService(settings, realms, auditTrail,
|
||||||
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
|
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
|
||||||
|
|
||||||
InternalMessage message = new InternalMessage();
|
InternalMessage message = new InternalMessage();
|
||||||
@ -778,19 +720,11 @@ public class AuthenticationServiceTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assertThreadContextContainsAuthentication(Authentication authentication) throws IOException {
|
void assertThreadContextContainsAuthentication(Authentication authentication) throws IOException {
|
||||||
assertThreadContextContainsAuthentication(authentication, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assertThreadContextContainsAuthentication(Authentication authentication, boolean sign) throws IOException {
|
|
||||||
Authentication contextAuth = threadContext.getTransient(Authentication.AUTHENTICATION_KEY);
|
Authentication contextAuth = threadContext.getTransient(Authentication.AUTHENTICATION_KEY);
|
||||||
assertThat(contextAuth, notNullValue());
|
assertThat(contextAuth, notNullValue());
|
||||||
assertThat(contextAuth, is(authentication));
|
assertThat(contextAuth, is(authentication));
|
||||||
if (sign) {
|
|
||||||
assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) "_signed_auth"));
|
|
||||||
} else {
|
|
||||||
assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) authentication.encode()));
|
assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) authentication.encode()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void mockAuthenticate(Realm realm, AuthenticationToken token, User user) {
|
private void mockAuthenticate(Realm realm, AuthenticationToken token, User user) {
|
||||||
doAnswer((i) -> {
|
doAnswer((i) -> {
|
||||||
|
@ -33,7 +33,6 @@ import org.elasticsearch.common.collect.MapBuilder;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||||
import org.elasticsearch.env.Environment;
|
|
||||||
import org.elasticsearch.index.get.GetResult;
|
import org.elasticsearch.index.get.GetResult;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
@ -45,7 +44,6 @@ import org.elasticsearch.xpack.security.SecurityTemplateService;
|
|||||||
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheAction;
|
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheAction;
|
||||||
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheRequest;
|
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.ElasticUser;
|
import org.elasticsearch.xpack.security.user.ElasticUser;
|
||||||
import org.elasticsearch.xpack.security.user.KibanaUser;
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
|
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
|
||||||
@ -82,8 +80,7 @@ public class NativeRealmMigratorTests extends ESTestCase {
|
|||||||
ThreadPool threadPool = mock(ThreadPool.class);
|
ThreadPool threadPool = mock(ThreadPool.class);
|
||||||
when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY));
|
when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY));
|
||||||
|
|
||||||
internalClient = new InternalClient(Settings.EMPTY, threadPool, mockClient,
|
internalClient = new InternalClient(Settings.EMPTY, threadPool, mockClient);
|
||||||
new CryptoService(Settings.EMPTY, new Environment(Settings.builder().put("path.home", createTempDir()).build())));
|
|
||||||
doAnswer(invocationOnMock -> {
|
doAnswer(invocationOnMock -> {
|
||||||
SearchRequest request = (SearchRequest) invocationOnMock.getArguments()[1];
|
SearchRequest request = (SearchRequest) invocationOnMock.getArguments()[1];
|
||||||
ActionListener listener = (ActionListener) invocationOnMock.getArguments()[2];
|
ActionListener listener = (ActionListener) invocationOnMock.getArguments()[2];
|
||||||
|
@ -49,7 +49,7 @@ public class NativeUsersStoreTests extends ESTestCase {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupMocks() {
|
public void setupMocks() {
|
||||||
internalClient = new InternalClient(Settings.EMPTY, null, null, null) {
|
internalClient = new InternalClient(Settings.EMPTY, null, null) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <
|
protected <
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.crypto;
|
package org.elasticsearch.xpack.security.crypto;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -21,7 +19,6 @@ import org.junit.Before;
|
|||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
@ -46,8 +43,6 @@ public class CryptoServiceTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSigned() throws Exception {
|
public void testSigned() throws Exception {
|
||||||
// randomize whether to use a system key or not
|
|
||||||
Settings settings = randomBoolean() ? this.settings : Settings.EMPTY;
|
|
||||||
CryptoService service = new CryptoService(settings, env);
|
CryptoService service = new CryptoService(settings, env);
|
||||||
String text = randomAsciiOfLength(10);
|
String text = randomAsciiOfLength(10);
|
||||||
String signed = service.sign(text);
|
String signed = service.sign(text);
|
||||||
@ -64,11 +59,11 @@ public class CryptoServiceTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSignAndUnsignNoKeyFile() throws Exception {
|
public void testSignAndUnsignNoKeyFile() throws Exception {
|
||||||
|
Files.delete(keyFile);
|
||||||
CryptoService service = new CryptoService(Settings.EMPTY, env);
|
CryptoService service = new CryptoService(Settings.EMPTY, env);
|
||||||
final String text = randomAsciiOfLength(10);
|
final String text = randomAsciiOfLength(10);
|
||||||
String signed = service.sign(text);
|
String signed = service.sign(text);
|
||||||
// we always have some sort of key to sign with
|
assertThat(text, equalTo(signed));
|
||||||
assertThat(text, not(equalTo(signed)));
|
|
||||||
String unsigned = service.unsignAndVerify(signed);
|
String unsigned = service.unsignAndVerify(signed);
|
||||||
assertThat(unsigned, equalTo(text));
|
assertThat(unsigned, equalTo(text));
|
||||||
}
|
}
|
||||||
@ -182,17 +177,6 @@ public class CryptoServiceTests extends ESTestCase {
|
|||||||
assertThat(service.isEncrypted(service.encrypt(randomAsciiOfLength(10).toCharArray())), is(true));
|
assertThat(service.isEncrypted(service.encrypt(randomAsciiOfLength(10).toCharArray())), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSigningKeyCanBeRecomputedConsistently() {
|
|
||||||
final SecretKey systemKey = new SecretKeySpec(CryptoService.generateKey(), CryptoService.KEY_ALGO);
|
|
||||||
final SecretKey randomKey = CryptoService.generateSecretKey(CryptoService.RANDOM_KEY_SIZE);
|
|
||||||
int iterations = randomInt(100);
|
|
||||||
final SecretKey signingKey = CryptoService.createSigningKey(systemKey, randomKey);
|
|
||||||
for (int i = 0; i < iterations; i++) {
|
|
||||||
SecretKey regenerated = CryptoService.createSigningKey(systemKey, randomKey);
|
|
||||||
assertThat(regenerated, equalTo(signingKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSystemKeyFileRequired() throws Exception {
|
public void testSystemKeyFileRequired() throws Exception {
|
||||||
Files.delete(keyFile);
|
Files.delete(keyFile);
|
||||||
Settings customSettings = Settings.builder().put(settings).put("xpack.security.system_key.required", true).build();
|
Settings customSettings = Settings.builder().put(settings).put("xpack.security.system_key.required", true).build();
|
||||||
|
@ -10,7 +10,6 @@ import org.elasticsearch.action.support.DestructiveOperations;
|
|||||||
import org.elasticsearch.common.settings.ClusterSettings;
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.env.Environment;
|
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
@ -29,7 +28,6 @@ import org.elasticsearch.xpack.security.authc.Authentication;
|
|||||||
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.KibanaUser;
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
@ -57,7 +55,6 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
|||||||
private ThreadPool threadPool;
|
private ThreadPool threadPool;
|
||||||
private ThreadContext threadContext;
|
private ThreadContext threadContext;
|
||||||
private XPackLicenseState xPackLicenseState;
|
private XPackLicenseState xPackLicenseState;
|
||||||
private CryptoService cryptoService;
|
|
||||||
private SecurityContext securityContext;
|
private SecurityContext securityContext;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,8 +64,7 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
|||||||
threadPool = mock(ThreadPool.class);
|
threadPool = mock(ThreadPool.class);
|
||||||
threadContext = new ThreadContext(settings);
|
threadContext = new ThreadContext(settings);
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
||||||
cryptoService = new CryptoService(settings, new Environment(settings));
|
securityContext = spy(new SecurityContext(settings, threadPool.getThreadContext()));
|
||||||
securityContext = spy(new SecurityContext(settings, threadPool.getThreadContext(), cryptoService));
|
|
||||||
xPackLicenseState = mock(XPackLicenseState.class);
|
xPackLicenseState = mock(XPackLicenseState.class);
|
||||||
when(xPackLicenseState.isAuthAllowed()).thenReturn(true);
|
when(xPackLicenseState.isAuthAllowed()).thenReturn(true);
|
||||||
}
|
}
|
||||||
@ -99,7 +95,7 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
|||||||
public void testSendAsync() throws Exception {
|
public void testSendAsync() throws Exception {
|
||||||
final User user = new User("test");
|
final User user = new User("test");
|
||||||
final Authentication authentication = new Authentication(user, new RealmRef("ldap", "foo", "node1"), null);
|
final Authentication authentication = new Authentication(user, new RealmRef("ldap", "foo", "node1"), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, AuthenticationService.SIGN_USER_HEADER.get(settings));
|
authentication.writeToContext(threadContext);
|
||||||
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
||||||
mock(AuthenticationService.class), mock(AuthorizationService.class), xPackLicenseState, mock(SSLService.class),
|
mock(AuthenticationService.class), mock(AuthorizationService.class), xPackLicenseState, mock(SSLService.class),
|
||||||
securityContext, new DestructiveOperations(Settings.EMPTY, new ClusterSettings(Settings.EMPTY,
|
securityContext, new DestructiveOperations(Settings.EMPTY, new ClusterSettings(Settings.EMPTY,
|
||||||
@ -131,7 +127,7 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
|||||||
public void testSendAsyncSwitchToSystem() throws Exception {
|
public void testSendAsyncSwitchToSystem() throws Exception {
|
||||||
final User user = new User("test");
|
final User user = new User("test");
|
||||||
final Authentication authentication = new Authentication(user, new RealmRef("ldap", "foo", "node1"), null);
|
final Authentication authentication = new Authentication(user, new RealmRef("ldap", "foo", "node1"), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, AuthenticationService.SIGN_USER_HEADER.get(settings));
|
authentication.writeToContext(threadContext);
|
||||||
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, "indices:foo");
|
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, "indices:foo");
|
||||||
|
|
||||||
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
||||||
@ -189,7 +185,7 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
|||||||
public void testSendWithKibanaUser() throws Exception {
|
public void testSendWithKibanaUser() throws Exception {
|
||||||
final User user = new KibanaUser(true);
|
final User user = new KibanaUser(true);
|
||||||
final Authentication authentication = new Authentication(user, new RealmRef("reserved", "reserved", "node1"), null);
|
final Authentication authentication = new Authentication(user, new RealmRef("reserved", "reserved", "node1"), null);
|
||||||
authentication.writeToContext(threadContext, cryptoService, AuthenticationService.SIGN_USER_HEADER.get(settings));
|
authentication.writeToContext(threadContext);
|
||||||
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, "indices:foo");
|
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, "indices:foo");
|
||||||
|
|
||||||
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
||||||
|
@ -18,7 +18,6 @@ import org.elasticsearch.action.support.PlainActionFuture;
|
|||||||
import org.elasticsearch.common.settings.ClusterSettings;
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.env.Environment;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.transport.TransportChannel;
|
import org.elasticsearch.transport.TransportChannel;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
@ -30,7 +29,6 @@ import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
|||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.Role;
|
import org.elasticsearch.xpack.security.authz.permission.Role;
|
||||||
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
|
||||||
import org.elasticsearch.xpack.security.user.KibanaUser;
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
@ -273,15 +271,13 @@ public class ServerTransportFilterTests extends ESTestCase {
|
|||||||
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
|
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
|
||||||
ThreadContext threadContext = new ThreadContext(settings);
|
ThreadContext threadContext = new ThreadContext(settings);
|
||||||
return new ServerTransportFilter.ClientProfile(authcService, authzService, threadContext, false, destructiveOperations,
|
return new ServerTransportFilter.ClientProfile(authcService, authzService, threadContext, false, destructiveOperations,
|
||||||
reservedRealmEnabled,
|
reservedRealmEnabled, new SecurityContext(settings, threadContext));
|
||||||
new SecurityContext(settings, threadContext, new CryptoService(Settings.EMPTY, new Environment(settings))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerTransportFilter.NodeProfile getNodeFilter(boolean reservedRealmEnabled) throws IOException {
|
private ServerTransportFilter.NodeProfile getNodeFilter(boolean reservedRealmEnabled) throws IOException {
|
||||||
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
|
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
|
||||||
ThreadContext threadContext = new ThreadContext(settings);
|
ThreadContext threadContext = new ThreadContext(settings);
|
||||||
return new ServerTransportFilter.NodeProfile(authcService, authzService, threadContext, false, destructiveOperations,
|
return new ServerTransportFilter.NodeProfile(authcService, authzService, threadContext, false, destructiveOperations,
|
||||||
reservedRealmEnabled,
|
reservedRealmEnabled, new SecurityContext(settings, threadContext));
|
||||||
new SecurityContext(settings, threadContext, new CryptoService(Settings.EMPTY, new Environment(settings))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ public class IndexActionTests extends ESIntegTestCase {
|
|||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
Client client = client();
|
Client client = client();
|
||||||
InternalClient internalClient = new InternalClient(client.settings(), client.threadPool(), client, null);
|
InternalClient internalClient = new InternalClient(client.settings(), client.threadPool(), client);
|
||||||
|
|
||||||
IndexActionFactory actionParser = new IndexActionFactory(Settings.EMPTY, internalClient);
|
IndexActionFactory actionParser = new IndexActionFactory(Settings.EMPTY, internalClient);
|
||||||
XContentParser parser = createParser(builder);
|
XContentParser parser = createParser(builder);
|
||||||
@ -234,7 +234,7 @@ public class IndexActionTests extends ESIntegTestCase {
|
|||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
Client client = client();
|
Client client = client();
|
||||||
InternalClient internalClient = new InternalClient(client.settings(), client.threadPool(), client, null);
|
InternalClient internalClient = new InternalClient(client.settings(), client.threadPool(), client);
|
||||||
|
|
||||||
IndexActionFactory actionParser = new IndexActionFactory(Settings.EMPTY, internalClient);
|
IndexActionFactory actionParser = new IndexActionFactory(Settings.EMPTY, internalClient);
|
||||||
XContentParser parser = createParser(builder);
|
XContentParser parser = createParser(builder);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user