Merge branch 'master' into feature/sql
Original commit: elastic/x-pack-elasticsearch@51fc29da6a
This commit is contained in:
commit
50a46d0ed2
|
@ -113,7 +113,7 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant ->
|
||||||
try {
|
try {
|
||||||
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health").openConnection();
|
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health").openConnection();
|
||||||
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
||||||
Base64.getEncoder().encodeToString("test_admin:changeme".getBytes(StandardCharsets.UTF_8)));
|
Base64.getEncoder().encodeToString("test_admin:x-pack-test-password".getBytes(StandardCharsets.UTF_8)));
|
||||||
httpURLConnection.setRequestMethod("GET");
|
httpURLConnection.setRequestMethod("GET");
|
||||||
httpURLConnection.setConnectTimeout(1000);
|
httpURLConnection.setConnectTimeout(1000);
|
||||||
httpURLConnection.setReadTimeout(30000);
|
httpURLConnection.setReadTimeout(30000);
|
||||||
|
@ -145,7 +145,7 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant ->
|
||||||
integTestCluster {
|
integTestCluster {
|
||||||
plugin ':x-pack-elasticsearch:plugin'
|
plugin ':x-pack-elasticsearch:plugin'
|
||||||
setupCommand 'setupTestAdmin',
|
setupCommand 'setupTestAdmin',
|
||||||
'bin/x-pack/users', 'useradd', 'test_admin', '-p', 'changeme', '-r', 'superuser'
|
'bin/x-pack/users', 'useradd', 'test_admin', '-p', 'x-pack-test-password', '-r', 'superuser'
|
||||||
waitCondition = waitWithAuth
|
waitCondition = waitWithAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
:es-repo-dir: {docdir}/../../../../elasticsearch/docs
|
:es-repo-dir: {docdir}/../../../../elasticsearch/docs
|
||||||
:es-test-dir: {docdir}/../../../../elasticsearch/docs/src/test
|
:es-test-dir: {docdir}/../../../../elasticsearch/docs/src/test
|
||||||
:plugins-examples-dir: {docdir}/../../../../elasticsearch/plugins/examples
|
:plugins-examples-dir: {docdir}/../../../../elasticsearch/plugins/examples
|
||||||
:docs-dir: {docdir}/../../../../docs
|
|
||||||
|
|
||||||
include::{es-repo-dir}/Versions.asciidoc[]
|
include::{es-repo-dir}/Versions.asciidoc[]
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,8 @@ make it easier to control which users have authority to view and manage the jobs
|
||||||
{dfeeds}, and results.
|
{dfeeds}, and results.
|
||||||
|
|
||||||
By default, you can perform all of the steps in this tutorial by using the
|
By default, you can perform all of the steps in this tutorial by using the
|
||||||
built-in `elastic` super user. The default password for the `elastic` user is
|
built-in `elastic` super user. However, the password must be set before the user
|
||||||
`changeme`. For information about how to change that password, see
|
can do anything. For information about how to set that password, see
|
||||||
<<security-getting-started>>.
|
<<security-getting-started>>.
|
||||||
|
|
||||||
If you are performing these steps in a production environment, take extra care
|
If you are performing these steps in a production environment, take extra care
|
||||||
|
@ -191,7 +191,7 @@ mapping for the data set:
|
||||||
[source,shell]
|
[source,shell]
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
curl -u elastic:changeme -X PUT -H 'Content-Type: application/json'
|
curl -u elastic:x-pack-test-password -X PUT -H 'Content-Type: application/json'
|
||||||
http://localhost:9200/server-metrics -d '{
|
http://localhost:9200/server-metrics -d '{
|
||||||
"settings":{
|
"settings":{
|
||||||
"number_of_shards":1,
|
"number_of_shards":1,
|
||||||
|
@ -227,7 +227,7 @@ http://localhost:9200/server-metrics -d '{
|
||||||
}'
|
}'
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
NOTE: If you run this command, you must replace `changeme` with your
|
NOTE: If you run this command, you must replace `x-pack-test-password` with your
|
||||||
actual password.
|
actual password.
|
||||||
|
|
||||||
////
|
////
|
||||||
|
@ -247,16 +247,16 @@ example, which loads the four JSON files:
|
||||||
[source,shell]
|
[source,shell]
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
curl -u elastic:changeme -X POST -H "Content-Type: application/json"
|
curl -u elastic:x-pack-test-password -X POST -H "Content-Type: application/json"
|
||||||
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_1.json"
|
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_1.json"
|
||||||
|
|
||||||
curl -u elastic:changeme -X POST -H "Content-Type: application/json"
|
curl -u elastic:x-pack-test-password -X POST -H "Content-Type: application/json"
|
||||||
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_2.json"
|
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_2.json"
|
||||||
|
|
||||||
curl -u elastic:changeme -X POST -H "Content-Type: application/json"
|
curl -u elastic:x-pack-test-password -X POST -H "Content-Type: application/json"
|
||||||
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_3.json"
|
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_3.json"
|
||||||
|
|
||||||
curl -u elastic:changeme -X POST -H "Content-Type: application/json"
|
curl -u elastic:x-pack-test-password -X POST -H "Content-Type: application/json"
|
||||||
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_4.json"
|
http://localhost:9200/server-metrics/_bulk --data-binary "@server-metrics_4.json"
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ You can verify that the data was loaded successfully with the following command:
|
||||||
[source,shell]
|
[source,shell]
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
curl 'http://localhost:9200/_cat/indices?v' -u elastic:changeme
|
curl 'http://localhost:9200/_cat/indices?v' -u elastic:x-pack-test-password
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
You should see output similar to the following:
|
You should see output similar to the following:
|
||||||
|
|
|
@ -13,7 +13,7 @@ To change the password of the logged in user, submit a POST request to the
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
POST _xpack/security/user/elastic/_password
|
POST _xpack/security/user/elastic/_password
|
||||||
{
|
{
|
||||||
"password": "changeme"
|
"password": "x-pack-test-password"
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
// CONSOLE
|
// CONSOLE
|
||||||
|
|
|
@ -16,8 +16,8 @@ endpoint.
|
||||||
POST /_xpack/security/oauth2/token
|
POST /_xpack/security/oauth2/token
|
||||||
{
|
{
|
||||||
"grant_type" : "password",
|
"grant_type" : "password",
|
||||||
"username" : "elastic",
|
"username" : "test_admin",
|
||||||
"password" : "changeme"
|
"password" : "x-pack-test-password"
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
// CONSOLE
|
// CONSOLE
|
||||||
|
|
|
@ -15,9 +15,11 @@ see <<managing-native-users, Managing Native Users>>.
|
||||||
=== Built-in Users
|
=== Built-in Users
|
||||||
|
|
||||||
{security} provides built-in user credentials to help you get up and running.
|
{security} provides built-in user credentials to help you get up and running.
|
||||||
These users have a fixed set of privileges and the default password `changeme`.
|
These users have a fixed set of privileges and cannot be authenticated until their
|
||||||
Please read <<reset-built-in-user-passwords,Reset Built-in User Passwords>> and
|
passwords have been set. The exception is the `elastic` user which can be authenticated
|
||||||
<<disabling-default-password, Disable Default Password Functionality>> below.
|
from a localhost rest request with an empty password. Until a password is set, the elastic
|
||||||
|
user is only authorized to perform change password requests.
|
||||||
|
Please read <<reset-built-in-user-passwords,Reset Built-in User Passwords>> below.
|
||||||
|
|
||||||
.{security} Built-in Users
|
.{security} Built-in Users
|
||||||
|========
|
|========
|
||||||
|
@ -48,8 +50,7 @@ be disabled individually, using the
|
||||||
==== Reset Built-in User Passwords
|
==== Reset Built-in User Passwords
|
||||||
[IMPORTANT]
|
[IMPORTANT]
|
||||||
=============================================================================
|
=============================================================================
|
||||||
You must reset the default passwords for all built-in users, and then
|
You must set the passwords for all built-in users.
|
||||||
<<disabling-default-password, disable default password support>>.
|
|
||||||
You can update passwords from the *Management > Users* UI in Kibana or with the
|
You can update passwords from the *Management > Users* UI in Kibana or with the
|
||||||
{ref}/security-api-users.html#security-api-reset-user-password[Reset Password API]:
|
{ref}/security-api-users.html#security-api-reset-user-password[Reset Password API]:
|
||||||
|
|
||||||
|
@ -117,14 +118,8 @@ PUT _xpack/security/user/logstash_system/_enable
|
||||||
==== Disable Default Password Functionality
|
==== Disable Default Password Functionality
|
||||||
[IMPORTANT]
|
[IMPORTANT]
|
||||||
=============================================================================
|
=============================================================================
|
||||||
The default password of `changeme` is provided as a convenience that allows you to quickly
|
This setting is deprecated. The elastic user no longer has a default password. The password must
|
||||||
setup your Elasticsearch stack. It should not be used when running in production.
|
be set before the user can be used.
|
||||||
|
|
||||||
Once you have changed the password for the built-in users, you must disable default password support
|
|
||||||
by setting `xpack.security.authc.accept_default_password` to `false`.
|
|
||||||
|
|
||||||
A {ref}/bootstrap-checks.html[bootstrap check] will prevent your cluster from operating in production
|
|
||||||
mode until you make this configuration change.
|
|
||||||
|
|
||||||
=============================================================================
|
=============================================================================
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ xpack:
|
||||||
order: 0
|
order: 0
|
||||||
url: "ldaps://ldap.example.com:636"
|
url: "ldaps://ldap.example.com:636"
|
||||||
bind_dn: "cn=ldapuser, ou=users, o=services, dc=example, dc=com"
|
bind_dn: "cn=ldapuser, ou=users, o=services, dc=example, dc=com"
|
||||||
bind_password: changeme
|
bind_password: x-pack-test-password
|
||||||
user_search:
|
user_search:
|
||||||
base_dn: "dc=example,dc=com"
|
base_dn: "dc=example,dc=com"
|
||||||
attribute: cn
|
attribute: cn
|
||||||
|
|
|
@ -21,7 +21,7 @@ Run the migrate tool after you install the X-Pack plugin. For example:
|
||||||
|
|
||||||
[source, sh]
|
[source, sh]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
$ bin/x-pack/migrate native -U http://localhost:9200 -u elastic -p changeme
|
$ bin/x-pack/migrate native -U http://localhost:9200 -u elastic -p x-pack-test-password
|
||||||
-n lee,foo -r role1,role2,role3,role4,foo
|
-n lee,foo -r role1,role2,role3,role4,foo
|
||||||
starting migration of users and roles...
|
starting migration of users and roles...
|
||||||
importing users from [/home/es/config/shield/users]...
|
importing users from [/home/es/config/shield/users]...
|
||||||
|
@ -69,5 +69,5 @@ to specify a different configuration directory, the command would look like:
|
||||||
|
|
||||||
[source, sh]
|
[source, sh]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
$ bin/x-pack/migrate native -U http://localhost:9200 -u elastic -p changeme --path.conf /etc/elasticsearch
|
$ bin/x-pack/migrate native -U http://localhost:9200 -u elastic -p x-pack-test-password --path.conf /etc/elasticsearch
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
|
@ -80,7 +80,7 @@ xpack:
|
||||||
type: pki
|
type: pki
|
||||||
truststore:
|
truststore:
|
||||||
path: "/path/to/pki_truststore.jks"
|
path: "/path/to/pki_truststore.jks"
|
||||||
password: "changeme"
|
password: "x-pack-test-password"
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
. Restart Elasticsearch.
|
. Restart Elasticsearch.
|
||||||
|
|
|
@ -36,7 +36,8 @@ curl -XPUT -u elastic 'localhost:9200/_xpack/security/user/logstash_system/_pass
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
// NOTCONSOLE
|
// NOTCONSOLE
|
||||||
|
|
||||||
NOTE: The default password for the `elastic` user is `changeme`.
|
NOTE: By default, the `elastic` user does not have a password set. Until its password is set, the `elastic` user will only be
|
||||||
|
allowed to submit change password rest requests from localhost.
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
||||||
|
@ -72,23 +73,6 @@ curl -XPOST -u elastic 'localhost:9200/_xpack/security/user/johndoe' -H "Content
|
||||||
// NOTCONSOLE
|
// NOTCONSOLE
|
||||||
--
|
--
|
||||||
|
|
||||||
[[enable-message-authentication]]
|
|
||||||
. Enable message authentication to verify that messages are not tampered with or corrupted in transit:
|
|
||||||
.. Run the `syskeygen` tool from `ES_HOME` without any options:
|
|
||||||
+
|
|
||||||
[source, shell]
|
|
||||||
----------------
|
|
||||||
bin/x-pack/syskeygen
|
|
||||||
----------------
|
|
||||||
+
|
|
||||||
This creates a system key file in `CONFIG_DIR/x-pack/system_key`.
|
|
||||||
|
|
||||||
.. Copy the generated system key to the rest of the nodes in the cluster.
|
|
||||||
+
|
|
||||||
IMPORTANT: The system key is a symmetric key, so the same key must be on every
|
|
||||||
node in the cluster.
|
|
||||||
|
|
||||||
|
|
||||||
[[enable-auditing]]
|
[[enable-auditing]]
|
||||||
. Enable Auditing to keep track of attempted and successful interactions with
|
. Enable Auditing to keep track of attempted and successful interactions with
|
||||||
your Elasticsearch cluster:
|
your Elasticsearch cluster:
|
||||||
|
|
|
@ -52,11 +52,8 @@ A critical part of security is keeping confidential data confidential.
|
||||||
Elasticsearch has built-in protections against accidental data loss and
|
Elasticsearch has built-in protections against accidental data loss and
|
||||||
corruption. However, there's nothing to stop deliberate tampering or data
|
corruption. However, there's nothing to stop deliberate tampering or data
|
||||||
interception. {security} preserves the integrity of your data by
|
interception. {security} preserves the integrity of your data by
|
||||||
<<ssl-tls, encrypting communications>> to and from nodes and
|
<<ssl-tls, encrypting communications>> to and from nodes.
|
||||||
<<enable-message-authentication, authenticating messages>> to verify that they
|
For even greater protection, you can increase the <<ciphers, encryption strength>> and
|
||||||
have not been tampered with or corrupted in transit during node-to-node
|
|
||||||
communication. For even greater protection, you can increase the
|
|
||||||
<<ciphers, encryption strength>> and
|
|
||||||
<<separating-node-client-traffic, separate client traffic from node-to-node communications>>.
|
<<separating-node-client-traffic, separate client traffic from node-to-node communications>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,6 @@ The {security} uses the following files:
|
||||||
* `CONFIG_DIR/x-pack/log4j2.properties` contains audit information (read more
|
* `CONFIG_DIR/x-pack/log4j2.properties` contains audit information (read more
|
||||||
<<logging-file, here>>).
|
<<logging-file, here>>).
|
||||||
|
|
||||||
* `CONFIG_DIR/x-pack/system_key` holds a cluster secret key that's used to
|
|
||||||
authenticate messages during node to node communication. For more information,
|
|
||||||
see <<enable-message-authentication, Enabling Message Authentication>>.
|
|
||||||
|
|
||||||
[[security-files-location]]
|
[[security-files-location]]
|
||||||
|
|
||||||
IMPORTANT: Any files that {security} uses must be stored in the Elasticsearch
|
IMPORTANT: Any files that {security} uses must be stored in the Elasticsearch
|
||||||
|
|
|
@ -102,7 +102,7 @@ March 15, 2016
|
||||||
|
|
||||||
.Bug Fixes
|
.Bug Fixes
|
||||||
* Enable <<field-and-document-access-control,document and field level security>> by default.
|
* Enable <<field-and-document-access-control,document and field level security>> by default.
|
||||||
* Fix issues with <<enable-message-authentication,message authentication>> on certain JDKs that do not support cloning message
|
* Fix issues with message authentication on certain JDKs that do not support cloning message
|
||||||
authentication codes.
|
authentication codes.
|
||||||
* Built in <<setting-up-authentication, realms>> no longer throw an exception if the `Authorization` header does not contain a basic
|
* Built in <<setting-up-authentication, realms>> no longer throw an exception if the `Authorization` header does not contain a basic
|
||||||
authentication token.
|
authentication token.
|
||||||
|
@ -209,7 +209,7 @@ the correct user to index the audit events.
|
||||||
July 21, 2015
|
July 21, 2015
|
||||||
|
|
||||||
.Bug Fixes
|
.Bug Fixes
|
||||||
* Fixes <<enable-message-authentication,message authentication>> serialization to work with Shield 1.2.1 and earlier.
|
* Fixes message authentication serialization to work with Shield 1.2.1 and earlier.
|
||||||
** NOTE: if you are upgrading from Shield 1.3.0 or Shield 1.2.2 a {ref-17}/setup-upgrade.html#restart-upgrade[cluster restart upgrade]
|
** NOTE: if you are upgrading from Shield 1.3.0 or Shield 1.2.2 a {ref-17}/setup-upgrade.html#restart-upgrade[cluster restart upgrade]
|
||||||
will be necessary. When upgrading from other versions of Shield, follow the normal upgrade procedure.
|
will be necessary. When upgrading from other versions of Shield, follow the normal upgrade procedure.
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ June 24, 2015
|
||||||
July 21, 2015
|
July 21, 2015
|
||||||
|
|
||||||
.Bug Fixes
|
.Bug Fixes
|
||||||
* Fixes <<enable-message-authentication,message authentication>> serialization to work with Shield 1.2.1 and earlier.
|
* Fixes message authentication serialization to work with Shield 1.2.1 and earlier.
|
||||||
** NOTE: if you are upgrading from Shield 1.2.2 a {ref-17}/setup-upgrade.html#restart-upgrade[cluster restart upgrade]
|
** NOTE: if you are upgrading from Shield 1.2.2 a {ref-17}/setup-upgrade.html#restart-upgrade[cluster restart upgrade]
|
||||||
will be necessary. When upgrading from other versions of Shield, follow the normal upgrade procedure.
|
will be necessary. When upgrading from other versions of Shield, follow the normal upgrade procedure.
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,11 @@ for the client traffic by adding the following to `elasticsearch.yml`:
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
transport.profiles.client.xpack.security.ssl.truststore:
|
transport.profiles.client.xpack.security.ssl.truststore:
|
||||||
path: /path/to/another/truststore
|
path: /path/to/another/truststore
|
||||||
password: changeme
|
password: x-pack-test-password
|
||||||
|
|
||||||
transport.profiles.client.xpack.security.ssl.keystore:
|
transport.profiles.client.xpack.security.ssl.keystore:
|
||||||
path: /path/to/another/keystore
|
path: /path/to/another/keystore
|
||||||
password: changeme
|
password: x-pack-test-password
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
To change the default behavior that requires certificates for transport clients,
|
To change the default behavior that requires certificates for transport clients,
|
||||||
|
|
|
@ -57,7 +57,7 @@ users from the **Management / Users** UI in Kibana or through the
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
POST /_xpack/security/user/packetbeat_internal
|
POST /_xpack/security/user/packetbeat_internal
|
||||||
{
|
{
|
||||||
"password" : "changeme",
|
"password" : "x-pack-test-password",
|
||||||
"roles" : [ "packetbeat_writer"],
|
"roles" : [ "packetbeat_writer"],
|
||||||
"full_name" : "Internal Packetbeat User"
|
"full_name" : "Internal Packetbeat User"
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ output.elasticsearch:
|
||||||
hosts: ["localhost:9200"]
|
hosts: ["localhost:9200"]
|
||||||
index: "packetbeat"
|
index: "packetbeat"
|
||||||
username: "packetbeat_internal"
|
username: "packetbeat_internal"
|
||||||
password: "changeme"
|
password: "x-pack-test-password"
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
.. To use PKI authentication, configure the `certificate` and
|
.. To use PKI authentication, configure the `certificate` and
|
||||||
|
@ -144,7 +144,7 @@ the `packetbeat_reader` role:
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
POST /_xpack/security/user/packetbeat_user
|
POST /_xpack/security/user/packetbeat_user
|
||||||
{
|
{
|
||||||
"password" : "changeme",
|
"password" : "x-pack-test-password",
|
||||||
"roles" : [ "packetbeat_reader"],
|
"roles" : [ "packetbeat_reader"],
|
||||||
"full_name" : "Packetbeat User"
|
"full_name" : "Packetbeat User"
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
|
||||||
|
|
||||||
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
||||||
.put("cluster.name", "myClusterName")
|
.put("cluster.name", "myClusterName")
|
||||||
.put("xpack.security.user", "transport_client_user:changeme")
|
.put("xpack.security.user", "transport_client_user:x-pack-test-password")
|
||||||
...
|
...
|
||||||
.build())
|
.build())
|
||||||
.addTransportAddress(new InetSocketTransportAddress("localhost", 9300))
|
.addTransportAddress(new InetSocketTransportAddress("localhost", 9300))
|
||||||
|
@ -169,14 +169,14 @@ import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordTok
|
||||||
|
|
||||||
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
||||||
.put("cluster.name", "myClusterName")
|
.put("cluster.name", "myClusterName")
|
||||||
.put("xpack.security.user", "transport_client_user:changeme")
|
.put("xpack.security.user", "transport_client_user:x-pack-test-password")
|
||||||
...
|
...
|
||||||
.build())
|
.build())
|
||||||
.build()
|
.build()
|
||||||
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300))
|
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300))
|
||||||
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9301))
|
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9301))
|
||||||
|
|
||||||
String token = basicAuthHeaderValue("test_user", new SecureString("changeme".toCharArray()));
|
String token = basicAuthHeaderValue("test_user", new SecureString("x-pack-test-password".toCharArray()));
|
||||||
|
|
||||||
client.filterWithHeader(Collections.singletonMap("Authorization", token))
|
client.filterWithHeader(Collections.singletonMap("Authorization", token))
|
||||||
.prepareSearch().get();
|
.prepareSearch().get();
|
||||||
|
@ -198,7 +198,7 @@ import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
|
||||||
|
|
||||||
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
||||||
.put("cluster.name", "myClusterName")
|
.put("cluster.name", "myClusterName")
|
||||||
.put("xpack.security.user", "transport_client_user:changeme")
|
.put("xpack.security.user", "transport_client_user:x-pack-test-password")
|
||||||
.put("xpack.ssl.key", "/path/to/client.key")
|
.put("xpack.ssl.key", "/path/to/client.key")
|
||||||
.put("xpack.ssl.certificate", "/path/to/client.crt")
|
.put("xpack.ssl.certificate", "/path/to/client.crt")
|
||||||
.put("xpack.ssl.certificate_authorities", "/path/to/ca.crt")
|
.put("xpack.ssl.certificate_authorities", "/path/to/ca.crt")
|
||||||
|
@ -217,7 +217,7 @@ import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
|
||||||
|
|
||||||
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
||||||
.put("cluster.name", "myClusterName")
|
.put("cluster.name", "myClusterName")
|
||||||
.put("xpack.security.user", "transport_client_user:changeme")
|
.put("xpack.security.user", "transport_client_user:x-pack-test-password")
|
||||||
.put("xpack.ssl.key", "/path/to/client.key")
|
.put("xpack.ssl.key", "/path/to/client.key")
|
||||||
.put("xpack.ssl.certificate", "/path/to/client.crt")
|
.put("xpack.ssl.certificate", "/path/to/client.crt")
|
||||||
.put("xpack.ssl.certificate_authorities", "/path/to/ca.crt")
|
.put("xpack.ssl.certificate_authorities", "/path/to/ca.crt")
|
||||||
|
@ -246,7 +246,7 @@ import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
|
||||||
|
|
||||||
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
|
||||||
.put("cluster.name", "myClusterName")
|
.put("cluster.name", "myClusterName")
|
||||||
.put("xpack.security.user", "test_user:changeme")
|
.put("xpack.security.user", "test_user:x-pack-test-password")
|
||||||
.put("xpack.ssl.certificate_authorities", "/path/to/ca.crt")
|
.put("xpack.ssl.certificate_authorities", "/path/to/ca.crt")
|
||||||
.put("xpack.security.transport.ssl.enabled", "true")
|
.put("xpack.security.transport.ssl.enabled", "true")
|
||||||
...
|
...
|
||||||
|
|
|
@ -27,8 +27,8 @@ To use Kibana with {security}:
|
||||||
requests as this user to access the cluster monitoring APIs and the `.kibana` index.
|
requests as this user to access the cluster monitoring APIs and the `.kibana` index.
|
||||||
The server does _not_ need access to user indices.
|
The server does _not_ need access to user indices.
|
||||||
+
|
+
|
||||||
By default, the `kibana` user password is set to `changeme`. Change this password
|
By default, the `kibana` does not have a password. The user will not be enabled until
|
||||||
through the reset password API:
|
a password is set. Set the password through the reset password API:
|
||||||
+
|
+
|
||||||
[source,shell]
|
[source,shell]
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
|
@ -61,7 +61,7 @@ the `user` API:
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
POST _xpack/security/user/logstash_internal
|
POST _xpack/security/user/logstash_internal
|
||||||
{
|
{
|
||||||
"password" : "changeme",
|
"password" : "x-pack-test-password",
|
||||||
"roles" : [ "logstash_writer"],
|
"roles" : [ "logstash_writer"],
|
||||||
"full_name" : "Internal Logstash User"
|
"full_name" : "Internal Logstash User"
|
||||||
}
|
}
|
||||||
|
@ -76,18 +76,18 @@ plugins in your Logstash `.conf` file. For example:
|
||||||
input {
|
input {
|
||||||
...
|
...
|
||||||
user => logstash_internal
|
user => logstash_internal
|
||||||
password => changeme
|
password => x-pack-test-password
|
||||||
}
|
}
|
||||||
filter {
|
filter {
|
||||||
...
|
...
|
||||||
user => logstash_internal
|
user => logstash_internal
|
||||||
password => changeme
|
password => x-pack-test-password
|
||||||
}
|
}
|
||||||
output {
|
output {
|
||||||
elasticsearch {
|
elasticsearch {
|
||||||
...
|
...
|
||||||
user => logstash_internal
|
user => logstash_internal
|
||||||
password => changeme
|
password => x-pack-test-password
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ the `user` API:
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
POST _xpack/security/user/logstash_user
|
POST _xpack/security/user/logstash_user
|
||||||
{
|
{
|
||||||
"password" : "changeme",
|
"password" : "x-pack-test-password",
|
||||||
"roles" : [ "logstash_reader"],
|
"roles" : [ "logstash_reader"],
|
||||||
"full_name" : "Kibana User"
|
"full_name" : "Kibana User"
|
||||||
}
|
}
|
||||||
|
@ -187,8 +187,8 @@ This user has the minimum permissions necessary for the monitoring function, and
|
||||||
_should not_ be used for any other purpose - it is specifically _not intended_ for
|
_should not_ be used for any other purpose - it is specifically _not intended_ for
|
||||||
use within a Logstash pipeline.
|
use within a Logstash pipeline.
|
||||||
|
|
||||||
By default, the `logstash_system` user password is set to `changeme`.
|
By default, the `logstash_system` does not have a password. The user will not be enabled until
|
||||||
Change this password through the reset password API:
|
a password is set. Set the password through the reset password API:
|
||||||
|
|
||||||
[source,js]
|
[source,js]
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
|
|
@ -152,7 +152,7 @@ for the agent user:
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
xpack.monitoring.elasticsearch.url: ["http://es-mon-1:9200", "http://es-mon2:9200"]
|
xpack.monitoring.elasticsearch.url: ["http://es-mon-1:9200", "http://es-mon2:9200"]
|
||||||
xpack.monitoring.elasticsearch.username: "remote_monitor"
|
xpack.monitoring.elasticsearch.username: "remote_monitor"
|
||||||
xpack.monitoring.elasticsearch.password: "changeme"
|
xpack.monitoring.elasticsearch.password: "x-pack-test-password"
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
.. If SSL/TLS is enabled on the monitoring cluster:
|
.. If SSL/TLS is enabled on the monitoring cluster:
|
||||||
|
@ -176,6 +176,6 @@ Alternatively, you can configure trusted certificates using a truststore
|
||||||
[source,yaml]
|
[source,yaml]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
xpack.monitoring.elasticsearch.ssl.truststore.path: /path/to/file
|
xpack.monitoring.elasticsearch.ssl.truststore.path: /path/to/file
|
||||||
xpack.monitoring.elasticsearch.ssl.truststore.password: changeme
|
xpack.monitoring.elasticsearch.ssl.truststore.password: x-pack-test-password
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
--
|
--
|
|
@ -24,7 +24,7 @@ the following request creates a `reporter` user that has the
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
POST /_xpack/security/user/reporter
|
POST /_xpack/security/user/reporter
|
||||||
{
|
{
|
||||||
"password" : "changeme",
|
"password" : "x-pack-test-password",
|
||||||
"roles" : ["kibana_user", "reporting_user"],
|
"roles" : ["kibana_user", "reporting_user"],
|
||||||
"full_name" : "Reporting User"
|
"full_name" : "Reporting User"
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,6 @@ To use a tribe node with secured clusters:
|
||||||
|
|
||||||
. Install {xpack} on the tribe node and every node in each connected cluster.
|
. Install {xpack} on the tribe node and every node in each connected cluster.
|
||||||
|
|
||||||
. Enable <<enable-message-authentication, message authentication>> globally.
|
|
||||||
Generate a system key on one node and copy it to the tribe node and every other
|
|
||||||
node in each of the connected clusters.
|
|
||||||
+
|
|
||||||
IMPORTANT: For message authentication to work properly across multiple clusters,
|
|
||||||
the tribe node and all of the connected clusters must share the same
|
|
||||||
system key. {security} reads the system key from `CONFIG_DIR/x-pack/system_key`.
|
|
||||||
|
|
||||||
. Enable encryption globally. To encrypt communications, you must enable
|
. Enable encryption globally. To encrypt communications, you must enable
|
||||||
<<ssl-tls,enable SSL/TLS>> on every node.
|
<<ssl-tls,enable SSL/TLS>> on every node.
|
||||||
+
|
+
|
||||||
|
|
|
@ -21,8 +21,6 @@ Configure in both `elasticsearch.yml` and `kibana.yml`.
|
||||||
==== Default Password Security Settings
|
==== Default Password Security Settings
|
||||||
`xpack.security.authc.accept_default_password`::
|
`xpack.security.authc.accept_default_password`::
|
||||||
In `elasticsearch.yml`, set this to `false` to disable support for the default "changeme" password.
|
In `elasticsearch.yml`, set this to `false` to disable support for the default "changeme" password.
|
||||||
For more information, see {xpack-ref}/setting-up-authentication.html#disabling-default-password[
|
|
||||||
Disable Default Password Functionality].
|
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
[[anonymous-access-settings]]
|
[[anonymous-access-settings]]
|
||||||
|
|
|
@ -88,7 +88,7 @@ PUT _xpack/watcher/watch/cluster_health_watch
|
||||||
"auth": {
|
"auth": {
|
||||||
"basic": {
|
"basic": {
|
||||||
"username": "elastic",
|
"username": "elastic",
|
||||||
"password": "changeme"
|
"password": "x-pack-test-password"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ output { <2>
|
||||||
elasticsearch {
|
elasticsearch {
|
||||||
hosts => "http://localhost:9200"
|
hosts => "http://localhost:9200"
|
||||||
user => "elastic"
|
user => "elastic"
|
||||||
password => "changeme"
|
password => "x-pack-test-password"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import static java.util.Collections.singletonMap;
|
||||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
|
||||||
public class XDocsClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
|
public class XDocsClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
|
||||||
private static final String USER_TOKEN = basicAuthHeaderValue("test_admin", new SecureString("changeme".toCharArray()));
|
private static final String USER_TOKEN = basicAuthHeaderValue("test_admin", new SecureString("x-pack-test-password".toCharArray()));
|
||||||
|
|
||||||
public XDocsClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
|
public XDocsClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
|
||||||
super(testCandidate);
|
super(testCandidate);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/bin/
|
|
@ -98,7 +98,7 @@ if [ -e "$CONF_DIR" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$ES_HOME" > /dev/null
|
cd "$ES_HOME" > /dev/null
|
||||||
"$JAVA" $ES_JAVA_OPTS -Des.path.home="$ES_HOME" -cp "$ES_CLASSPATH" org.elasticsearch.xpack.security.crypto.tool.SystemKeyTool $properties "${args[@]}"
|
"$JAVA" $ES_JAVA_OPTS -Des.path.home="$ES_HOME" -cp "$ES_CLASSPATH" org.elasticsearch.common.settings.EncKeyTool $properties "${args[@]}"
|
||||||
status=$?
|
status=$?
|
||||||
cd - > /dev/null
|
cd - > /dev/null
|
||||||
exit $status
|
exit $status
|
||||||
|
|
|
@ -5,5 +5,5 @@ rem or more contributor license agreements. Licensed under the Elastic License;
|
||||||
rem you may not use this file except in compliance with the Elastic License.
|
rem you may not use this file except in compliance with the Elastic License.
|
||||||
|
|
||||||
PUSHD "%~dp0"
|
PUSHD "%~dp0"
|
||||||
CALL "%~dp0.in.bat" org.elasticsearch.xpack.security.crypto.tool.SystemKeyTool %*
|
CALL "%~dp0.in.bat" org.elasticsearch.common.settings.EncKeyTool %*
|
||||||
POPD
|
POPD
|
|
@ -210,13 +210,50 @@ integTestCluster {
|
||||||
|
|
||||||
waitCondition = { NodeInfo node, AntBuilder ant ->
|
waitCondition = { NodeInfo node, AntBuilder ant ->
|
||||||
File tmpFile = new File(node.cwd, 'wait.success')
|
File tmpFile = new File(node.cwd, 'wait.success')
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
HttpURLConnection httpURLConnection = null;
|
||||||
|
try {
|
||||||
|
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_xpack/security/user/elastic/_password")
|
||||||
|
.openConnection();
|
||||||
|
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
||||||
|
Base64.getEncoder().encodeToString("elastic:".getBytes(StandardCharsets.UTF_8)));
|
||||||
|
httpURLConnection.setRequestMethod("PUT");
|
||||||
|
httpURLConnection.setDoOutput(true);
|
||||||
|
httpURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
||||||
|
|
||||||
|
httpURLConnection.connect();
|
||||||
|
OutputStream out = httpURLConnection.getOutputStream();
|
||||||
|
out.write("{\"password\": \"x-pack-test-password\"}".getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.close()
|
||||||
|
|
||||||
|
if (httpURLConnection.getResponseCode() == 200) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
httpURLConnection.disconnect()
|
||||||
|
if (i == 9) {
|
||||||
|
logger.error("final attempt to set elastic password", e)
|
||||||
|
} else {
|
||||||
|
logger.debug("failed to set elastic password", e)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (httpURLConnection != null) {
|
||||||
|
httpURLConnection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// did not start, so wait a bit before trying again
|
||||||
|
Thread.sleep(500L);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
// we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned
|
// we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned
|
||||||
HttpURLConnection httpURLConnection = null;
|
HttpURLConnection httpURLConnection = null;
|
||||||
try {
|
try {
|
||||||
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=${numNodes}&wait_for_status=yellow").openConnection();
|
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=${numNodes}&wait_for_status=yellow").openConnection();
|
||||||
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
||||||
Base64.getEncoder().encodeToString("elastic:changeme".getBytes(StandardCharsets.UTF_8)));
|
Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8)));
|
||||||
httpURLConnection.setRequestMethod("GET");
|
httpURLConnection.setRequestMethod("GET");
|
||||||
httpURLConnection.connect();
|
httpURLConnection.connect();
|
||||||
if (httpURLConnection.getResponseCode() == 200) {
|
if (httpURLConnection.getResponseCode() == 200) {
|
||||||
|
|
|
@ -97,6 +97,7 @@ import org.elasticsearch.xpack.security.Security;
|
||||||
import org.elasticsearch.xpack.security.SecurityFeatureSet;
|
import org.elasticsearch.xpack.security.SecurityFeatureSet;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
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.sql.plugin.SqlPlugin;
|
import org.elasticsearch.xpack.sql.plugin.SqlPlugin;
|
||||||
import org.elasticsearch.xpack.ssl.SSLConfigurationReloader;
|
import org.elasticsearch.xpack.ssl.SSLConfigurationReloader;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
|
@ -122,9 +123,12 @@ import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.security.auth.DestroyFailedException;
|
import javax.security.auth.DestroyFailedException;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.watcher.Watcher.ENCRYPT_SENSITIVE_DATA_SETTING;
|
||||||
|
|
||||||
public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, IngestPlugin, NetworkPlugin {
|
public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
|
|
||||||
public static final String NAME = "x-pack";
|
public static final String NAME = "x-pack";
|
||||||
|
@ -203,6 +207,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
||||||
protected Graph graph;
|
protected Graph graph;
|
||||||
protected MachineLearning machineLearning;
|
protected MachineLearning machineLearning;
|
||||||
protected Logstash logstash;
|
protected Logstash logstash;
|
||||||
|
protected CryptoService cryptoService;
|
||||||
protected Deprecation deprecation;
|
protected Deprecation deprecation;
|
||||||
protected Upgrade upgrade;
|
protected Upgrade upgrade;
|
||||||
protected SqlPlugin sql;
|
protected SqlPlugin sql;
|
||||||
|
@ -232,6 +237,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
||||||
} else {
|
} else {
|
||||||
this.extensionsService = null;
|
this.extensionsService = null;
|
||||||
}
|
}
|
||||||
|
cryptoService = ENCRYPT_SENSITIVE_DATA_SETTING.get(settings) ? new CryptoService(settings) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For tests only
|
// For tests only
|
||||||
|
@ -286,7 +292,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
||||||
|
|
||||||
// watcher http stuff
|
// watcher http stuff
|
||||||
Map<String, HttpAuthFactory> httpAuthFactories = new HashMap<>();
|
Map<String, HttpAuthFactory> httpAuthFactories = new HashMap<>();
|
||||||
httpAuthFactories.put(BasicAuth.TYPE, new BasicAuthFactory(security.getCryptoService()));
|
httpAuthFactories.put(BasicAuth.TYPE, new BasicAuthFactory(cryptoService));
|
||||||
// TODO: add more auth types, or remove this indirection
|
// TODO: add more auth types, or remove this indirection
|
||||||
HttpAuthRegistry httpAuthRegistry = new HttpAuthRegistry(httpAuthFactories);
|
HttpAuthRegistry httpAuthRegistry = new HttpAuthRegistry(httpAuthFactories);
|
||||||
HttpRequestTemplate.Parser httpTemplateParser = new HttpRequestTemplate.Parser(httpAuthRegistry);
|
HttpRequestTemplate.Parser httpTemplateParser = new HttpRequestTemplate.Parser(httpAuthRegistry);
|
||||||
|
@ -299,7 +305,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
||||||
components.addAll(notificationComponents);
|
components.addAll(notificationComponents);
|
||||||
|
|
||||||
components.addAll(watcher.createComponents(getClock(), scriptService, internalClient, licenseState,
|
components.addAll(watcher.createComponents(getClock(), scriptService, internalClient, licenseState,
|
||||||
httpClient, httpTemplateParser, threadPool, clusterService, security.getCryptoService(), xContentRegistry, components));
|
httpClient, httpTemplateParser, threadPool, clusterService, cryptoService, xContentRegistry, components));
|
||||||
|
|
||||||
|
|
||||||
components.addAll(machineLearning.createComponents(internalClient, clusterService, threadPool, xContentRegistry));
|
components.addAll(machineLearning.createComponents(internalClient, clusterService, threadPool, xContentRegistry));
|
||||||
|
@ -321,7 +327,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
||||||
HttpRequestTemplate.Parser httpTemplateParser, ScriptService scriptService,
|
HttpRequestTemplate.Parser httpTemplateParser, ScriptService scriptService,
|
||||||
HttpAuthRegistry httpAuthRegistry) {
|
HttpAuthRegistry httpAuthRegistry) {
|
||||||
List<Object> components = new ArrayList<>();
|
List<Object> components = new ArrayList<>();
|
||||||
components.add(new EmailService(settings, security.getCryptoService(), clusterSettings));
|
components.add(new EmailService(settings, cryptoService, clusterSettings));
|
||||||
components.add(new HipChatService(settings, httpClient, clusterSettings));
|
components.add(new HipChatService(settings, httpClient, clusterSettings));
|
||||||
components.add(new JiraService(settings, httpClient, clusterSettings));
|
components.add(new JiraService(settings, httpClient, clusterSettings));
|
||||||
components.add(new SlackService(settings, httpClient, clusterSettings));
|
components.add(new SlackService(settings, httpClient, clusterSettings));
|
||||||
|
@ -582,7 +588,10 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BootstrapCheck> getBootstrapChecks() {
|
public List<BootstrapCheck> getBootstrapChecks() {
|
||||||
return security.getBootstrapChecks();
|
return Collections.unmodifiableList(
|
||||||
|
Stream.of(security.getBootstrapChecks(), watcher.getBootstrapChecks())
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.rest.action.RestToXContentListener;
|
import org.elasticsearch.rest.action.RestToXContentListener;
|
||||||
|
@ -28,10 +27,10 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder;
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||||
import static org.elasticsearch.xpack.graph.action.GraphExploreAction.INSTANCE;
|
import static org.elasticsearch.xpack.graph.action.GraphExploreAction.INSTANCE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see GraphExploreRequest
|
* @see GraphExploreRequest
|
||||||
*/
|
*/
|
||||||
|
@ -89,7 +88,6 @@ public class RestGraphAction extends XPackRestHandler {
|
||||||
Hop currentHop = graphRequest.createNextHop(null);
|
Hop currentHop = graphRequest.createNextHop(null);
|
||||||
|
|
||||||
try (XContentParser parser = request.contentOrSourceParamParser()) {
|
try (XContentParser parser = request.contentOrSourceParamParser()) {
|
||||||
QueryParseContext context = new QueryParseContext(parser);
|
|
||||||
|
|
||||||
XContentParser.Token token = parser.nextToken();
|
XContentParser.Token token = parser.nextToken();
|
||||||
|
|
||||||
|
@ -97,15 +95,14 @@ public class RestGraphAction extends XPackRestHandler {
|
||||||
throw new ElasticsearchParseException("failed to parse search source. source must be an object, but found [{}] instead",
|
throw new ElasticsearchParseException("failed to parse search source. source must be an object, but found [{}] instead",
|
||||||
token.name());
|
token.name());
|
||||||
}
|
}
|
||||||
parseHop(parser, context, currentHop, graphRequest);
|
parseHop(parser, currentHop, graphRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphRequest.types(Strings.splitStringByCommaToArray(request.param("type")));
|
graphRequest.types(Strings.splitStringByCommaToArray(request.param("type")));
|
||||||
return channel -> client.es().execute(INSTANCE, graphRequest, new RestToXContentListener<>(channel));
|
return channel -> client.es().execute(INSTANCE, graphRequest, new RestToXContentListener<>(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseHop(XContentParser parser, QueryParseContext context, Hop currentHop,
|
private void parseHop(XContentParser parser, Hop currentHop, GraphExploreRequest graphRequest) throws IOException {
|
||||||
GraphExploreRequest graphRequest) throws IOException {
|
|
||||||
String fieldName = null;
|
String fieldName = null;
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
|
|
||||||
|
@ -121,15 +118,15 @@ public class RestGraphAction extends XPackRestHandler {
|
||||||
}
|
}
|
||||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||||
if (QUERY_FIELD.match(fieldName)) {
|
if (QUERY_FIELD.match(fieldName)) {
|
||||||
currentHop.guidingQuery(context.parseInnerQueryBuilder());
|
currentHop.guidingQuery(parseInnerQueryBuilder(parser));
|
||||||
} else if (CONNECTIONS_FIELD.match(fieldName)) {
|
} else if (CONNECTIONS_FIELD.match(fieldName)) {
|
||||||
parseHop(parser, context, graphRequest.createNextHop(null), graphRequest);
|
parseHop(parser, graphRequest.createNextHop(null), graphRequest);
|
||||||
} else if (CONTROLS_FIELD.match(fieldName)) {
|
} else if (CONTROLS_FIELD.match(fieldName)) {
|
||||||
if (currentHop.getParentHop() != null) {
|
if (currentHop.getParentHop() != null) {
|
||||||
throw new ElasticsearchParseException(
|
throw new ElasticsearchParseException(
|
||||||
"Controls are a global setting that can only be set in the root " + fieldName, token.name());
|
"Controls are a global setting that can only be set in the root " + fieldName, token.name());
|
||||||
}
|
}
|
||||||
parseControls(parser, context, graphRequest);
|
parseControls(parser, graphRequest);
|
||||||
} else {
|
} else {
|
||||||
throw new ElasticsearchParseException("Illegal object property in graph definition " + fieldName, token.name());
|
throw new ElasticsearchParseException("Illegal object property in graph definition " + fieldName, token.name());
|
||||||
|
|
||||||
|
@ -221,7 +218,7 @@ public class RestGraphAction extends XPackRestHandler {
|
||||||
"Graph vertices definition cannot contain both "+ INCLUDE_FIELD.getPreferredName()+
|
"Graph vertices definition cannot contain both "+ INCLUDE_FIELD.getPreferredName()+
|
||||||
" and "+EXCLUDE_FIELD.getPreferredName()+" clauses", token.name());
|
" and "+EXCLUDE_FIELD.getPreferredName()+" clauses", token.name());
|
||||||
}
|
}
|
||||||
excludes = new HashSet<String>();
|
excludes = new HashSet<>();
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||||
excludes.add(parser.text());
|
excludes.add(parser.text());
|
||||||
}
|
}
|
||||||
|
@ -274,7 +271,7 @@ public class RestGraphAction extends XPackRestHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void parseControls(XContentParser parser, QueryParseContext context, GraphExploreRequest graphRequest) throws IOException {
|
private void parseControls(XContentParser parser, GraphExploreRequest graphRequest) throws IOException {
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
|
|
||||||
String fieldName = null;
|
String fieldName = null;
|
||||||
|
|
|
@ -18,9 +18,9 @@ import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.index.query.AbstractQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
|
||||||
import org.elasticsearch.search.aggregations.AggregationBuilder;
|
import org.elasticsearch.search.aggregations.AggregationBuilder;
|
||||||
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
||||||
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
|
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
|
||||||
|
@ -87,14 +87,14 @@ public class DatafeedConfig extends AbstractDiffable<DatafeedConfig> implements
|
||||||
PARSER.declareString((builder, val) ->
|
PARSER.declareString((builder, val) ->
|
||||||
builder.setFrequency(TimeValue.parseTimeValue(val, FREQUENCY.getPreferredName())), FREQUENCY);
|
builder.setFrequency(TimeValue.parseTimeValue(val, FREQUENCY.getPreferredName())), FREQUENCY);
|
||||||
PARSER.declareObject(Builder::setQuery,
|
PARSER.declareObject(Builder::setQuery,
|
||||||
(p, c) -> new QueryParseContext(p).parseInnerQueryBuilder(), QUERY);
|
(p, c) -> AbstractQueryBuilder.parseInnerQueryBuilder(p), QUERY);
|
||||||
PARSER.declareObject(Builder::setAggregations, (p, c) -> AggregatorFactories.parseAggregators(new QueryParseContext(p)),
|
PARSER.declareObject(Builder::setAggregations, (p, c) -> AggregatorFactories.parseAggregators(p),
|
||||||
AGGREGATIONS);
|
AGGREGATIONS);
|
||||||
PARSER.declareObject(Builder::setAggregations,(p, c) -> AggregatorFactories.parseAggregators(new QueryParseContext(p)), AGGS);
|
PARSER.declareObject(Builder::setAggregations,(p, c) -> AggregatorFactories.parseAggregators(p), AGGS);
|
||||||
PARSER.declareObject(Builder::setScriptFields, (p, c) -> {
|
PARSER.declareObject(Builder::setScriptFields, (p, c) -> {
|
||||||
List<SearchSourceBuilder.ScriptField> parsedScriptFields = new ArrayList<>();
|
List<SearchSourceBuilder.ScriptField> parsedScriptFields = new ArrayList<>();
|
||||||
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
|
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
|
||||||
parsedScriptFields.add(new SearchSourceBuilder.ScriptField(new QueryParseContext(p)));
|
parsedScriptFields.add(new SearchSourceBuilder.ScriptField(p));
|
||||||
}
|
}
|
||||||
parsedScriptFields.sort(Comparator.comparing(SearchSourceBuilder.ScriptField::fieldName));
|
parsedScriptFields.sort(Comparator.comparing(SearchSourceBuilder.ScriptField::fieldName));
|
||||||
return parsedScriptFields;
|
return parsedScriptFields;
|
||||||
|
|
|
@ -16,8 +16,8 @@ import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.index.query.AbstractQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
|
||||||
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
@ -49,15 +49,15 @@ public class DatafeedUpdate implements Writeable, ToXContentObject {
|
||||||
PARSER.declareString((builder, val) -> builder.setFrequency(
|
PARSER.declareString((builder, val) -> builder.setFrequency(
|
||||||
TimeValue.parseTimeValue(val, DatafeedConfig.FREQUENCY.getPreferredName())), DatafeedConfig.FREQUENCY);
|
TimeValue.parseTimeValue(val, DatafeedConfig.FREQUENCY.getPreferredName())), DatafeedConfig.FREQUENCY);
|
||||||
PARSER.declareObject(Builder::setQuery,
|
PARSER.declareObject(Builder::setQuery,
|
||||||
(p, c) -> new QueryParseContext(p).parseInnerQueryBuilder(), DatafeedConfig.QUERY);
|
(p, c) -> AbstractQueryBuilder.parseInnerQueryBuilder(p), DatafeedConfig.QUERY);
|
||||||
PARSER.declareObject(Builder::setAggregations, (p, c) -> AggregatorFactories.parseAggregators(new QueryParseContext(p)),
|
PARSER.declareObject(Builder::setAggregations, (p, c) -> AggregatorFactories.parseAggregators(p),
|
||||||
DatafeedConfig.AGGREGATIONS);
|
DatafeedConfig.AGGREGATIONS);
|
||||||
PARSER.declareObject(Builder::setAggregations,(p, c) -> AggregatorFactories.parseAggregators(new QueryParseContext(p)),
|
PARSER.declareObject(Builder::setAggregations,(p, c) -> AggregatorFactories.parseAggregators(p),
|
||||||
DatafeedConfig.AGGS);
|
DatafeedConfig.AGGS);
|
||||||
PARSER.declareObject(Builder::setScriptFields, (p, c) -> {
|
PARSER.declareObject(Builder::setScriptFields, (p, c) -> {
|
||||||
List<SearchSourceBuilder.ScriptField> parsedScriptFields = new ArrayList<>();
|
List<SearchSourceBuilder.ScriptField> parsedScriptFields = new ArrayList<>();
|
||||||
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
|
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
|
||||||
parsedScriptFields.add(new SearchSourceBuilder.ScriptField(new QueryParseContext(p)));
|
parsedScriptFields.add(new SearchSourceBuilder.ScriptField(p));
|
||||||
}
|
}
|
||||||
parsedScriptFields.sort(Comparator.comparing(SearchSourceBuilder.ScriptField::fieldName));
|
parsedScriptFields.sort(Comparator.comparing(SearchSourceBuilder.ScriptField::fieldName));
|
||||||
return parsedScriptFields;
|
return parsedScriptFields;
|
||||||
|
|
|
@ -31,6 +31,16 @@ import java.util.Collections;
|
||||||
*/
|
*/
|
||||||
public class NodeStatsCollector extends Collector {
|
public class NodeStatsCollector extends Collector {
|
||||||
|
|
||||||
|
private static final CommonStatsFlags FLAGS =
|
||||||
|
new CommonStatsFlags(CommonStatsFlags.Flag.Docs,
|
||||||
|
CommonStatsFlags.Flag.FieldData,
|
||||||
|
CommonStatsFlags.Flag.Store,
|
||||||
|
CommonStatsFlags.Flag.Indexing,
|
||||||
|
CommonStatsFlags.Flag.QueryCache,
|
||||||
|
CommonStatsFlags.Flag.RequestCache,
|
||||||
|
CommonStatsFlags.Flag.Search,
|
||||||
|
CommonStatsFlags.Flag.Segments);
|
||||||
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
|
|
||||||
public NodeStatsCollector(Settings settings, ClusterService clusterService,
|
public NodeStatsCollector(Settings settings, ClusterService clusterService,
|
||||||
|
@ -43,7 +53,7 @@ public class NodeStatsCollector extends Collector {
|
||||||
@Override
|
@Override
|
||||||
protected Collection<MonitoringDoc> doCollect() throws Exception {
|
protected Collection<MonitoringDoc> doCollect() throws Exception {
|
||||||
NodesStatsRequest request = new NodesStatsRequest("_local");
|
NodesStatsRequest request = new NodesStatsRequest("_local");
|
||||||
request.indices(CommonStatsFlags.ALL);
|
request.indices(FLAGS);
|
||||||
request.os(true);
|
request.os(true);
|
||||||
request.jvm(true);
|
request.jvm(true);
|
||||||
request.process(true);
|
request.process(true);
|
||||||
|
@ -67,4 +77,5 @@ public class NodeStatsCollector extends Collector {
|
||||||
|
|
||||||
return Collections.singletonList(nodeStatsDoc);
|
return Collections.singletonList(nodeStatsDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,6 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
private final boolean transportClientMode;
|
private final boolean transportClientMode;
|
||||||
private final XPackLicenseState licenseState;
|
private final XPackLicenseState licenseState;
|
||||||
private final CryptoService cryptoService;
|
|
||||||
private final SSLService sslService;
|
private final SSLService sslService;
|
||||||
/* what a PITA that we need an extra indirection to initialize this. Yet, once we got rid of guice we can thing about how
|
/* what a PITA that we need an extra indirection to initialize this. Yet, once we got rid of guice we can thing about how
|
||||||
* to fix this or make it simpler. Today we need several service that are created in createComponents but we need to register
|
* to fix this or make it simpler. Today we need several service that are created in createComponents but we need to register
|
||||||
|
@ -220,18 +219,11 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
this.enabled = XPackSettings.SECURITY_ENABLED.get(settings);
|
this.enabled = XPackSettings.SECURITY_ENABLED.get(settings);
|
||||||
if (enabled && transportClientMode == false) {
|
if (enabled && transportClientMode == false) {
|
||||||
validateAutoCreateIndex(settings);
|
validateAutoCreateIndex(settings);
|
||||||
cryptoService = new CryptoService(settings, env);
|
|
||||||
} else {
|
|
||||||
cryptoService = null;
|
|
||||||
}
|
}
|
||||||
this.licenseState = licenseState;
|
this.licenseState = licenseState;
|
||||||
this.sslService = sslService;
|
this.sslService = sslService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptoService getCryptoService() {
|
|
||||||
return cryptoService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Module> nodeModules() {
|
public Collection<Module> nodeModules() {
|
||||||
List<Module> modules = new ArrayList<>();
|
List<Module> modules = new ArrayList<>();
|
||||||
if (enabled == false || transportClientMode) {
|
if (enabled == false || transportClientMode) {
|
||||||
|
@ -254,7 +246,6 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
|
|
||||||
if (enabled == false) {
|
if (enabled == false) {
|
||||||
modules.add(b -> {
|
modules.add(b -> {
|
||||||
b.bind(CryptoService.class).toProvider(Providers.of(null));
|
|
||||||
b.bind(Realms.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
|
b.bind(Realms.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
|
||||||
b.bind(CompositeRolesStore.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
|
b.bind(CompositeRolesStore.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
|
||||||
b.bind(NativeRoleMappingStore.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
|
b.bind(NativeRoleMappingStore.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
|
||||||
|
@ -268,7 +259,6 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
// which might not be the case during Plugin class instantiation. Once nodeModules are pulled
|
// which might not be the case during Plugin class instantiation. Once nodeModules are pulled
|
||||||
// everything should have been loaded
|
// everything should have been loaded
|
||||||
modules.add(b -> {
|
modules.add(b -> {
|
||||||
b.bind(CryptoService.class).toInstance(cryptoService);
|
|
||||||
if (XPackSettings.AUDIT_ENABLED.get(settings)) {
|
if (XPackSettings.AUDIT_ENABLED.get(settings)) {
|
||||||
b.bind(AuditTrail.class).to(AuditTrailService.class); // interface used by some actions...
|
b.bind(AuditTrail.class).to(AuditTrailService.class); // interface used by some actions...
|
||||||
}
|
}
|
||||||
|
@ -474,9 +464,6 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
settingsList.add(TokenService.DELETE_INTERVAL);
|
settingsList.add(TokenService.DELETE_INTERVAL);
|
||||||
settingsList.add(TokenService.DELETE_TIMEOUT);
|
settingsList.add(TokenService.DELETE_TIMEOUT);
|
||||||
|
|
||||||
// encryption settings
|
|
||||||
CryptoService.addSettings(settingsList);
|
|
||||||
|
|
||||||
// hide settings
|
// hide settings
|
||||||
settingsList.add(Setting.listSetting(setting("hide_settings"), Collections.emptyList(), Function.identity(),
|
settingsList.add(Setting.listSetting(setting("hide_settings"), Collections.emptyList(), Function.identity(),
|
||||||
Property.NodeScope, Property.Filtered));
|
Property.NodeScope, Property.Filtered));
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.SecureString;
|
||||||
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;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.node.Node;
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
|
@ -29,6 +30,7 @@ import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
@ -132,8 +134,8 @@ public class AuthenticationService extends AbstractComponent {
|
||||||
|
|
||||||
private final AuditableRequest request;
|
private final AuditableRequest request;
|
||||||
private final User fallbackUser;
|
private final User fallbackUser;
|
||||||
private final ActionListener<Authentication> listener;
|
|
||||||
|
|
||||||
|
private final ActionListener<Authentication> listener;
|
||||||
private RealmRef authenticatedBy = null;
|
private RealmRef authenticatedBy = null;
|
||||||
private RealmRef lookedupBy = null;
|
private RealmRef lookedupBy = null;
|
||||||
private AuthenticationToken authenticationToken = null;
|
private AuthenticationToken authenticationToken = null;
|
||||||
|
@ -279,7 +281,7 @@ public class AuthenticationService extends AbstractComponent {
|
||||||
authenticationToken.principal(), realm.name(), ex);
|
authenticationToken.principal(), realm.name(), ex);
|
||||||
logger.debug("Authentication failed due to exception", ex);
|
logger.debug("Authentication failed due to exception", ex);
|
||||||
userListener.onFailure(ex);
|
userListener.onFailure(ex);
|
||||||
}));
|
}), request);
|
||||||
} else {
|
} else {
|
||||||
userListener.onResponse(null);
|
userListener.onResponse(null);
|
||||||
}
|
}
|
||||||
|
@ -436,16 +438,21 @@ public class AuthenticationService extends AbstractComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract static class AuditableRequest {
|
abstract static class AuditableRequest implements IncomingRequest {
|
||||||
|
|
||||||
final AuditTrail auditTrail;
|
final AuditTrail auditTrail;
|
||||||
final AuthenticationFailureHandler failureHandler;
|
final AuthenticationFailureHandler failureHandler;
|
||||||
final ThreadContext threadContext;
|
final ThreadContext threadContext;
|
||||||
|
private final InetSocketAddress remoteAddress;
|
||||||
|
private final RequestType requestType;
|
||||||
|
|
||||||
AuditableRequest(AuditTrail auditTrail, AuthenticationFailureHandler failureHandler, ThreadContext threadContext) {
|
AuditableRequest(AuditTrail auditTrail, AuthenticationFailureHandler failureHandler, ThreadContext threadContext,
|
||||||
|
RequestType requestType, InetSocketAddress remoteAddress) {
|
||||||
this.auditTrail = auditTrail;
|
this.auditTrail = auditTrail;
|
||||||
this.failureHandler = failureHandler;
|
this.failureHandler = failureHandler;
|
||||||
this.threadContext = threadContext;
|
this.threadContext = threadContext;
|
||||||
|
this.remoteAddress = remoteAddress;
|
||||||
|
this.requestType = requestType;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void realmAuthenticationFailed(AuthenticationToken token, String realm);
|
abstract void realmAuthenticationFailed(AuthenticationToken token, String realm);
|
||||||
|
@ -461,6 +468,14 @@ public class AuthenticationService extends AbstractComponent {
|
||||||
abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token);
|
abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token);
|
||||||
|
|
||||||
abstract void authenticationSuccess(String realm, User user);
|
abstract void authenticationSuccess(String realm, User user);
|
||||||
|
|
||||||
|
public InetSocketAddress getRemoteAddress() {
|
||||||
|
return remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestType getType() {
|
||||||
|
return requestType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AuditableTransportRequest extends AuditableRequest {
|
static class AuditableTransportRequest extends AuditableRequest {
|
||||||
|
@ -470,7 +485,7 @@ public class AuthenticationService extends AbstractComponent {
|
||||||
|
|
||||||
AuditableTransportRequest(AuditTrail auditTrail, AuthenticationFailureHandler failureHandler, ThreadContext threadContext,
|
AuditableTransportRequest(AuditTrail auditTrail, AuthenticationFailureHandler failureHandler, ThreadContext threadContext,
|
||||||
String action, TransportMessage message) {
|
String action, TransportMessage message) {
|
||||||
super(auditTrail, failureHandler, threadContext);
|
super(auditTrail, failureHandler, threadContext, getType(message), getRemoteAddress(message));
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
@ -523,15 +538,25 @@ public class AuthenticationService extends AbstractComponent {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "transport request action [" + action + "]";
|
return "transport request action [" + action + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static RequestType getType(TransportMessage message) {
|
||||||
|
return message.remoteAddress() == null ? RequestType.LOCAL_NODE : RequestType.REMOTE_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InetSocketAddress getRemoteAddress(TransportMessage message) {
|
||||||
|
TransportAddress transportAddress = message.remoteAddress();
|
||||||
|
return transportAddress == null ? null : transportAddress.address();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AuditableRestRequest extends AuditableRequest {
|
static class AuditableRestRequest extends AuditableRequest {
|
||||||
|
|
||||||
private final RestRequest request;
|
private final RestRequest request;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
AuditableRestRequest(AuditTrail auditTrail, AuthenticationFailureHandler failureHandler, ThreadContext threadContext,
|
AuditableRestRequest(AuditTrail auditTrail, AuthenticationFailureHandler failureHandler, ThreadContext threadContext,
|
||||||
RestRequest request) {
|
RestRequest request) {
|
||||||
super(auditTrail, failureHandler, threadContext);
|
super(auditTrail, failureHandler, threadContext, RequestType.REST, (InetSocketAddress) request.getRemoteAddress());
|
||||||
this.request = request;
|
this.request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.security.authc;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This represents an incoming request that needs to be authenticated
|
||||||
|
*/
|
||||||
|
public interface IncomingRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the remote address for the request. It will be null if the request is a
|
||||||
|
* local transport request.
|
||||||
|
*
|
||||||
|
* @return the remote socket address
|
||||||
|
*/
|
||||||
|
InetSocketAddress getRemoteAddress();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the type of request that is incoming. It can be a rest request, a remote
|
||||||
|
* transport request, or a local transport request.
|
||||||
|
*
|
||||||
|
* @return the request type
|
||||||
|
*/
|
||||||
|
RequestType getType();
|
||||||
|
|
||||||
|
enum RequestType {
|
||||||
|
REST,
|
||||||
|
REMOTE_NODE,
|
||||||
|
LOCAL_NODE
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,10 +81,12 @@ public abstract class Realm implements Comparable<Realm> {
|
||||||
* {@link ActionListener#onResponse} with the User associated with the given token. An unsuccessful authentication calls
|
* {@link ActionListener#onResponse} with the User associated with the given token. An unsuccessful authentication calls
|
||||||
* with {@code null} on the argument.
|
* with {@code null} on the argument.
|
||||||
*
|
*
|
||||||
|
* The remote address should be null if the request initiated from the local node.
|
||||||
* @param token The authentication token
|
* @param token The authentication token
|
||||||
* @param listener The listener to pass the authentication result to
|
* @param listener The listener to pass the authentication result to
|
||||||
|
* @param incomingRequest the request that is being authenticated
|
||||||
*/
|
*/
|
||||||
public abstract void authenticate(AuthenticationToken token, ActionListener<User> listener);
|
public abstract void authenticate(AuthenticationToken token, ActionListener<User> listener, IncomingRequest incomingRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the user identified the String identifier. A successful lookup will call the {@link ActionListener#onResponse}
|
* Looks up the user identified the String identifier. A successful lookup will call the {@link ActionListener#onResponse}
|
||||||
|
|
|
@ -7,12 +7,12 @@ package org.elasticsearch.xpack.security.authc.esnative;
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +35,7 @@ public class NativeRealm extends CachingUsernamePasswordRealm {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener) {
|
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
userStore.verifyPassword(token.principal(), token.credentials(), listener);
|
userStore.verifyPassword(token.principal(), token.credentials(), listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,7 @@ public class NativeRealmMigrator implements IndexLifecycleManager.IndexDataMigra
|
||||||
}
|
}
|
||||||
|
|
||||||
try (SecureString secureString = new SecureString(passwordHash.toCharArray())) {
|
try (SecureString secureString = new SecureString(passwordHash.toCharArray())) {
|
||||||
return Hasher.BCRYPT.verify(ReservedRealm.DEFAULT_PASSWORD_TEXT, secureString.getChars());
|
return Hasher.BCRYPT.verify(ReservedRealm.EMPTY_PASSWORD_TEXT, secureString.getChars());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -548,7 +548,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
} else if (password.isEmpty() && containerSettings.inContainer() && username.equals(ElasticUser.NAME)) {
|
} else if (password.isEmpty() && containerSettings.inContainer() && username.equals(ElasticUser.NAME)) {
|
||||||
listener.onResponse(new ReservedUserInfo(containerSettings.getPasswordHash(), enabled, false));
|
listener.onResponse(new ReservedUserInfo(containerSettings.getPasswordHash(), enabled, false));
|
||||||
} else if (password.isEmpty()) {
|
} else if (password.isEmpty()) {
|
||||||
listener.onResponse(new ReservedUserInfo(ReservedRealm.DEFAULT_PASSWORD_HASH, enabled, true));
|
listener.onResponse(new ReservedUserInfo(ReservedRealm.EMPTY_PASSWORD_HASH, enabled, true));
|
||||||
} else {
|
} else {
|
||||||
listener.onResponse(new ReservedUserInfo(password.toCharArray(), enabled, false));
|
listener.onResponse(new ReservedUserInfo(password.toCharArray(), enabled, false));
|
||||||
}
|
}
|
||||||
|
@ -607,7 +607,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
char[] passwordHash = containerSettings.getPasswordHash();
|
char[] passwordHash = containerSettings.getPasswordHash();
|
||||||
userInfos.put(searchHit.getId(), new ReservedUserInfo(passwordHash, enabled, false));
|
userInfos.put(searchHit.getId(), new ReservedUserInfo(passwordHash, enabled, false));
|
||||||
} else if (password.isEmpty()) {
|
} else if (password.isEmpty()) {
|
||||||
userInfos.put(username, new ReservedUserInfo(ReservedRealm.DEFAULT_PASSWORD_HASH, enabled, true));
|
userInfos.put(username, new ReservedUserInfo(ReservedRealm.EMPTY_PASSWORD_HASH, enabled, true));
|
||||||
} else {
|
} else {
|
||||||
userInfos.put(username, new ReservedUserInfo(password.toCharArray(), enabled, false));
|
userInfos.put(username, new ReservedUserInfo(password.toCharArray(), enabled, false));
|
||||||
}
|
}
|
||||||
|
@ -694,12 +694,12 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
|
|
||||||
public final char[] passwordHash;
|
public final char[] passwordHash;
|
||||||
public final boolean enabled;
|
public final boolean enabled;
|
||||||
public final boolean hasDefaultPassword;
|
public final boolean hasEmptyPassword;
|
||||||
|
|
||||||
ReservedUserInfo(char[] passwordHash, boolean enabled, boolean hasDefaultPassword) {
|
ReservedUserInfo(char[] passwordHash, boolean enabled, boolean hasEmptyPassword) {
|
||||||
this.passwordHash = passwordHash;
|
this.passwordHash = passwordHash;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
this.hasDefaultPassword = hasDefaultPassword;
|
this.hasEmptyPassword = hasEmptyPassword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.Security;
|
import org.elasticsearch.xpack.security.Security;
|
||||||
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.ReservedUserInfo;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.ReservedUserInfo;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||||
|
@ -30,6 +31,9 @@ import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
|
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -44,20 +48,20 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
|
|
||||||
public static final String TYPE = "reserved";
|
public static final String TYPE = "reserved";
|
||||||
|
|
||||||
public static final SecureString DEFAULT_PASSWORD_TEXT = new SecureString("changeme".toCharArray());
|
public static final SecureString EMPTY_PASSWORD_TEXT = new SecureString("".toCharArray());
|
||||||
static final char[] DEFAULT_PASSWORD_HASH = Hasher.BCRYPT.hash(DEFAULT_PASSWORD_TEXT);
|
static final char[] EMPTY_PASSWORD_HASH = Hasher.BCRYPT.hash(EMPTY_PASSWORD_TEXT);
|
||||||
|
|
||||||
private static final ReservedUserInfo DEFAULT_USER_INFO = new ReservedUserInfo(DEFAULT_PASSWORD_HASH, true, true);
|
private static final ReservedUserInfo DEFAULT_USER_INFO = new ReservedUserInfo(EMPTY_PASSWORD_HASH, true, true);
|
||||||
private static final ReservedUserInfo DISABLED_USER_INFO = new ReservedUserInfo(DEFAULT_PASSWORD_HASH, false, true);
|
private static final ReservedUserInfo DISABLED_USER_INFO = new ReservedUserInfo(EMPTY_PASSWORD_HASH, false, true);
|
||||||
|
|
||||||
public static final Setting<Boolean> ACCEPT_DEFAULT_PASSWORD_SETTING = Setting.boolSetting(
|
public static final Setting<Boolean> ACCEPT_DEFAULT_PASSWORD_SETTING = Setting.boolSetting(
|
||||||
Security.setting("authc.accept_default_password"), true, Setting.Property.NodeScope, Setting.Property.Filtered);
|
Security.setting("authc.accept_default_password"), true, Setting.Property.NodeScope, Setting.Property.Filtered,
|
||||||
|
Setting.Property.Deprecated);
|
||||||
|
|
||||||
private final NativeUsersStore nativeUsersStore;
|
private final NativeUsersStore nativeUsersStore;
|
||||||
private final AnonymousUser anonymousUser;
|
private final AnonymousUser anonymousUser;
|
||||||
private final boolean realmEnabled;
|
private final boolean realmEnabled;
|
||||||
private final boolean anonymousEnabled;
|
private final boolean anonymousEnabled;
|
||||||
private final boolean defaultPasswordEnabled;
|
|
||||||
private final SecurityLifecycleService securityLifecycleService;
|
private final SecurityLifecycleService securityLifecycleService;
|
||||||
|
|
||||||
public ReservedRealm(Environment env, Settings settings, NativeUsersStore nativeUsersStore, AnonymousUser anonymousUser,
|
public ReservedRealm(Environment env, Settings settings, NativeUsersStore nativeUsersStore, AnonymousUser anonymousUser,
|
||||||
|
@ -67,12 +71,28 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
this.realmEnabled = XPackSettings.RESERVED_REALM_ENABLED_SETTING.get(settings);
|
this.realmEnabled = XPackSettings.RESERVED_REALM_ENABLED_SETTING.get(settings);
|
||||||
this.anonymousUser = anonymousUser;
|
this.anonymousUser = anonymousUser;
|
||||||
this.anonymousEnabled = AnonymousUser.isAnonymousEnabled(settings);
|
this.anonymousEnabled = AnonymousUser.isAnonymousEnabled(settings);
|
||||||
this.defaultPasswordEnabled = ACCEPT_DEFAULT_PASSWORD_SETTING.get(settings);
|
|
||||||
this.securityLifecycleService = securityLifecycleService;
|
this.securityLifecycleService = securityLifecycleService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener) {
|
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
|
if (incomingRequest.getType() != IncomingRequest.RequestType.REST) {
|
||||||
|
doAuthenticate(token, listener, false);
|
||||||
|
} else {
|
||||||
|
InetAddress address = incomingRequest.getRemoteAddress().getAddress();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// This checks if the address is the loopback address or if it is bound to one of this machine's
|
||||||
|
// network interfaces. This is because we want to allow requests that originate from this machine.
|
||||||
|
final boolean isLocalMachine = address.isLoopbackAddress() || NetworkInterface.getByInetAddress(address) != null;
|
||||||
|
doAuthenticate(token, listener, isLocalMachine);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
listener.onFailure(Exceptions.authenticationError("failed to authenticate user [{}]", e, token.principal()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener, boolean acceptEmptyPassword) {
|
||||||
if (realmEnabled == false) {
|
if (realmEnabled == false) {
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
} else if (isReserved(token.principal(), config.globalSettings()) == false) {
|
} else if (isReserved(token.principal(), config.globalSettings()) == false) {
|
||||||
|
@ -82,7 +102,10 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
Runnable action;
|
Runnable action;
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
try {
|
try {
|
||||||
if (verifyPassword(userInfo, token)) {
|
if (userInfo.hasEmptyPassword && isSetupMode(token.principal(), acceptEmptyPassword) == false) {
|
||||||
|
action = () -> listener.onFailure(Exceptions.authenticationError("failed to authenticate user [{}]",
|
||||||
|
token.principal()));
|
||||||
|
} else if (verifyPassword(userInfo, token)) {
|
||||||
final User user = getUser(token.principal(), userInfo);
|
final User user = getUser(token.principal(), userInfo);
|
||||||
action = () -> listener.onResponse(user);
|
action = () -> listener.onResponse(user);
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,7 +113,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
token.principal()));
|
token.principal()));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (userInfo.passwordHash != DEFAULT_PASSWORD_HASH) {
|
if (userInfo.passwordHash != EMPTY_PASSWORD_HASH) {
|
||||||
Arrays.fill(userInfo.passwordHash, (char) 0);
|
Arrays.fill(userInfo.passwordHash, (char) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,11 +127,12 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSetupMode(String userName, boolean acceptEmptyPassword) {
|
||||||
|
return ElasticUser.NAME.equals(userName) && acceptEmptyPassword;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean verifyPassword(ReservedUserInfo userInfo, UsernamePasswordToken token) {
|
private boolean verifyPassword(ReservedUserInfo userInfo, UsernamePasswordToken token) {
|
||||||
if (Hasher.BCRYPT.verify(token.credentials(), userInfo.passwordHash)) {
|
if (Hasher.BCRYPT.verify(token.credentials(), userInfo.passwordHash)) {
|
||||||
if (userInfo.hasDefaultPassword && this.defaultPasswordEnabled == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -154,7 +178,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
assert username != null;
|
assert username != null;
|
||||||
switch (username) {
|
switch (username) {
|
||||||
case ElasticUser.NAME:
|
case ElasticUser.NAME:
|
||||||
return new ElasticUser(userInfo.enabled);
|
return new ElasticUser(userInfo.enabled, userInfo.hasEmptyPassword);
|
||||||
case KibanaUser.NAME:
|
case KibanaUser.NAME:
|
||||||
return new KibanaUser(userInfo.enabled);
|
return new KibanaUser(userInfo.enabled);
|
||||||
case LogstashSystemUser.NAME:
|
case LogstashSystemUser.NAME:
|
||||||
|
@ -178,7 +202,8 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
|
||||||
List<User> users = new ArrayList<>(4);
|
List<User> users = new ArrayList<>(4);
|
||||||
|
|
||||||
ReservedUserInfo userInfo = reservedUserInfos.get(ElasticUser.NAME);
|
ReservedUserInfo userInfo = reservedUserInfos.get(ElasticUser.NAME);
|
||||||
users.add(new ElasticUser(userInfo == null || userInfo.enabled));
|
users.add(new ElasticUser(userInfo == null || userInfo.enabled,
|
||||||
|
userInfo == null || userInfo.hasEmptyPassword));
|
||||||
|
|
||||||
userInfo = reservedUserInfos.get(KibanaUser.NAME);
|
userInfo = reservedUserInfos.get(KibanaUser.NAME);
|
||||||
users.add(new KibanaUser(userInfo == null || userInfo.enabled));
|
users.add(new KibanaUser(userInfo == null || userInfo.enabled));
|
||||||
|
|
|
@ -157,7 +157,7 @@ public class SetupPasswordTool extends MultiCommand {
|
||||||
private OptionSpec<String> noPromptOption;
|
private OptionSpec<String> noPromptOption;
|
||||||
|
|
||||||
private String elasticUser = ElasticUser.NAME;
|
private String elasticUser = ElasticUser.NAME;
|
||||||
private SecureString elasticUserPassword = ReservedRealm.DEFAULT_PASSWORD_TEXT;
|
private SecureString elasticUserPassword = ReservedRealm.EMPTY_PASSWORD_TEXT;
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
SetupCommand(String description) {
|
SetupCommand(String description) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Set;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
|
@ -37,7 +38,7 @@ public class FileRealm extends CachingUsernamePasswordRealm {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener) {
|
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
if (userPasswdStore.verifyPassword(token.principal(), token.credentials())) {
|
if (userPasswdStore.verifyPassword(token.principal(), token.credentials())) {
|
||||||
String[] roles = userRolesStore.roles(token.principal());
|
String[] roles = userRolesStore.roles(token.principal());
|
||||||
listener.onResponse(new User(token.principal(), roles));
|
listener.onResponse(new User(token.principal(), roles));
|
||||||
|
|
|
@ -19,7 +19,6 @@ import org.apache.lucene.util.IOUtils;
|
||||||
import org.elasticsearch.ElasticsearchTimeoutException;
|
import org.elasticsearch.ElasticsearchTimeoutException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.ContextPreservingActionListener;
|
import org.elasticsearch.action.support.ContextPreservingActionListener;
|
||||||
import org.elasticsearch.common.Strings;
|
|
||||||
import org.elasticsearch.common.collect.MapBuilder;
|
import org.elasticsearch.common.collect.MapBuilder;
|
||||||
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;
|
||||||
|
@ -29,6 +28,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.threadpool.ThreadPool.Names;
|
import org.elasticsearch.threadpool.ThreadPool.Names;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmSettings;
|
import org.elasticsearch.xpack.security.authc.RealmSettings;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapLoadBalancing;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapLoadBalancing;
|
||||||
|
@ -142,7 +142,7 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
|
||||||
* This user will then be passed to the listener
|
* This user will then be passed to the listener
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener) {
|
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
// we submit to the threadpool because authentication using LDAP will execute blocking I/O for a bind request and we don't want
|
// we submit to the threadpool because authentication using LDAP will execute blocking I/O for a bind request and we don't want
|
||||||
// network threads stuck waiting for a socket to connect. After the bind, then all interaction with LDAP should be async
|
// network threads stuck waiting for a socket to connect. After the bind, then all interaction with LDAP should be async
|
||||||
final CancellableLdapRunnable cancellableLdapRunnable = new CancellableLdapRunnable(listener,
|
final CancellableLdapRunnable cancellableLdapRunnable = new CancellableLdapRunnable(listener,
|
||||||
|
|
|
@ -17,6 +17,7 @@ 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.env.Environment;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
|
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
|
||||||
import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper;
|
import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper;
|
||||||
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
|
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
|
||||||
|
@ -40,9 +41,6 @@ import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
|
|
||||||
import static org.elasticsearch.xpack.security.Security.setting;
|
|
||||||
|
|
||||||
public class PkiRealm extends Realm {
|
public class PkiRealm extends Realm {
|
||||||
|
|
||||||
public static final String PKI_CERT_HEADER_NAME = "__SECURITY_CLIENT_CERTIFICATE";
|
public static final String PKI_CERT_HEADER_NAME = "__SECURITY_CLIENT_CERTIFICATE";
|
||||||
|
@ -84,7 +82,7 @@ public class PkiRealm extends Realm {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticationToken authToken, ActionListener<User> listener) {
|
public void authenticate(AuthenticationToken authToken, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
X509AuthenticationToken token = (X509AuthenticationToken)authToken;
|
X509AuthenticationToken token = (X509AuthenticationToken)authToken;
|
||||||
if (isCertificateChainTrusted(trustManager, token, logger) == false) {
|
if (isCertificateChainTrusted(trustManager, token, logger) == false) {
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
|
@ -67,17 +68,18 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
* If the user exists in the cache (keyed by the principle name), then the password is validated
|
* If the user exists in the cache (keyed by the principle name), then the password is validated
|
||||||
* against a hash also stored in the cache. Otherwise the subclass authenticates the user via
|
* against a hash also stored in the cache. Otherwise the subclass authenticates the user via
|
||||||
* doAuthenticate
|
* doAuthenticate
|
||||||
*
|
|
||||||
* @param authToken The authentication token
|
* @param authToken The authentication token
|
||||||
|
* @param listener to be called at completion
|
||||||
|
* @param incomingRequest the request that is being authenticated
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void authenticate(AuthenticationToken authToken, ActionListener<User> listener) {
|
public final void authenticate(AuthenticationToken authToken, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
UsernamePasswordToken token = (UsernamePasswordToken)authToken;
|
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
|
||||||
try {
|
try {
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
doAuthenticate(token, listener);
|
doAuthenticate(token, listener, incomingRequest);
|
||||||
} else {
|
} else {
|
||||||
authenticateWithCache(token, listener);
|
authenticateWithCache(token, listener, incomingRequest);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// each realm should handle exceptions, if we get one here it should be considered fatal
|
// each realm should handle exceptions, if we get one here it should be considered fatal
|
||||||
|
@ -85,7 +87,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticateWithCache(UsernamePasswordToken token, ActionListener<User> listener) {
|
private void authenticateWithCache(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
UserWithHash userWithHash = cache.get(token.principal());
|
UserWithHash userWithHash = cache.get(token.principal());
|
||||||
if (userWithHash == null) {
|
if (userWithHash == null) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
@ -97,7 +99,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
logger.debug("realm [{}] authenticated user [{}], with roles [{}]", name(), token.principal(), user.roles());
|
logger.debug("realm [{}] authenticated user [{}], with roles [{}]", name(), token.principal(), user.roles());
|
||||||
}
|
}
|
||||||
listener.onResponse(user);
|
listener.onResponse(user);
|
||||||
}, listener::onFailure));
|
}, listener::onFailure), incomingRequest);
|
||||||
} else if (userWithHash.hasHash()) {
|
} else if (userWithHash.hasHash()) {
|
||||||
if (userWithHash.verify(token.credentials())) {
|
if (userWithHash.verify(token.credentials())) {
|
||||||
if (userWithHash.user.enabled()) {
|
if (userWithHash.user.enabled()) {
|
||||||
|
@ -114,7 +116,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
user.enabled(), user.roles());
|
user.enabled(), user.roles());
|
||||||
}
|
}
|
||||||
listener.onResponse(user);
|
listener.onResponse(user);
|
||||||
}, listener::onFailure));
|
}, listener::onFailure), incomingRequest);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cache.invalidate(token.principal());
|
cache.invalidate(token.principal());
|
||||||
|
@ -124,7 +126,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
name(), token.principal(), user.roles());
|
name(), token.principal(), user.roles());
|
||||||
}
|
}
|
||||||
listener.onResponse(user);
|
listener.onResponse(user);
|
||||||
}, listener::onFailure));
|
}, listener::onFailure), incomingRequest);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cache.invalidate(token.principal());
|
cache.invalidate(token.principal());
|
||||||
|
@ -134,12 +136,12 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
"realm [{}] authenticated user [{}] with roles [{}]", name(), token.principal(), user.roles());
|
"realm [{}] authenticated user [{}] with roles [{}]", name(), token.principal(), user.roles());
|
||||||
}
|
}
|
||||||
listener.onResponse(user);
|
listener.onResponse(user);
|
||||||
}, listener::onFailure));
|
}, listener::onFailure), incomingRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doAuthenticateAndCache(UsernamePasswordToken token, ActionListener<User> listener) {
|
private void doAuthenticateAndCache(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
doAuthenticate(token, ActionListener.wrap((user) -> {
|
ActionListener<User> wrapped = ActionListener.wrap((user) -> {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -148,7 +150,9 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
cache.put(token.principal(), userWithHash);
|
cache.put(token.principal(), userWithHash);
|
||||||
listener.onResponse(user);
|
listener.onResponse(user);
|
||||||
}
|
}
|
||||||
}, listener::onFailure));
|
}, listener::onFailure);
|
||||||
|
|
||||||
|
doAuthenticate(token, wrapped, incomingRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -158,7 +162,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener);
|
protected abstract void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener, IncomingRequest incomingRequest);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void lookupUser(String username, ActionListener<User> listener) {
|
public final void lookupUser(String username, ActionListener<User> listener) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.CompositeIndicesRequest;
|
import org.elasticsearch.action.CompositeIndicesRequest;
|
||||||
import org.elasticsearch.action.IndicesRequest;
|
import org.elasticsearch.action.IndicesRequest;
|
||||||
|
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
|
||||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
|
@ -54,6 +55,7 @@ import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
|
||||||
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
||||||
import org.elasticsearch.xpack.security.support.Automatons;
|
import org.elasticsearch.xpack.security.support.Automatons;
|
||||||
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.ElasticUser;
|
||||||
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.elasticsearch.xpack.security.user.XPackUser;
|
import org.elasticsearch.xpack.security.user.XPackUser;
|
||||||
|
@ -150,6 +152,12 @@ public class AuthorizationService extends AbstractComponent {
|
||||||
throw denial(authentication, action, request);
|
throw denial(authentication, action, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user is the elastic user in setup mode, then only change password requests can be authorized
|
||||||
|
if (ElasticUser.isElasticUserInSetupMode(authentication.getUser())
|
||||||
|
&& ChangePasswordAction.NAME.equals(action) == false) {
|
||||||
|
throw denial(authentication, action, request);
|
||||||
|
}
|
||||||
|
|
||||||
// get the roles of the authenticated user, which may be different than the effective
|
// get the roles of the authenticated user, which may be different than the effective
|
||||||
Role permission = userRole;
|
Role permission = userRole;
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
|
||||||
String templateResult = evaluateTemplate(bytesReference.utf8ToString());
|
String templateResult = evaluateTemplate(bytesReference.utf8ToString());
|
||||||
try (XContentParser parser = XContentFactory.xContent(templateResult)
|
try (XContentParser parser = XContentFactory.xContent(templateResult)
|
||||||
.createParser(queryShardContext.getXContentRegistry(), templateResult)) {
|
.createParser(queryShardContext.getXContentRegistry(), templateResult)) {
|
||||||
QueryBuilder queryBuilder = queryShardContext.newParseContext(parser).parseInnerQueryBuilder();
|
QueryBuilder queryBuilder = queryShardContext.parseInnerQueryBuilder(parser);
|
||||||
verifyRoleQuery(queryBuilder);
|
verifyRoleQuery(queryBuilder);
|
||||||
failIfQueryUsesClient(scriptService, queryBuilder, queryShardContext);
|
failIfQueryUsesClient(scriptService, queryBuilder, queryShardContext);
|
||||||
ParsedQuery parsedQuery = queryShardContext.toFilter(queryBuilder);
|
ParsedQuery parsedQuery = queryShardContext.toFilter(queryBuilder);
|
||||||
|
|
|
@ -12,10 +12,8 @@ import javax.crypto.KeyGenerator;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
@ -25,12 +23,12 @@ import java.util.List;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
|
import org.elasticsearch.common.io.Streams;
|
||||||
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;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
|
||||||
import org.elasticsearch.xpack.XPackPlugin;
|
|
||||||
import org.elasticsearch.xpack.security.authc.support.CharArrays;
|
import org.elasticsearch.xpack.security.authc.support.CharArrays;
|
||||||
|
import org.elasticsearch.xpack.watcher.Watcher;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.Security.setting;
|
import static org.elasticsearch.xpack.security.Security.setting;
|
||||||
|
|
||||||
|
@ -42,14 +40,19 @@ public class CryptoService extends AbstractComponent {
|
||||||
public static final String KEY_ALGO = "HmacSHA512";
|
public static final String KEY_ALGO = "HmacSHA512";
|
||||||
public static final int KEY_SIZE = 1024;
|
public static final int KEY_SIZE = 1024;
|
||||||
|
|
||||||
static final String FILE_NAME = "system_key";
|
|
||||||
static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CTR/NoPadding";
|
|
||||||
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;
|
|
||||||
|
|
||||||
private static final Setting<Boolean> SYSTEM_KEY_REQUIRED_SETTING =
|
// the encryption used in this class was picked when Java 7 was still the min. supported
|
||||||
Setting.boolSetting(setting("system_key.required"), false, Property.NodeScope);
|
// version. The use of counter mode was chosen to simplify the need to deal with padding
|
||||||
|
// and for its speed. 128 bit key length is chosen due to the JCE policy that ships by
|
||||||
|
// default with the Oracle JDK.
|
||||||
|
// TODO: with better support in Java 8, we should consider moving to use AES GCM as it
|
||||||
|
// also provides authentication of the encrypted data, which is something that we are
|
||||||
|
// missing here.
|
||||||
|
private static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CTR/NoPadding";
|
||||||
|
private static final String DEFAULT_KEY_ALGORITH = "AES";
|
||||||
|
private static final int DEFAULT_KEY_LENGTH = 128;
|
||||||
|
|
||||||
private static final Setting<String> ENCRYPTION_ALGO_SETTING =
|
private static final Setting<String> ENCRYPTION_ALGO_SETTING =
|
||||||
new Setting<>(setting("encryption.algorithm"), s -> DEFAULT_ENCRYPTION_ALGORITHM, s -> s, Property.NodeScope);
|
new Setting<>(setting("encryption.algorithm"), s -> DEFAULT_ENCRYPTION_ALGORITHM, s -> s, Property.NodeScope);
|
||||||
private static final Setting<Integer> ENCRYPTION_KEY_LENGTH_SETTING =
|
private static final Setting<Integer> ENCRYPTION_KEY_LENGTH_SETTING =
|
||||||
|
@ -65,7 +68,7 @@ public class CryptoService extends AbstractComponent {
|
||||||
*/
|
*/
|
||||||
private final SecretKey encryptionKey;
|
private final SecretKey encryptionKey;
|
||||||
|
|
||||||
public CryptoService(Settings settings, Environment env) throws IOException {
|
public CryptoService(Settings settings) throws IOException {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings);
|
this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings);
|
||||||
final int keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings);
|
final int keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings);
|
||||||
|
@ -76,17 +79,13 @@ public class CryptoService extends AbstractComponent {
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
Path keyFile = resolveSystemKey(env);
|
SecretKey systemKey = readSystemKey(Watcher.ENCRYPTION_KEY_SETTING.get(settings));
|
||||||
SecretKey systemKey = readSystemKey(keyFile, SYSTEM_KEY_REQUIRED_SETTING.get(settings));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
|
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
|
||||||
} catch (NoSuchAlgorithmException nsae) {
|
} catch (NoSuchAlgorithmException nsae) {
|
||||||
throw new ElasticsearchException("failed to start crypto service. could not load encryption key", nsae);
|
throw new ElasticsearchException("failed to start crypto service. could not load encryption key", nsae);
|
||||||
}
|
}
|
||||||
if (systemKey != null) {
|
assert encryptionKey != null : "the encryption key should never be null";
|
||||||
logger.info("system key [{}] has been loaded", keyFile.toAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] generateKey() {
|
public static byte[] generateKey() {
|
||||||
|
@ -103,21 +102,14 @@ public class CryptoService extends AbstractComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Path resolveSystemKey(Environment env) {
|
private static SecretKey readSystemKey(InputStream in) throws IOException {
|
||||||
return XPackPlugin.resolveConfigFile(env, FILE_NAME);
|
final int keySizeBytes = KEY_SIZE / 8;
|
||||||
|
final byte[] keyBytes = new byte[keySizeBytes];
|
||||||
|
final int read = Streams.readFully(in, keyBytes);
|
||||||
|
if (read != keySizeBytes) {
|
||||||
|
throw new IllegalArgumentException("key size did not match expected value; was the key generated with syskeygen?");
|
||||||
}
|
}
|
||||||
|
return new SecretKeySpec(keyBytes, KEY_ALGO);
|
||||||
private static SecretKey readSystemKey(Path file, boolean required) throws IOException {
|
|
||||||
if (Files.exists(file)) {
|
|
||||||
byte[] bytes = Files.readAllBytes(file);
|
|
||||||
return new SecretKeySpec(bytes, KEY_ALGO);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (required) {
|
|
||||||
throw new FileNotFoundException("[" + file + "] must be present with a valid key");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,15 +118,8 @@ public class CryptoService extends AbstractComponent {
|
||||||
* @return character array representing the encrypted data
|
* @return character array representing the encrypted data
|
||||||
*/
|
*/
|
||||||
public char[] encrypt(char[] chars) {
|
public char[] encrypt(char[] chars) {
|
||||||
SecretKey key = this.encryptionKey;
|
|
||||||
if (key == null) {
|
|
||||||
logger.warn("encrypt called without a key, returning plain text. run syskeygen and copy same key to all nodes to enable " +
|
|
||||||
"encryption");
|
|
||||||
return chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] charBytes = CharArrays.toUtf8Bytes(chars);
|
byte[] charBytes = CharArrays.toUtf8Bytes(chars);
|
||||||
String base64 = Base64.getEncoder().encodeToString(encryptInternal(charBytes, key));
|
String base64 = Base64.getEncoder().encodeToString(encryptInternal(charBytes, encryptionKey));
|
||||||
return ENCRYPTED_TEXT_PREFIX.concat(base64).toCharArray();
|
return ENCRYPTED_TEXT_PREFIX.concat(base64).toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,10 +129,6 @@ public class CryptoService extends AbstractComponent {
|
||||||
* @return plaintext chars
|
* @return plaintext chars
|
||||||
*/
|
*/
|
||||||
public char[] decrypt(char[] chars) {
|
public char[] decrypt(char[] chars) {
|
||||||
if (encryptionKey == null) {
|
|
||||||
return chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEncrypted(chars)) {
|
if (!isEncrypted(chars)) {
|
||||||
// Not encrypted
|
// Not encrypted
|
||||||
return chars;
|
return chars;
|
||||||
|
@ -170,18 +151,10 @@ public class CryptoService extends AbstractComponent {
|
||||||
* @param chars the chars to check if they are encrypted
|
* @param chars the chars to check if they are encrypted
|
||||||
* @return true is data is encrypted
|
* @return true is data is encrypted
|
||||||
*/
|
*/
|
||||||
public boolean isEncrypted(char[] chars) {
|
protected boolean isEncrypted(char[] chars) {
|
||||||
return CharArrays.charsBeginsWith(ENCRYPTED_TEXT_PREFIX, chars);
|
return CharArrays.charsBeginsWith(ENCRYPTED_TEXT_PREFIX, chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag for callers to determine if values will actually be encrypted or returned plaintext
|
|
||||||
* @return true if values will be encrypted
|
|
||||||
*/
|
|
||||||
public boolean isEncryptionEnabled() {
|
|
||||||
return this.encryptionKey != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] encryptInternal(byte[] bytes, SecretKey key) {
|
private byte[] encryptInternal(byte[] bytes, SecretKey key) {
|
||||||
byte[] iv = new byte[ivLength];
|
byte[] iv = new byte[ivLength];
|
||||||
secureRandom.nextBytes(iv);
|
secureRandom.nextBytes(iv);
|
||||||
|
@ -217,7 +190,7 @@ public class CryptoService extends AbstractComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Cipher cipher(int mode, String encryptionAlgorithm, SecretKey key, byte[] initializationVector) {
|
private static Cipher cipher(int mode, String encryptionAlgorithm, SecretKey key, byte[] initializationVector) {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance(encryptionAlgorithm);
|
Cipher cipher = Cipher.getInstance(encryptionAlgorithm);
|
||||||
cipher.init(mode, key, new IvParameterSpec(initializationVector));
|
cipher.init(mode, key, new IvParameterSpec(initializationVector));
|
||||||
|
@ -227,11 +200,7 @@ public class CryptoService extends AbstractComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) throws NoSuchAlgorithmException {
|
private static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) throws NoSuchAlgorithmException {
|
||||||
if (systemKey == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = systemKey.getEncoded();
|
byte[] bytes = systemKey.getEncoded();
|
||||||
if ((bytes.length * 8) < keyLength) {
|
if ((bytes.length * 8) < keyLength) {
|
||||||
throw new IllegalArgumentException("at least " + keyLength + " bits should be provided as key data");
|
throw new IllegalArgumentException("at least " + keyLength + " bits should be provided as key data");
|
||||||
|
@ -253,6 +222,5 @@ public class CryptoService extends AbstractComponent {
|
||||||
settings.add(ENCRYPTION_KEY_LENGTH_SETTING);
|
settings.add(ENCRYPTION_KEY_LENGTH_SETTING);
|
||||||
settings.add(ENCRYPTION_KEY_ALGO_SETTING);
|
settings.add(ENCRYPTION_KEY_ALGO_SETTING);
|
||||||
settings.add(ENCRYPTION_ALGO_SETTING);
|
settings.add(ENCRYPTION_ALGO_SETTING);
|
||||||
settings.add(SYSTEM_KEY_REQUIRED_SETTING);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.common.SuppressForbidden;
|
||||||
import org.elasticsearch.common.io.PathUtils;
|
import org.elasticsearch.common.io.PathUtils;
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
|
import org.elasticsearch.xpack.XPackPlugin;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
@ -61,7 +62,7 @@ public class SystemKeyTool extends EnvironmentAwareCommand {
|
||||||
}
|
}
|
||||||
keyPath = parsePath(args.get(0));
|
keyPath = parsePath(args.get(0));
|
||||||
} else {
|
} else {
|
||||||
keyPath = CryptoService.resolveSystemKey(env);
|
keyPath = env.configFile().resolve(XPackPlugin.NAME).resolve("system_key");
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the key
|
// write the key
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.tasks.Task;
|
import org.elasticsearch.tasks.Task;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
import org.elasticsearch.transport.TcpTransport;
|
||||||
import org.elasticsearch.transport.Transport;
|
import org.elasticsearch.transport.Transport;
|
||||||
import org.elasticsearch.transport.TransportChannel;
|
import org.elasticsearch.transport.TransportChannel;
|
||||||
import org.elasticsearch.transport.TransportException;
|
import org.elasticsearch.transport.TransportException;
|
||||||
|
@ -28,7 +29,6 @@ import org.elasticsearch.transport.TransportResponse;
|
||||||
import org.elasticsearch.transport.TransportResponseHandler;
|
import org.elasticsearch.transport.TransportResponseHandler;
|
||||||
import org.elasticsearch.transport.TransportService;
|
import org.elasticsearch.transport.TransportService;
|
||||||
import org.elasticsearch.transport.TransportService.ContextRestoreResponseHandler;
|
import org.elasticsearch.transport.TransportService.ContextRestoreResponseHandler;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.SecurityContext;
|
import org.elasticsearch.xpack.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
|
@ -168,9 +168,9 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profileFilters.containsKey(TransportSettings.DEFAULT_PROFILE)) {
|
if (!profileFilters.containsKey(TcpTransport.DEFAULT_PROFILE)) {
|
||||||
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(transportSSLSettings);
|
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(transportSSLSettings);
|
||||||
profileFilters.put(TransportSettings.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService,
|
profileFilters.put(TcpTransport.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService,
|
||||||
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled, securityContext));
|
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled, securityContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.transport.BoundTransportAddress;
|
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||||
import org.elasticsearch.common.transport.TransportAddress;
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
import org.elasticsearch.transport.TcpTransport;
|
||||||
import org.elasticsearch.xpack.security.audit.AuditTrailService;
|
import org.elasticsearch.xpack.security.audit.AuditTrailService;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
@ -118,7 +118,7 @@ public class IPFilter {
|
||||||
isHttpFilterEnabled = IP_FILTER_ENABLED_HTTP_SETTING.get(settings);
|
isHttpFilterEnabled = IP_FILTER_ENABLED_HTTP_SETTING.get(settings);
|
||||||
isIpFilterEnabled = IP_FILTER_ENABLED_SETTING.get(settings);
|
isIpFilterEnabled = IP_FILTER_ENABLED_SETTING.get(settings);
|
||||||
|
|
||||||
this.transportGroups = TransportSettings.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(); // this is pretty crazy that we
|
this.transportGroups = TcpTransport.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(); // this is pretty crazy that we
|
||||||
// allow this to be updateable!!! - we have to fix this very soon
|
// allow this to be updateable!!! - we have to fix this very soon
|
||||||
clusterSettings.addSettingsUpdateConsumer(IP_FILTER_ENABLED_HTTP_SETTING, this::setHttpFiltering);
|
clusterSettings.addSettingsUpdateConsumer(IP_FILTER_ENABLED_HTTP_SETTING, this::setHttpFiltering);
|
||||||
clusterSettings.addSettingsUpdateConsumer(IP_FILTER_ENABLED_SETTING, this::setTransportFiltering);
|
clusterSettings.addSettingsUpdateConsumer(IP_FILTER_ENABLED_SETTING, this::setTransportFiltering);
|
||||||
|
@ -126,7 +126,7 @@ public class IPFilter {
|
||||||
clusterSettings.addSettingsUpdateConsumer(TRANSPORT_FILTER_DENY_SETTING, this::setTransportDenyFilter);
|
clusterSettings.addSettingsUpdateConsumer(TRANSPORT_FILTER_DENY_SETTING, this::setTransportDenyFilter);
|
||||||
clusterSettings.addSettingsUpdateConsumer(HTTP_FILTER_ALLOW_SETTING, this::setHttpAllowFilter);
|
clusterSettings.addSettingsUpdateConsumer(HTTP_FILTER_ALLOW_SETTING, this::setHttpAllowFilter);
|
||||||
clusterSettings.addSettingsUpdateConsumer(HTTP_FILTER_DENY_SETTING, this::setHttpDenyFilter);
|
clusterSettings.addSettingsUpdateConsumer(HTTP_FILTER_DENY_SETTING, this::setHttpDenyFilter);
|
||||||
clusterSettings.addSettingsUpdateConsumer(TransportSettings.TRANSPORT_PROFILES_SETTING, this::setTransportProfiles);
|
clusterSettings.addSettingsUpdateConsumer(TcpTransport.TRANSPORT_PROFILES_SETTING, this::setTransportProfiles);
|
||||||
updateRules();
|
updateRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.BigArrays;
|
import org.elasticsearch.common.util.BigArrays;
|
||||||
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
import org.elasticsearch.transport.TcpTransport;
|
||||||
import org.elasticsearch.transport.netty4.Netty4Transport;
|
import org.elasticsearch.transport.netty4.Netty4Transport;
|
||||||
import org.elasticsearch.xpack.ssl.SSLConfiguration;
|
import org.elasticsearch.xpack.ssl.SSLConfiguration;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
|
@ -65,8 +65,8 @@ public class SecurityNetty4Transport extends Netty4Transport {
|
||||||
profileConfiguration.put(entry.getKey(), configuration);
|
profileConfiguration.put(entry.getKey(), configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profileConfiguration.containsKey(TransportSettings.DEFAULT_PROFILE) == false) {
|
if (profileConfiguration.containsKey(TcpTransport.DEFAULT_PROFILE) == false) {
|
||||||
profileConfiguration.put(TransportSettings.DEFAULT_PROFILE, sslConfiguration);
|
profileConfiguration.put(TcpTransport.DEFAULT_PROFILE, sslConfiguration);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ package org.elasticsearch.xpack.security.user;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reserved {@code elastic} superuser. Has full permission/access to the cluster/indices and can
|
* The reserved {@code elastic} superuser. Has full permission/access to the cluster/indices and can
|
||||||
* run as any other user.
|
* run as any other user.
|
||||||
|
@ -15,8 +18,27 @@ public class ElasticUser extends User {
|
||||||
|
|
||||||
public static final String NAME = "elastic";
|
public static final String NAME = "elastic";
|
||||||
private static final String ROLE_NAME = "superuser";
|
private static final String ROLE_NAME = "superuser";
|
||||||
|
private static final String SETUP_MODE = "_setup_mode";
|
||||||
|
|
||||||
public ElasticUser(boolean enabled) {
|
public ElasticUser(boolean enabled) {
|
||||||
super(NAME, new String[] { ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);
|
this(enabled, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElasticUser(boolean enabled, boolean setupMode) {
|
||||||
|
super(NAME, new String[] { ROLE_NAME }, null, null, metadata(setupMode), enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isElasticUserInSetupMode(User user) {
|
||||||
|
return NAME.equals(user.principal()) && Boolean.TRUE.equals(user.metadata().get(SETUP_MODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> metadata(boolean setupMode) {
|
||||||
|
if (setupMode == false) {
|
||||||
|
return MetadataUtils.DEFAULT_RESERVED_METADATA;
|
||||||
|
} else {
|
||||||
|
HashMap<String, Object> metadata = new HashMap<>(MetadataUtils.DEFAULT_RESERVED_METADATA);
|
||||||
|
metadata.put(SETUP_MODE, true);
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
import org.elasticsearch.transport.TcpTransport;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.common.socket.SocketAccess;
|
import org.elasticsearch.xpack.common.socket.SocketAccess;
|
||||||
import org.elasticsearch.xpack.security.Security;
|
import org.elasticsearch.xpack.security.Security;
|
||||||
|
@ -852,7 +852,7 @@ public class SSLService extends AbstractComponent {
|
||||||
|
|
||||||
private static List<Settings> getTransportProfileSSLSettings(Settings settings) {
|
private static List<Settings> getTransportProfileSSLSettings(Settings settings) {
|
||||||
List<Settings> sslSettings = new ArrayList<>();
|
List<Settings> sslSettings = new ArrayList<>();
|
||||||
Map<String, Settings> profiles = TransportSettings.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(true);
|
Map<String, Settings> profiles = TcpTransport.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(true);
|
||||||
for (Entry<String, Settings> entry : profiles.entrySet()) {
|
for (Entry<String, Settings> entry : profiles.entrySet()) {
|
||||||
Settings profileSettings = entry.getValue().getByPrefix("xpack.security.ssl.");
|
Settings profileSettings = entry.getValue().getByPrefix("xpack.security.ssl.");
|
||||||
if (profileSettings.isEmpty() == false) {
|
if (profileSettings.isEmpty() == false) {
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.watcher;
|
||||||
|
|
||||||
|
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.env.Environment;
|
||||||
|
import org.elasticsearch.xpack.XPackPlugin;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
final class EncryptSensitiveDataBootstrapCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
private final Settings settings;
|
||||||
|
private final Environment environment;
|
||||||
|
|
||||||
|
EncryptSensitiveDataBootstrapCheck(Settings settings, Environment environment) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return Watcher.ENCRYPT_SENSITIVE_DATA_SETTING.get(settings) && Watcher.ENCRYPTION_KEY_SETTING.exists(settings) == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
final Path sysKeyPath = environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath();
|
||||||
|
if (Files.exists(sysKeyPath)) {
|
||||||
|
return "Encryption of sensitive data requires the key to be placed in the secure setting store. Run " +
|
||||||
|
"'bin/elasticsearch-keystore add-file " + Watcher.ENCRYPTION_KEY_SETTING.getKey() + " " +
|
||||||
|
environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath() +
|
||||||
|
"' to import the file.\nAfter importing, the system_key file should be removed from the " +
|
||||||
|
"filesystem.\nRepeat this on every node in the cluster.";
|
||||||
|
} else {
|
||||||
|
return "Encryption of sensitive data requires a key to be placed in the secure setting store. First run the " +
|
||||||
|
"bin/x-pack/syskeygen tool to generate a key file.\nThen run 'bin/elasticsearch-keystore add-file " +
|
||||||
|
Watcher.ENCRYPTION_KEY_SETTING.getKey() + " " +
|
||||||
|
environment.configFile().resolve(XPackPlugin.NAME).resolve("system_key").toAbsolutePath() + "' to import the key into" +
|
||||||
|
" the secure setting store. Finally, remove the system_key file from the filesystem.\n" +
|
||||||
|
"Repeat this on every node in the cluster";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean alwaysEnforce() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.watcher;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionRequest;
|
import org.elasticsearch.action.ActionRequest;
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
|
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||||
import org.elasticsearch.cluster.NamedDiff;
|
import org.elasticsearch.cluster.NamedDiff;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
|
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
|
||||||
|
@ -25,12 +26,14 @@ import org.elasticsearch.common.logging.Loggers;
|
||||||
import org.elasticsearch.common.regex.Regex;
|
import org.elasticsearch.common.regex.Regex;
|
||||||
import org.elasticsearch.common.settings.ClusterSettings;
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.IndexScopedSettings;
|
import org.elasticsearch.common.settings.IndexScopedSettings;
|
||||||
|
import org.elasticsearch.common.settings.SecureSetting;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.settings.SettingsFilter;
|
import org.elasticsearch.common.settings.SettingsFilter;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.util.concurrent.EsExecutors;
|
import org.elasticsearch.common.util.concurrent.EsExecutors;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.index.IndexModule;
|
import org.elasticsearch.index.IndexModule;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.plugins.ActionPlugin;
|
import org.elasticsearch.plugins.ActionPlugin;
|
||||||
|
@ -153,6 +156,7 @@ import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -177,6 +181,7 @@ public class Watcher implements ActionPlugin {
|
||||||
new Setting<>("index.xpack.watcher.template.version", "", Function.identity(), Setting.Property.IndexScope);
|
new Setting<>("index.xpack.watcher.template.version", "", Function.identity(), Setting.Property.IndexScope);
|
||||||
public static final Setting<Boolean> ENCRYPT_SENSITIVE_DATA_SETTING =
|
public static final Setting<Boolean> ENCRYPT_SENSITIVE_DATA_SETTING =
|
||||||
Setting.boolSetting("xpack.watcher.encrypt_sensitive_data", false, Setting.Property.NodeScope);
|
Setting.boolSetting("xpack.watcher.encrypt_sensitive_data", false, Setting.Property.NodeScope);
|
||||||
|
public static final Setting<InputStream> ENCRYPTION_KEY_SETTING = SecureSetting.secureFile("xpack.watcher.encryption_key", null);
|
||||||
public static final Setting<TimeValue> MAX_STOP_TIMEOUT_SETTING =
|
public static final Setting<TimeValue> MAX_STOP_TIMEOUT_SETTING =
|
||||||
Setting.timeSetting("xpack.watcher.stop.timeout", TimeValue.timeValueSeconds(30), Setting.Property.NodeScope);
|
Setting.timeSetting("xpack.watcher.stop.timeout", TimeValue.timeValueSeconds(30), Setting.Property.NodeScope);
|
||||||
|
|
||||||
|
@ -367,6 +372,7 @@ public class Watcher implements ActionPlugin {
|
||||||
settings.add(Setting.intSetting("xpack.watcher.execution.scroll.size", 0, Setting.Property.NodeScope));
|
settings.add(Setting.intSetting("xpack.watcher.execution.scroll.size", 0, Setting.Property.NodeScope));
|
||||||
settings.add(Setting.intSetting("xpack.watcher.watch.scroll.size", 0, Setting.Property.NodeScope));
|
settings.add(Setting.intSetting("xpack.watcher.watch.scroll.size", 0, Setting.Property.NodeScope));
|
||||||
settings.add(ENCRYPT_SENSITIVE_DATA_SETTING);
|
settings.add(ENCRYPT_SENSITIVE_DATA_SETTING);
|
||||||
|
settings.add(ENCRYPTION_KEY_SETTING);
|
||||||
|
|
||||||
settings.add(Setting.simpleString("xpack.watcher.internal.ops.search.default_timeout", Setting.Property.NodeScope));
|
settings.add(Setting.simpleString("xpack.watcher.internal.ops.search.default_timeout", Setting.Property.NodeScope));
|
||||||
settings.add(Setting.simpleString("xpack.watcher.internal.ops.bulk.default_timeout", Setting.Property.NodeScope));
|
settings.add(Setting.simpleString("xpack.watcher.internal.ops.bulk.default_timeout", Setting.Property.NodeScope));
|
||||||
|
@ -379,6 +385,8 @@ public class Watcher implements ActionPlugin {
|
||||||
settings.add(Setting.simpleString("xpack.watcher.execution.scroll.timeout", Setting.Property.NodeScope));
|
settings.add(Setting.simpleString("xpack.watcher.execution.scroll.timeout", Setting.Property.NodeScope));
|
||||||
settings.add(Setting.simpleString("xpack.watcher.start_immediately", Setting.Property.NodeScope));
|
settings.add(Setting.simpleString("xpack.watcher.start_immediately", Setting.Property.NodeScope));
|
||||||
|
|
||||||
|
// encryption settings
|
||||||
|
CryptoService.addSettings(settings);
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,4 +520,8 @@ public class Watcher implements ActionPlugin {
|
||||||
return map;
|
return map;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<BootstrapCheck> getBootstrapChecks() {
|
||||||
|
return Collections.singletonList(new EncryptSensitiveDataBootstrapCheck(settings, new Environment(settings)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.script.TemplateScript;
|
import org.elasticsearch.script.TemplateScript;
|
||||||
|
@ -63,7 +62,7 @@ public class WatcherSearchTemplateService extends AbstractComponent {
|
||||||
BytesReference source = request.getSearchSource();
|
BytesReference source = request.getSearchSource();
|
||||||
if (source != null && source.length() > 0) {
|
if (source != null && source.length() > 0) {
|
||||||
try (XContentParser parser = XContentFactory.xContent(source).createParser(xContentRegistry, source)) {
|
try (XContentParser parser = XContentFactory.xContent(source).createParser(xContentRegistry, source)) {
|
||||||
sourceBuilder.parseXContent(new QueryParseContext(parser));
|
sourceBuilder.parseXContent(parser);
|
||||||
searchRequest.source(sourceBuilder);
|
searchRequest.source(sourceBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.xpack.common.secret.Secret;
|
import org.elasticsearch.xpack.common.secret.Secret;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
||||||
import org.elasticsearch.xpack.support.clock.HaltedClock;
|
import org.elasticsearch.xpack.support.clock.HaltedClock;
|
||||||
import org.elasticsearch.xpack.watcher.Watcher;
|
|
||||||
import org.elasticsearch.xpack.watcher.actions.ActionRegistry;
|
import org.elasticsearch.xpack.watcher.actions.ActionRegistry;
|
||||||
import org.elasticsearch.xpack.watcher.actions.ActionStatus;
|
import org.elasticsearch.xpack.watcher.actions.ActionStatus;
|
||||||
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
||||||
|
@ -209,7 +208,7 @@ public class Watch implements ToXContentObject {
|
||||||
this.triggerService = triggerService;
|
this.triggerService = triggerService;
|
||||||
this.actionRegistry = actionRegistry;
|
this.actionRegistry = actionRegistry;
|
||||||
this.inputRegistry = inputRegistry;
|
this.inputRegistry = inputRegistry;
|
||||||
this.cryptoService = Watcher.ENCRYPT_SENSITIVE_DATA_SETTING.get(settings) ? cryptoService : null;
|
this.cryptoService = cryptoService;
|
||||||
this.defaultInput = new ExecutableNoneInput(logger);
|
this.defaultInput = new ExecutableNoneInput(logger);
|
||||||
this.defaultCondition = AlwaysCondition.INSTANCE;
|
this.defaultCondition = AlwaysCondition.INSTANCE;
|
||||||
this.defaultActions = Collections.emptyList();
|
this.defaultActions = Collections.emptyList();
|
||||||
|
|
|
@ -74,8 +74,8 @@ public class BulkUpdateTests extends SecurityIntegTestCase {
|
||||||
public void testThatBulkUpdateDoesNotLoseFieldsHttp() throws IOException {
|
public void testThatBulkUpdateDoesNotLoseFieldsHttp() throws IOException {
|
||||||
final String path = "/index1/type/1";
|
final String path = "/index1/type/1";
|
||||||
final Header basicAuthHeader = new BasicHeader("Authorization",
|
final Header basicAuthHeader = new BasicHeader("Authorization",
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
|
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_USER_NAME,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray())));
|
||||||
|
|
||||||
StringEntity body = new StringEntity("{\"test\":\"test\"}", ContentType.APPLICATION_JSON);
|
StringEntity body = new StringEntity("{\"test\":\"test\"}", ContentType.APPLICATION_JSON);
|
||||||
Response response = getRestClient().performRequest("PUT", path, Collections.emptyMap(), body, basicAuthHeader);
|
Response response = getRestClient().performRequest("PUT", path, Collections.emptyMap(), body, basicAuthHeader);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.test.SecurityIntegTestCase;
|
||||||
import org.elasticsearch.test.SecuritySettingsSource;
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheRequest;
|
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheRequest;
|
||||||
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheResponse;
|
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheResponse;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.Realm;
|
import org.elasticsearch.xpack.security.authc.Realm;
|
||||||
import org.elasticsearch.xpack.security.authc.Realms;
|
import org.elasticsearch.xpack.security.authc.Realms;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
|
@ -40,6 +41,7 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.sameInstance;
|
import static org.hamcrest.Matchers.sameInstance;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
public class ClearRealmsCacheTests extends SecurityIntegTestCase {
|
public class ClearRealmsCacheTests extends SecurityIntegTestCase {
|
||||||
private static final String USERS_PASSWD_HASHED = new String(Hasher.BCRYPT.hash(new SecureString("passwd".toCharArray())));
|
private static final String USERS_PASSWD_HASHED = new String(Hasher.BCRYPT.hash(new SecureString("passwd".toCharArray())));
|
||||||
|
@ -164,8 +166,8 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
|
||||||
static void executeHttpRequest(String path, Map<String, String> params) throws Exception {
|
static void executeHttpRequest(String path, Map<String, String> params) throws Exception {
|
||||||
Response response = getRestClient().performRequest("POST", path, params,
|
Response response = getRestClient().performRequest("POST", path, params,
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
|
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_USER_NAME,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray()))));
|
||||||
assertNotNull(response.getEntity());
|
assertNotNull(response.getEntity());
|
||||||
assertTrue(EntityUtils.toString(response.getEntity()).contains("cluster_name"));
|
assertTrue(EntityUtils.toString(response.getEntity()).contains("cluster_name"));
|
||||||
}
|
}
|
||||||
|
@ -233,7 +235,7 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
|
||||||
for (Realm realm : realms) {
|
for (Realm realm : realms) {
|
||||||
for (String username : usernames) {
|
for (String username : usernames) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(tokens.get(username), future);
|
realm.authenticate(tokens.get(username), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, notNullValue());
|
assertThat(user, notNullValue());
|
||||||
Map<Realm, User> realmToUser = users.get(username);
|
Map<Realm, User> realmToUser = users.get(username);
|
||||||
|
@ -250,7 +252,7 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
|
||||||
for (String username : usernames) {
|
for (String username : usernames) {
|
||||||
for (Realm realm : realms) {
|
for (Realm realm : realms) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(tokens.get(username), future);
|
realm.authenticate(tokens.get(username), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, sameInstance(users.get(username).get(realm)));
|
assertThat(user, sameInstance(users.get(username).get(realm)));
|
||||||
}
|
}
|
||||||
|
@ -263,7 +265,7 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
|
||||||
for (String username : usernames) {
|
for (String username : usernames) {
|
||||||
for (Realm realm : realms) {
|
for (Realm realm : realms) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(tokens.get(username), future);
|
realm.authenticate(tokens.get(username), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, notNullValue());
|
assertThat(user, notNullValue());
|
||||||
scenario.assertEviction(users.get(username).get(realm), user);
|
scenario.assertEviction(users.get(username).get(realm), user);
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class MultipleIndicesPermissionsTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String configRoles() {
|
protected String configRoles() {
|
||||||
return SecuritySettingsSource.DEFAULT_ROLE + ":\n" +
|
return SecuritySettingsSource.TEST_ROLE + ":\n" +
|
||||||
" cluster: [ all ]\n" +
|
" cluster: [ all ]\n" +
|
||||||
" indices:\n" +
|
" indices:\n" +
|
||||||
" - names: '*'\n" +
|
" - names: '*'\n" +
|
||||||
|
|
|
@ -7,7 +7,6 @@ package org.elasticsearch.integration;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.indices.TermsLookup;
|
import org.elasticsearch.indices.TermsLookup;
|
||||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||||
|
@ -26,7 +25,7 @@ public class SecurityCachePermissionTests extends SecurityIntegTestCase {
|
||||||
@Override
|
@Override
|
||||||
public String configUsers() {
|
public String configUsers() {
|
||||||
return super.configUsers()
|
return super.configUsers()
|
||||||
+ READ_ONE_IDX_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD_HASHED + "\n";
|
+ READ_ONE_IDX_USER + ":" + SecuritySettingsSource.TEST_PASSWORD_HASHED + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,7 +60,7 @@ public class SecurityCachePermissionTests extends SecurityIntegTestCase {
|
||||||
// Repeat with unauthorized user!!!!
|
// Repeat with unauthorized user!!!!
|
||||||
try {
|
try {
|
||||||
response = client().filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
|
response = client().filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
|
||||||
new SecureString("changeme".toCharArray()))))
|
SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING)))
|
||||||
.prepareSearch("data").setTypes("a").setQuery(QueryBuilders.constantScoreQuery(
|
.prepareSearch("data").setTypes("a").setQuery(QueryBuilders.constantScoreQuery(
|
||||||
QueryBuilders.termsLookupQuery("token", new TermsLookup("tokens", "tokens", "1", "tokens"))))
|
QueryBuilders.termsLookupQuery("token", new TermsLookup("tokens", "tokens", "1", "tokens"))))
|
||||||
.execute().actionGet();
|
.execute().actionGet();
|
||||||
|
|
|
@ -78,7 +78,7 @@ import static org.hamcrest.Matchers.notNullValue;
|
||||||
@TestLogging("org.elasticsearch.cluster.service:TRACE,org.elasticsearch.discovery.zen:TRACE")
|
@TestLogging("org.elasticsearch.cluster.service:TRACE,org.elasticsearch.discovery.zen:TRACE")
|
||||||
public class LicensingTests extends SecurityIntegTestCase {
|
public class LicensingTests extends SecurityIntegTestCase {
|
||||||
public static final String ROLES =
|
public static final String ROLES =
|
||||||
SecuritySettingsSource.DEFAULT_ROLE + ":\n" +
|
SecuritySettingsSource.TEST_ROLE + ":\n" +
|
||||||
" cluster: [ all ]\n" +
|
" cluster: [ all ]\n" +
|
||||||
" indices:\n" +
|
" indices:\n" +
|
||||||
" - names: '*'\n" +
|
" - names: '*'\n" +
|
||||||
|
@ -204,8 +204,8 @@ public class LicensingTests extends SecurityIntegTestCase {
|
||||||
e = expectThrows(ResponseException.class, () -> getRestClient().performRequest("GET", "/_xpack/security/_authenticate"));
|
e = expectThrows(ResponseException.class, () -> getRestClient().performRequest("GET", "/_xpack/security/_authenticate"));
|
||||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
|
||||||
|
|
||||||
final String basicAuthValue = UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
|
final String basicAuthValue = UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_USER_NAME,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray()));
|
||||||
response = getRestClient().performRequest("GET", "/", new BasicHeader("Authorization", basicAuthValue));
|
response = getRestClient().performRequest("GET", "/", new BasicHeader("Authorization", basicAuthValue));
|
||||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||||
response = getRestClient()
|
response = getRestClient()
|
||||||
|
|
|
@ -5,10 +5,28 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.test;
|
package org.elasticsearch.test;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.nio.entity.NStringEntity;
|
||||||
|
import org.elasticsearch.client.Response;
|
||||||
|
import org.elasticsearch.common.network.NetworkModule;
|
||||||
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
|
import org.elasticsearch.xpack.security.user.BeatsSystemUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.ElasticUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test case with method to handle the starting and stopping the stores for native users and roles
|
* Test case with method to handle the starting and stopping the stores for native users and roles
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +35,7 @@ public abstract class NativeRealmIntegTestCase extends SecurityIntegTestCase {
|
||||||
@Before
|
@Before
|
||||||
public void ensureNativeStoresStarted() throws Exception {
|
public void ensureNativeStoresStarted() throws Exception {
|
||||||
assertSecurityIndexActive();
|
assertSecurityIndexActive();
|
||||||
|
setupReservedPasswords();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -29,4 +48,41 @@ public abstract class NativeRealmIntegTestCase extends SecurityIntegTestCase {
|
||||||
client.prepareClearRealmCache().get();
|
client.prepareClearRealmCache().get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Settings nodeSettings(int nodeOrdinal) {
|
||||||
|
return Settings.builder()
|
||||||
|
.put(super.nodeSettings(nodeOrdinal))
|
||||||
|
.put(NetworkModule.HTTP_ENABLED.getKey(), true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecureString reservedPassword = SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING;
|
||||||
|
|
||||||
|
protected SecureString getReservedPassword() {
|
||||||
|
return reservedPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldSetReservedUserPasswords() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupReservedPasswords() throws IOException {
|
||||||
|
if (shouldSetReservedUserPasswords() == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info("setting up reserved passwords for test");
|
||||||
|
SecureString defaultPassword = new SecureString("".toCharArray());
|
||||||
|
|
||||||
|
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, BeatsSystemUser.NAME, LogstashSystemUser.NAME)) {
|
||||||
|
SecureString authPassword = username.equals(ElasticUser.NAME) ? defaultPassword : reservedPassword;
|
||||||
|
String payload = "{\"password\": \"" + new String(reservedPassword.getChars()) + "\"}";
|
||||||
|
HttpEntity entity = new NStringEntity(payload, ContentType.APPLICATION_JSON);
|
||||||
|
BasicHeader authHeader = new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
|
UsernamePasswordToken.basicAuthHeaderValue(ElasticUser.NAME, authPassword));
|
||||||
|
String route = "/_xpack/security/user/" + username + "/_password";
|
||||||
|
Response response = getRestClient().performRequest("PUT", route, Collections.emptyMap(), entity, authHeader);
|
||||||
|
}
|
||||||
|
logger.info("setting up reserved passwords finished");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,8 +244,8 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
|
||||||
@Override
|
@Override
|
||||||
protected Settings externalClusterClientSettings() {
|
protected Settings externalClusterClientSettings() {
|
||||||
return Settings.builder()
|
return Settings.builder()
|
||||||
.put(Security.USER_SETTING.getKey(), SecuritySettingsSource.DEFAULT_USER_NAME + ":"
|
.put(Security.USER_SETTING.getKey(), SecuritySettingsSource.TEST_USER_NAME + ":"
|
||||||
+ SecuritySettingsSource.DEFAULT_PASSWORD)
|
+ SecuritySettingsSource.TEST_PASSWORD)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,25 +52,25 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
|
||||||
|
|
||||||
public static final Settings DEFAULT_SETTINGS = Settings.EMPTY;
|
public static final Settings DEFAULT_SETTINGS = Settings.EMPTY;
|
||||||
|
|
||||||
public static final String DEFAULT_USER_NAME = "test_user";
|
public static final String TEST_USER_NAME = "test_user";
|
||||||
public static final String DEFAULT_PASSWORD = "changeme";
|
public static final String TEST_PASSWORD = "x-pack-test-password";
|
||||||
public static final SecureString DEFAULT_PASSWORD_SECURE_STRING = new SecureString("changeme".toCharArray());
|
public static final SecureString TEST_PASSWORD_SECURE_STRING = new SecureString("x-pack-test-password".toCharArray());
|
||||||
public static final String DEFAULT_PASSWORD_HASHED = new String(Hasher.BCRYPT.hash(new SecureString(DEFAULT_PASSWORD.toCharArray())));
|
public static final String TEST_PASSWORD_HASHED = new String(Hasher.BCRYPT.hash(new SecureString(TEST_PASSWORD.toCharArray())));
|
||||||
public static final String DEFAULT_ROLE = "user";
|
public static final String TEST_ROLE = "user";
|
||||||
|
|
||||||
public static final String DEFAULT_TRANSPORT_CLIENT_ROLE = "transport_client";
|
public static final String DEFAULT_TRANSPORT_CLIENT_ROLE = "transport_client";
|
||||||
public static final String DEFAULT_TRANSPORT_CLIENT_USER_NAME = "test_trans_client_user";
|
public static final String DEFAULT_TRANSPORT_CLIENT_USER_NAME = "test_trans_client_user";
|
||||||
|
|
||||||
public static final String CONFIG_STANDARD_USER =
|
public static final String CONFIG_STANDARD_USER =
|
||||||
DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD_HASHED + "\n" +
|
TEST_USER_NAME + ":" + TEST_PASSWORD_HASHED + "\n" +
|
||||||
DEFAULT_TRANSPORT_CLIENT_USER_NAME + ":" + DEFAULT_PASSWORD_HASHED + "\n";
|
DEFAULT_TRANSPORT_CLIENT_USER_NAME + ":" + TEST_PASSWORD_HASHED + "\n";
|
||||||
|
|
||||||
public static final String CONFIG_STANDARD_USER_ROLES =
|
public static final String CONFIG_STANDARD_USER_ROLES =
|
||||||
DEFAULT_ROLE + ":" + DEFAULT_USER_NAME + "," + DEFAULT_TRANSPORT_CLIENT_USER_NAME + "\n" +
|
TEST_ROLE + ":" + TEST_USER_NAME + "," + DEFAULT_TRANSPORT_CLIENT_USER_NAME + "\n" +
|
||||||
DEFAULT_TRANSPORT_CLIENT_ROLE + ":" + DEFAULT_TRANSPORT_CLIENT_USER_NAME+ "\n";
|
DEFAULT_TRANSPORT_CLIENT_ROLE + ":" + DEFAULT_TRANSPORT_CLIENT_USER_NAME+ "\n";
|
||||||
|
|
||||||
public static final String CONFIG_ROLE_ALLOW_ALL =
|
public static final String CONFIG_ROLE_ALLOW_ALL =
|
||||||
DEFAULT_ROLE + ":\n" +
|
TEST_ROLE + ":\n" +
|
||||||
" cluster: [ ALL ]\n" +
|
" cluster: [ ALL ]\n" +
|
||||||
" indices:\n" +
|
" indices:\n" +
|
||||||
" - names: '*'\n" +
|
" - names: '*'\n" +
|
||||||
|
@ -174,11 +174,11 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String nodeClientUsername() {
|
protected String nodeClientUsername() {
|
||||||
return DEFAULT_USER_NAME;
|
return TEST_USER_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SecureString nodeClientPassword() {
|
protected SecureString nodeClientPassword() {
|
||||||
return new SecureString(DEFAULT_PASSWORD.toCharArray());
|
return new SecureString(TEST_PASSWORD.toCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String transportClientUsername() {
|
protected String transportClientUsername() {
|
||||||
|
@ -186,7 +186,7 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SecureString transportClientPassword() {
|
protected SecureString transportClientPassword() {
|
||||||
return new SecureString(DEFAULT_PASSWORD.toCharArray());
|
return new SecureString(TEST_PASSWORD.toCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class<? extends XPackPlugin> xpackPluginClass() {
|
protected Class<? extends XPackPlugin> xpackPluginClass() {
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class SecurityTestsUtils {
|
||||||
|
|
||||||
public static void assertAuthorizationExceptionDefaultUsers(Throwable throwable, String action) {
|
public static void assertAuthorizationExceptionDefaultUsers(Throwable throwable, String action) {
|
||||||
assertAuthorizationException(throwable, either(containsString("[" + action + "] is unauthorized for user ["
|
assertAuthorizationException(throwable, either(containsString("[" + action + "] is unauthorized for user ["
|
||||||
+ SecuritySettingsSource.DEFAULT_USER_NAME + "]")).or(containsString("[" + action + "] is unauthorized for user ["
|
+ SecuritySettingsSource.TEST_USER_NAME + "]")).or(containsString("[" + action + "] is unauthorized for user ["
|
||||||
+ SecuritySettingsSource.DEFAULT_TRANSPORT_CLIENT_USER_NAME + "]")));
|
+ SecuritySettingsSource.DEFAULT_TRANSPORT_CLIENT_USER_NAME + "]")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ import org.apache.http.message.BasicHeader;
|
||||||
import org.elasticsearch.client.Response;
|
import org.elasticsearch.client.Response;
|
||||||
import org.elasticsearch.client.ResponseException;
|
import org.elasticsearch.client.ResponseException;
|
||||||
import org.elasticsearch.client.RestClient;
|
import org.elasticsearch.client.RestClient;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
|
||||||
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.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
import org.elasticsearch.xpack.ml.MachineLearning;
|
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -33,9 +33,9 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
public class DatafeedJobsRestIT extends ESRestTestCase {
|
public class DatafeedJobsRestIT extends ESRestTestCase {
|
||||||
|
|
||||||
private static final String BASIC_AUTH_VALUE_ELASTIC =
|
private static final String BASIC_AUTH_VALUE_ELASTIC =
|
||||||
basicAuthHeaderValue("elastic", new SecureString("changeme".toCharArray()));
|
basicAuthHeaderValue("elastic", SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING);
|
||||||
private static final String BASIC_AUTH_VALUE_ML_ADMIN =
|
private static final String BASIC_AUTH_VALUE_ML_ADMIN =
|
||||||
basicAuthHeaderValue("ml_admin", new SecureString("changeme".toCharArray()));
|
basicAuthHeaderValue("ml_admin", SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Settings restClientSettings() {
|
protected Settings restClientSettings() {
|
||||||
|
@ -50,11 +50,16 @@ public class DatafeedJobsRestIT extends ESRestTestCase {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUpData() throws Exception {
|
public void setUpData() throws Exception {
|
||||||
|
String password = new String(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING.getChars());
|
||||||
|
String elasticUserPayload = "{\"password\" : \"" + password + "\"}";
|
||||||
|
|
||||||
|
client().performRequest("put", "_xpack/security/user/elastic/_password", Collections.emptyMap(),
|
||||||
|
new StringEntity(elasticUserPayload, ContentType.APPLICATION_JSON));
|
||||||
|
|
||||||
// This user has admin rights on machine learning, but (importantly for the tests) no
|
// This user has admin rights on machine learning, but (importantly for the tests) no
|
||||||
// rights on any of the data indexes
|
// rights on any of the data indexes
|
||||||
String user = "{"
|
String user = "{"
|
||||||
+ " \"password\" : \"changeme\","
|
+ " \"password\" : \"" + password + "\","
|
||||||
+ " \"roles\" : [ \"machine_learning_admin\" ]"
|
+ " \"roles\" : [ \"machine_learning_admin\" ]"
|
||||||
+ "}";
|
+ "}";
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.elasticsearch.client.Response;
|
import org.elasticsearch.client.Response;
|
||||||
import org.elasticsearch.client.ResponseException;
|
import org.elasticsearch.client.ResponseException;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||||
import org.elasticsearch.common.util.concurrent.ConcurrentMapLong;
|
import org.elasticsearch.common.util.concurrent.ConcurrentMapLong;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
import org.elasticsearch.xpack.ml.MachineLearning;
|
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||||
import org.elasticsearch.xpack.ml.job.persistence.AnomalyDetectorsIndex;
|
import org.elasticsearch.xpack.ml.job.persistence.AnomalyDetectorsIndex;
|
||||||
|
@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
public class MlJobIT extends ESRestTestCase {
|
public class MlJobIT extends ESRestTestCase {
|
||||||
|
|
||||||
private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("elastic", new SecureString("changeme".toCharArray()));
|
private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("elastic", SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Settings restClientSettings() {
|
protected Settings restClientSettings() {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||||
import org.elasticsearch.search.SearchModule;
|
import org.elasticsearch.search.SearchModule;
|
||||||
import org.elasticsearch.tasks.Task;
|
import org.elasticsearch.tasks.Task;
|
||||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.ml.MlMetadata;
|
import org.elasticsearch.xpack.ml.MlMetadata;
|
||||||
import org.elasticsearch.xpack.ml.action.CloseJobAction;
|
import org.elasticsearch.xpack.ml.action.CloseJobAction;
|
||||||
|
@ -80,7 +81,7 @@ abstract class MlNativeAutodetectIntegTestCase extends SecurityIntegTestCase {
|
||||||
protected Settings externalClusterClientSettings() {
|
protected Settings externalClusterClientSettings() {
|
||||||
Settings.Builder builder = Settings.builder();
|
Settings.Builder builder = Settings.builder();
|
||||||
builder.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME4);
|
builder.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME4);
|
||||||
builder.put(Security.USER_SETTING.getKey(), "elastic:changeme");
|
builder.put(Security.USER_SETTING.getKey(), "elastic:" + SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING);
|
||||||
builder.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), true);
|
builder.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), true);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,7 @@ import org.elasticsearch.xpack.ml.job.results.Bucket;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
@ -57,16 +55,14 @@ public class UpdateInterimResultsIT extends MlNativeAutodetectIntegTestCase {
|
||||||
openJob(job.getId());
|
openJob(job.getId());
|
||||||
|
|
||||||
time = 1400000000;
|
time = 1400000000;
|
||||||
Map<Long, Integer> anomalies = new HashMap<>();
|
|
||||||
anomalies.put(1400021500L, 14);
|
|
||||||
|
|
||||||
// push some data, flush job, verify no interim results
|
// push some data, flush job, verify no interim results
|
||||||
assertThat(postData(job.getId(), createData(50, anomalies)).getProcessedRecordCount(), equalTo(50L));
|
assertThat(postData(job.getId(), createData(50)).getProcessedRecordCount(), equalTo(50L));
|
||||||
flushJob(job.getId(), false);
|
flushJob(job.getId(), false);
|
||||||
assertThat(getInterimResults(job.getId()).isEmpty(), is(true));
|
assertThat(getInterimResults(job.getId()).isEmpty(), is(true));
|
||||||
|
|
||||||
// push some more data, flush job, verify no interim results
|
// push some more data, flush job, verify no interim results
|
||||||
assertThat(postData(job.getId(), createData(30, anomalies)).getProcessedRecordCount(), equalTo(30L));
|
assertThat(postData(job.getId(), createData(30)).getProcessedRecordCount(), equalTo(30L));
|
||||||
flushJob(job.getId(), false);
|
flushJob(job.getId(), false);
|
||||||
assertThat(getInterimResults(job.getId()).isEmpty(), is(true));
|
assertThat(getInterimResults(job.getId()).isEmpty(), is(true));
|
||||||
assertThat(time, equalTo(1400040000L));
|
assertThat(time, equalTo(1400040000L));
|
||||||
|
@ -81,7 +77,7 @@ public class UpdateInterimResultsIT extends MlNativeAutodetectIntegTestCase {
|
||||||
// We might need to retry this while waiting for a refresh
|
// We might need to retry this while waiting for a refresh
|
||||||
assertBusy(() -> {
|
assertBusy(() -> {
|
||||||
List<Bucket> firstInterimBuckets = getInterimResults(job.getId());
|
List<Bucket> firstInterimBuckets = getInterimResults(job.getId());
|
||||||
assertThat(firstInterimBuckets.size(), equalTo(2));
|
assertThat("interim buckets were: " + firstInterimBuckets, firstInterimBuckets.size(), equalTo(2));
|
||||||
assertThat(firstInterimBuckets.get(0).getTimestamp().getTime(), equalTo(1400039000000L));
|
assertThat(firstInterimBuckets.get(0).getTimestamp().getTime(), equalTo(1400039000000L));
|
||||||
assertThat(firstInterimBuckets.get(1).getTimestamp().getTime(), equalTo(1400040000000L));
|
assertThat(firstInterimBuckets.get(1).getTimestamp().getTime(), equalTo(1400040000000L));
|
||||||
assertThat(firstInterimBuckets.get(1).getRecords().get(0).getActual().get(0), equalTo(16.0));
|
assertThat(firstInterimBuckets.get(1).getRecords().get(0).getActual().get(0), equalTo(16.0));
|
||||||
|
@ -101,7 +97,7 @@ public class UpdateInterimResultsIT extends MlNativeAutodetectIntegTestCase {
|
||||||
|
|
||||||
// push rest of data, close, verify no interim results
|
// push rest of data, close, verify no interim results
|
||||||
time += BUCKET_SPAN_SECONDS;
|
time += BUCKET_SPAN_SECONDS;
|
||||||
assertThat(postData(job.getId(), createData(30, anomalies)).getProcessedRecordCount(), equalTo(30L));
|
assertThat(postData(job.getId(), createData(30)).getProcessedRecordCount(), equalTo(30L));
|
||||||
closeJob(job.getId());
|
closeJob(job.getId());
|
||||||
assertThat(getInterimResults(job.getId()).isEmpty(), is(true));
|
assertThat(getInterimResults(job.getId()).isEmpty(), is(true));
|
||||||
|
|
||||||
|
@ -114,10 +110,10 @@ public class UpdateInterimResultsIT extends MlNativeAutodetectIntegTestCase {
|
||||||
assertThat(bucket.get(0).getRecords().get(0).getActual().get(0), equalTo(14.0));
|
assertThat(bucket.get(0).getRecords().get(0).getActual().get(0), equalTo(14.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createData(int halfBuckets, Map<Long, Integer> timeToValueMap) {
|
private String createData(int halfBuckets) {
|
||||||
StringBuilder data = new StringBuilder();
|
StringBuilder data = new StringBuilder();
|
||||||
for (int i = 0; i < halfBuckets; i++) {
|
for (int i = 0; i < halfBuckets; i++) {
|
||||||
int value = timeToValueMap.getOrDefault(time, randomIntBetween(1, 3));
|
int value = randomIntBetween(1, 3);
|
||||||
data.append("{\"time\":").append(time).append(", \"value\":").append(value).append("}\n");
|
data.append("{\"time\":").append(time).append(", \"value\":").append(value).append("}\n");
|
||||||
time += BUCKET_SPAN_SECONDS / 2;
|
time += BUCKET_SPAN_SECONDS / 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,8 @@ public class OldMonitoringIndicesBackwardsCompatibilityTests extends AbstractOld
|
||||||
httpExporter.put("type", "http");
|
httpExporter.put("type", "http");
|
||||||
httpExporter.put("enabled", port == null ? "false" : "true");
|
httpExporter.put("enabled", port == null ? "false" : "true");
|
||||||
httpExporter.put("host", "http://localhost:" + (port == null ? "does_not_matter" : port));
|
httpExporter.put("host", "http://localhost:" + (port == null ? "does_not_matter" : port));
|
||||||
httpExporter.put("auth.username", SecuritySettingsSource.DEFAULT_USER_NAME);
|
httpExporter.put("auth.username", SecuritySettingsSource.TEST_USER_NAME);
|
||||||
httpExporter.put("auth.password", SecuritySettingsSource.DEFAULT_PASSWORD);
|
httpExporter.put("auth.password", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
|
|
||||||
settings.putProperties(httpExporter, k -> MonitoringSettings.EXPORTERS_SETTINGS.getKey() + "my_exporter." + k);
|
settings.putProperties(httpExporter, k -> MonitoringSettings.EXPORTERS_SETTINGS.getKey() + "my_exporter." + k);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.elasticsearch.index.IndexNotFoundException;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.test.TestCluster;
|
import org.elasticsearch.test.TestCluster;
|
||||||
import org.elasticsearch.test.store.MockFSIndexStore;
|
import org.elasticsearch.test.store.MockFSIndexStore;
|
||||||
import org.elasticsearch.test.transport.MockTransportService;
|
import org.elasticsearch.test.transport.MockTransportService;
|
||||||
|
@ -157,7 +158,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase {
|
||||||
return Settings.builder()
|
return Settings.builder()
|
||||||
.put(super.transportClientSettings())
|
.put(super.transportClientSettings())
|
||||||
.put("client.transport.sniff", false)
|
.put("client.transport.sniff", false)
|
||||||
.put(Security.USER_SETTING.getKey(), "test:changeme")
|
.put(Security.USER_SETTING.getKey(), "test:" + SecuritySettings.TEST_PASSWORD)
|
||||||
.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME4)
|
.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME4)
|
||||||
.put(NetworkModule.HTTP_TYPE_KEY, Security.NAME4)
|
.put(NetworkModule.HTTP_TYPE_KEY, Security.NAME4)
|
||||||
.put(XPackSettings.WATCHER_ENABLED.getKey(), watcherEnabled)
|
.put(XPackSettings.WATCHER_ENABLED.getKey(), watcherEnabled)
|
||||||
|
@ -467,7 +468,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase {
|
||||||
public static class SecuritySettings {
|
public static class SecuritySettings {
|
||||||
|
|
||||||
public static final String TEST_USERNAME = "test";
|
public static final String TEST_USERNAME = "test";
|
||||||
public static final String TEST_PASSWORD = "changeme";
|
public static final String TEST_PASSWORD = SecuritySettingsSource.TEST_PASSWORD;
|
||||||
private static final String TEST_PASSWORD_HASHED = new String(Hasher.BCRYPT.hash(new SecureString(TEST_PASSWORD.toCharArray())));
|
private static final String TEST_PASSWORD_HASHED = new String(Hasher.BCRYPT.hash(new SecureString(TEST_PASSWORD.toCharArray())));
|
||||||
|
|
||||||
static boolean auditLogsEnabled = SystemPropertyUtil.getBoolean("tests.audit_logs", true);
|
static boolean auditLogsEnabled = SystemPropertyUtil.getBoolean("tests.audit_logs", true);
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
package org.elasticsearch.xpack.notification.email;
|
package org.elasticsearch.xpack.notification.email;
|
||||||
|
|
||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
|
import org.elasticsearch.common.settings.MockSecureSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||||
import org.elasticsearch.xpack.notification.email.support.EmailServer;
|
import org.elasticsearch.xpack.notification.email.support.EmailServer;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
||||||
|
import org.elasticsearch.xpack.watcher.Watcher;
|
||||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||||
import org.elasticsearch.xpack.watcher.execution.ActionExecutionMode;
|
import org.elasticsearch.xpack.watcher.execution.ActionExecutionMode;
|
||||||
|
@ -43,6 +45,7 @@ public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTest
|
||||||
|
|
||||||
private EmailServer server;
|
private EmailServer server;
|
||||||
private Boolean encryptSensitiveData;
|
private Boolean encryptSensitiveData;
|
||||||
|
private byte[] encryptionKey;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
@ -58,15 +61,23 @@ public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTest
|
||||||
@Override
|
@Override
|
||||||
protected Settings nodeSettings(int nodeOrdinal) {
|
protected Settings nodeSettings(int nodeOrdinal) {
|
||||||
if (encryptSensitiveData == null) {
|
if (encryptSensitiveData == null) {
|
||||||
encryptSensitiveData = securityEnabled() && randomBoolean();
|
encryptSensitiveData = randomBoolean();
|
||||||
|
if (encryptSensitiveData) {
|
||||||
|
encryptionKey = CryptoService.generateKey();
|
||||||
}
|
}
|
||||||
return Settings.builder()
|
}
|
||||||
|
Settings.Builder builder = Settings.builder()
|
||||||
.put(super.nodeSettings(nodeOrdinal))
|
.put(super.nodeSettings(nodeOrdinal))
|
||||||
.put("xpack.notification.email.account.test.smtp.auth", true)
|
.put("xpack.notification.email.account.test.smtp.auth", true)
|
||||||
.put("xpack.notification.email.account.test.smtp.port", server.port())
|
.put("xpack.notification.email.account.test.smtp.port", server.port())
|
||||||
.put("xpack.notification.email.account.test.smtp.host", "localhost")
|
.put("xpack.notification.email.account.test.smtp.host", "localhost")
|
||||||
.put("xpack.watcher.encrypt_sensitive_data", encryptSensitiveData)
|
.put("xpack.watcher.encrypt_sensitive_data", encryptSensitiveData);
|
||||||
.build();
|
if (encryptSensitiveData) {
|
||||||
|
MockSecureSettings secureSettings = new MockSecureSettings();
|
||||||
|
secureSettings.setFile(Watcher.ENCRYPTION_KEY_SETTING.getKey(), encryptionKey);
|
||||||
|
builder.setSecureSettings(secureSettings);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmail() throws Exception {
|
public void testEmail() throws Exception {
|
||||||
|
@ -91,9 +102,12 @@ public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTest
|
||||||
Map<String, Object> source = response.getSource();
|
Map<String, Object> source = response.getSource();
|
||||||
Object value = XContentMapValues.extractValue("actions._email.email.password", source);
|
Object value = XContentMapValues.extractValue("actions._email.email.password", source);
|
||||||
assertThat(value, notNullValue());
|
assertThat(value, notNullValue());
|
||||||
if (securityEnabled() && encryptSensitiveData) {
|
if (encryptSensitiveData) {
|
||||||
assertThat(value, not(is(EmailServer.PASSWORD)));
|
assertThat(value, not(is(EmailServer.PASSWORD)));
|
||||||
CryptoService cryptoService = getInstanceFromMaster(CryptoService.class);
|
MockSecureSettings mockSecureSettings = new MockSecureSettings();
|
||||||
|
mockSecureSettings.setFile(Watcher.ENCRYPTION_KEY_SETTING.getKey(), encryptionKey);
|
||||||
|
Settings settings = Settings.builder().setSecureSettings(mockSecureSettings).build();
|
||||||
|
CryptoService cryptoService = new CryptoService(settings);
|
||||||
assertThat(new String(cryptoService.decrypt(((String) value).toCharArray())), is(EmailServer.PASSWORD));
|
assertThat(new String(cryptoService.decrypt(((String) value).toCharArray())), is(EmailServer.PASSWORD));
|
||||||
} else {
|
} else {
|
||||||
assertThat(value, is(EmailServer.PASSWORD));
|
assertThat(value, is(EmailServer.PASSWORD));
|
||||||
|
|
|
@ -43,8 +43,8 @@ public class SecurityPluginTests extends SecurityIntegTestCase {
|
||||||
logger.info("executing authorized request to /_xpack infos");
|
logger.info("executing authorized request to /_xpack infos");
|
||||||
Response response = getRestClient().performRequest("GET", "/_xpack",
|
Response response = getRestClient().performRequest("GET", "/_xpack",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
|
basicAuthHeaderValue(SecuritySettingsSource.TEST_USER_NAME,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray()))));
|
||||||
assertThat(response.getStatusLine().getStatusCode(), is(OK.getStatus()));
|
assertThat(response.getStatusLine().getStatusCode(), is(OK.getStatus()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,22 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security;
|
package org.elasticsearch.xpack.security;
|
||||||
|
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.nio.entity.NStringEntity;
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.elasticsearch.client.RestClientBuilder;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateObserver;
|
import org.elasticsearch.cluster.ClusterStateObserver;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.UUIDs;
|
import org.elasticsearch.common.UUIDs;
|
||||||
|
import org.elasticsearch.common.network.NetworkModule;
|
||||||
import org.elasticsearch.common.settings.MockSecureSettings;
|
import org.elasticsearch.common.settings.MockSecureSettings;
|
||||||
import org.elasticsearch.common.settings.SecureSettings;
|
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
@ -33,11 +40,14 @@ import org.elasticsearch.xpack.security.action.role.PutRoleResponse;
|
||||||
import org.elasticsearch.xpack.security.action.user.PutUserResponse;
|
import org.elasticsearch.xpack.security.action.user.PutUserResponse;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
|
import org.elasticsearch.xpack.security.user.ElasticUser;
|
||||||
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
|
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -76,7 +86,16 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
if (cluster2 == null) {
|
if (cluster2 == null) {
|
||||||
SecuritySettingsSource cluster2SettingsSource =
|
SecuritySettingsSource cluster2SettingsSource =
|
||||||
new SecuritySettingsSource(defaultMaxNumberOfNodes(), useGeneratedSSL, createTempDir(), Scope.SUITE);
|
new SecuritySettingsSource(defaultMaxNumberOfNodes(), useGeneratedSSL, createTempDir(), Scope.SUITE) {
|
||||||
|
@Override
|
||||||
|
public Settings nodeSettings(int nodeOrdinal) {
|
||||||
|
return Settings.builder()
|
||||||
|
.put(super.nodeSettings(nodeOrdinal))
|
||||||
|
.put(NetworkModule.HTTP_ENABLED.getKey(), true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
cluster2 = new InternalTestCluster(randomLong(), createTempDir(), true, true, 1, 2,
|
cluster2 = new InternalTestCluster(randomLong(), createTempDir(), true, true, 1, 2,
|
||||||
UUIDs.randomBase64UUID(random()), cluster2SettingsSource, 0, false, SECOND_CLUSTER_NODE_PREFIX, getMockPlugins(),
|
UUIDs.randomBase64UUID(random()), cluster2SettingsSource, 0, false, SECOND_CLUSTER_NODE_PREFIX, getMockPlugins(),
|
||||||
getClientWrapper());
|
getClientWrapper());
|
||||||
|
@ -85,6 +104,11 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSetReservedUserPasswords() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useGeneratedSSLConfig() {
|
public boolean useGeneratedSSLConfig() {
|
||||||
return useGeneratedSSL;
|
return useGeneratedSSL;
|
||||||
|
@ -138,8 +162,17 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
|
||||||
|
|
||||||
private void setupTribeNode(Settings settings) throws NodeValidationException, InterruptedException {
|
private void setupTribeNode(Settings settings) throws NodeValidationException, InterruptedException {
|
||||||
SecuritySettingsSource cluster2SettingsSource =
|
SecuritySettingsSource cluster2SettingsSource =
|
||||||
new SecuritySettingsSource(1, useGeneratedSSL, createTempDir(), Scope.TEST);
|
new SecuritySettingsSource(1, useGeneratedSSL, createTempDir(), Scope.TEST) {
|
||||||
Map<String,String> asMap = new HashMap<>(cluster2SettingsSource.nodeSettings(0).getAsMap());
|
@Override
|
||||||
|
public Settings nodeSettings(int nodeOrdinal) {
|
||||||
|
return Settings.builder()
|
||||||
|
.put(super.nodeSettings(nodeOrdinal))
|
||||||
|
.put(NetworkModule.HTTP_ENABLED.getKey(), true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, String> asMap = new HashMap<>(cluster2SettingsSource.nodeSettings(0).getAsMap());
|
||||||
asMap.remove(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey());
|
asMap.remove(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey());
|
||||||
Settings.Builder tribe1Defaults = Settings.builder();
|
Settings.Builder tribe1Defaults = Settings.builder();
|
||||||
Settings.Builder tribe2Defaults = Settings.builder();
|
Settings.Builder tribe2Defaults = Settings.builder();
|
||||||
|
@ -216,9 +249,25 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatTribeCanAuthenticateElasticUser() throws Exception {
|
public void testThatTribeCanAuthenticateElasticUser() throws Exception {
|
||||||
|
InetSocketAddress[] inetSocketAddresses = cluster2.httpAddresses();
|
||||||
|
List<HttpHost> hosts = new ArrayList<>();
|
||||||
|
for (InetSocketAddress address : inetSocketAddresses) {
|
||||||
|
hosts.add(new HttpHost(address.getAddress(), address.getPort()));
|
||||||
|
}
|
||||||
|
RestClientBuilder builder = RestClient.builder(hosts.toArray(new HttpHost[hosts.size()]));
|
||||||
|
RestClient client = builder.build();
|
||||||
|
|
||||||
|
String payload = "{\"password\": \"" + SecuritySettingsSource.TEST_PASSWORD + "\"}";
|
||||||
|
HttpEntity entity = new NStringEntity(payload, ContentType.APPLICATION_JSON);
|
||||||
|
BasicHeader authHeader = new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
|
UsernamePasswordToken.basicAuthHeaderValue(ElasticUser.NAME, new SecureString("".toCharArray())));
|
||||||
|
String route = "/_xpack/security/user/" + ElasticUser.NAME + "/_password";
|
||||||
|
client.performRequest("PUT", route, Collections.emptyMap(), entity, authHeader);
|
||||||
|
client.close();
|
||||||
|
|
||||||
setupTribeNode(Settings.EMPTY);
|
setupTribeNode(Settings.EMPTY);
|
||||||
ClusterHealthResponse response = tribeClient.filterWithHeader(Collections.singletonMap("Authorization",
|
ClusterHealthResponse response = tribeClient.filterWithHeader(Collections.singletonMap("Authorization",
|
||||||
UsernamePasswordToken.basicAuthHeaderValue("elastic", new SecureString("changeme".toCharArray()))))
|
UsernamePasswordToken.basicAuthHeaderValue("elastic", SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING)))
|
||||||
.admin().cluster().prepareHealth().get();
|
.admin().cluster().prepareHealth().get();
|
||||||
assertNoTimeout(response);
|
assertNoTimeout(response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.ActionFilters;
|
import org.elasticsearch.action.support.ActionFilters;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
||||||
|
@ -54,7 +54,7 @@ public class TransportChangePasswordActionTests extends ESTestCase {
|
||||||
|
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest();
|
ChangePasswordRequest request = new ChangePasswordRequest();
|
||||||
request.username(anonymousUser.principal());
|
request.username(anonymousUser.principal());
|
||||||
request.passwordHash(Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray())));
|
request.passwordHash(Hasher.BCRYPT.hash(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING));
|
||||||
|
|
||||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
||||||
final AtomicReference<ChangePasswordResponse> responseRef = new AtomicReference<>();
|
final AtomicReference<ChangePasswordResponse> responseRef = new AtomicReference<>();
|
||||||
|
@ -85,7 +85,7 @@ public class TransportChangePasswordActionTests extends ESTestCase {
|
||||||
|
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest();
|
ChangePasswordRequest request = new ChangePasswordRequest();
|
||||||
request.username(randomFrom(SystemUser.INSTANCE.principal(), XPackUser.INSTANCE.principal()));
|
request.username(randomFrom(SystemUser.INSTANCE.principal(), XPackUser.INSTANCE.principal()));
|
||||||
request.passwordHash(Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray())));
|
request.passwordHash(Hasher.BCRYPT.hash(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING));
|
||||||
|
|
||||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
||||||
final AtomicReference<ChangePasswordResponse> responseRef = new AtomicReference<>();
|
final AtomicReference<ChangePasswordResponse> responseRef = new AtomicReference<>();
|
||||||
|
@ -112,15 +112,13 @@ public class TransportChangePasswordActionTests extends ESTestCase {
|
||||||
NativeUsersStore usersStore = mock(NativeUsersStore.class);
|
NativeUsersStore usersStore = mock(NativeUsersStore.class);
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest();
|
ChangePasswordRequest request = new ChangePasswordRequest();
|
||||||
request.username(user.principal());
|
request.username(user.principal());
|
||||||
request.passwordHash(Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray())));
|
request.passwordHash(Hasher.BCRYPT.hash(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING));
|
||||||
doAnswer(new Answer() {
|
doAnswer(invocation -> {
|
||||||
public Void answer(InvocationOnMock invocation) {
|
|
||||||
Object[] args = invocation.getArguments();
|
Object[] args = invocation.getArguments();
|
||||||
assert args.length == 2;
|
assert args.length == 2;
|
||||||
ActionListener<Void> listener = (ActionListener<Void>) args[1];
|
ActionListener<Void> listener = (ActionListener<Void>) args[1];
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
}).when(usersStore).changePassword(eq(request), any(ActionListener.class));
|
}).when(usersStore).changePassword(eq(request), any(ActionListener.class));
|
||||||
TransportService transportService = new TransportService(Settings.EMPTY, null, null, TransportService.NOOP_TRANSPORT_INTERCEPTOR,
|
TransportService transportService = new TransportService(Settings.EMPTY, null, null, TransportService.NOOP_TRANSPORT_INTERCEPTOR,
|
||||||
x -> null, null);
|
x -> null, null);
|
||||||
|
@ -152,7 +150,7 @@ public class TransportChangePasswordActionTests extends ESTestCase {
|
||||||
NativeUsersStore usersStore = mock(NativeUsersStore.class);
|
NativeUsersStore usersStore = mock(NativeUsersStore.class);
|
||||||
ChangePasswordRequest request = new ChangePasswordRequest();
|
ChangePasswordRequest request = new ChangePasswordRequest();
|
||||||
request.username(user.principal());
|
request.username(user.principal());
|
||||||
request.passwordHash(Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray())));
|
request.passwordHash(Hasher.BCRYPT.hash(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING));
|
||||||
final Exception e = randomFrom(new ElasticsearchSecurityException(""), new IllegalStateException(), new RuntimeException());
|
final Exception e = randomFrom(new ElasticsearchSecurityException(""), new IllegalStateException(), new RuntimeException());
|
||||||
doAnswer(new Answer() {
|
doAnswer(new Answer() {
|
||||||
public Void answer(InvocationOnMock invocation) {
|
public Void answer(InvocationOnMock invocation) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealmTests;
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealmTests;
|
||||||
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
import org.elasticsearch.xpack.security.user.AnonymousUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.ElasticUser;
|
||||||
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.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
@ -180,9 +181,11 @@ public class TransportGetUsersActionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
User[] users = responseRef.get().users();
|
||||||
|
|
||||||
assertThat(throwableRef.get(), is(nullValue()));
|
assertThat(throwableRef.get(), is(nullValue()));
|
||||||
assertThat(responseRef.get(), is(notNullValue()));
|
assertThat(responseRef.get(), is(notNullValue()));
|
||||||
assertThat(responseRef.get().users(), arrayContaining(reservedUsers.toArray(new User[reservedUsers.size()])));
|
assertThat(users, arrayContaining(reservedUsers.toArray(new User[reservedUsers.size()])));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetAllUsers() {
|
public void testGetAllUsers() {
|
||||||
|
|
|
@ -11,10 +11,10 @@ import org.elasticsearch.action.support.ActionFilters;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.common.ValidationException;
|
import org.elasticsearch.common.ValidationException;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
|
||||||
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.env.Environment;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
||||||
|
@ -163,7 +163,7 @@ public class TransportPutUserActionTests extends ESTestCase {
|
||||||
final PutUserRequest request = new PutUserRequest();
|
final PutUserRequest request = new PutUserRequest();
|
||||||
request.username(user.principal());
|
request.username(user.principal());
|
||||||
if (isCreate) {
|
if (isCreate) {
|
||||||
request.passwordHash(Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray())));
|
request.passwordHash(Hasher.BCRYPT.hash(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING));
|
||||||
}
|
}
|
||||||
final boolean created = isCreate ? randomBoolean() : false; // updates should always return false for create
|
final boolean created = isCreate ? randomBoolean() : false; // updates should always return false for create
|
||||||
doAnswer(new Answer() {
|
doAnswer(new Answer() {
|
||||||
|
|
|
@ -15,8 +15,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.apache.http.message.BasicHeader;
|
import org.apache.http.message.BasicHeader;
|
||||||
import org.elasticsearch.action.ActionFuture;
|
import org.elasticsearch.action.ActionFuture;
|
||||||
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
|
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
|
||||||
import org.elasticsearch.action.admin.indices.recovery.RecoveryAction;
|
|
||||||
import org.elasticsearch.action.admin.indices.recovery.RecoveryRequestBuilder;
|
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
@ -24,7 +22,6 @@ import org.elasticsearch.client.Requests;
|
||||||
import org.elasticsearch.client.ResponseException;
|
import org.elasticsearch.client.ResponseException;
|
||||||
import org.elasticsearch.common.network.NetworkModule;
|
import org.elasticsearch.common.network.NetworkModule;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||||
|
@ -37,7 +34,7 @@ import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_PASSWORD_SECURE_STRING;
|
import static org.elasticsearch.test.SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING;
|
||||||
import static org.hamcrest.Matchers.arrayContaining;
|
import static org.hamcrest.Matchers.arrayContaining;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
@ -70,7 +67,7 @@ public class AuditTrailTests extends SecurityIntegTestCase {
|
||||||
@Override
|
@Override
|
||||||
public String configUsers() {
|
public String configUsers() {
|
||||||
return super.configUsers()
|
return super.configUsers()
|
||||||
+ AUTHENTICATE_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD_HASHED + "\n"
|
+ AUTHENTICATE_USER + ":" + SecuritySettingsSource.TEST_PASSWORD_HASHED + "\n"
|
||||||
+ EXECUTE_USER + ":xx_no_password_xx\n";
|
+ EXECUTE_USER + ":xx_no_password_xx\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +87,7 @@ public class AuditTrailTests extends SecurityIntegTestCase {
|
||||||
try {
|
try {
|
||||||
getRestClient().performRequest("GET", "/.security/_search",
|
getRestClient().performRequest("GET", "/.security/_search",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(AUTHENTICATE_USER, DEFAULT_PASSWORD_SECURE_STRING)),
|
UsernamePasswordToken.basicAuthHeaderValue(AUTHENTICATE_USER, TEST_PASSWORD_SECURE_STRING)),
|
||||||
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, EXECUTE_USER));
|
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, EXECUTE_USER));
|
||||||
fail("request should have failed");
|
fail("request should have failed");
|
||||||
} catch (ResponseException e) {
|
} catch (ResponseException e) {
|
||||||
|
@ -112,7 +109,7 @@ public class AuditTrailTests extends SecurityIntegTestCase {
|
||||||
try {
|
try {
|
||||||
getRestClient().performRequest("GET", "/.security/_search",
|
getRestClient().performRequest("GET", "/.security/_search",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(AUTHENTICATE_USER, DEFAULT_PASSWORD_SECURE_STRING)),
|
UsernamePasswordToken.basicAuthHeaderValue(AUTHENTICATE_USER, TEST_PASSWORD_SECURE_STRING)),
|
||||||
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, ""));
|
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, ""));
|
||||||
fail("request should have failed");
|
fail("request should have failed");
|
||||||
} catch (ResponseException e) {
|
} catch (ResponseException e) {
|
||||||
|
|
|
@ -183,8 +183,8 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
|
||||||
Settings.Builder builder = Settings.builder()
|
Settings.Builder builder = Settings.builder()
|
||||||
.put("xpack.security.audit.index.client." + XPackSettings.SECURITY_ENABLED.getKey(), useSecurity)
|
.put("xpack.security.audit.index.client." + XPackSettings.SECURITY_ENABLED.getKey(), useSecurity)
|
||||||
.put(remoteSettings(NetworkAddress.format(inet.address().getAddress()), inet.address().getPort(), cluster2Name))
|
.put(remoteSettings(NetworkAddress.format(inet.address().getAddress()), inet.address().getPort(), cluster2Name))
|
||||||
.put("xpack.security.audit.index.client.xpack.security.user", SecuritySettingsSource.DEFAULT_USER_NAME + ":" +
|
.put("xpack.security.audit.index.client.xpack.security.user", SecuritySettingsSource.TEST_USER_NAME + ":" +
|
||||||
SecuritySettingsSource.DEFAULT_PASSWORD);
|
SecuritySettingsSource.TEST_PASSWORD);
|
||||||
|
|
||||||
if (useGeneratedSSL == false) {
|
if (useGeneratedSSL == false) {
|
||||||
cluster2SettingsSource.addClientSSLSettings(builder, "xpack.security.audit.index.client.");
|
cluster2SettingsSource.addClientSSLSettings(builder, "xpack.security.audit.index.client.");
|
||||||
|
|
|
@ -101,7 +101,7 @@ public class RemoteIndexAuditTrailStartingTests extends SecurityIntegTestCase {
|
||||||
.put("xpack.security.audit.outputs", randomFrom("index", "index,logfile"))
|
.put("xpack.security.audit.outputs", randomFrom("index", "index,logfile"))
|
||||||
.putArray("xpack.security.audit.index.client.hosts", addresses.toArray(new String[addresses.size()]))
|
.putArray("xpack.security.audit.index.client.hosts", addresses.toArray(new String[addresses.size()]))
|
||||||
.put("xpack.security.audit.index.client.cluster.name", clusterName)
|
.put("xpack.security.audit.index.client.cluster.name", clusterName)
|
||||||
.put("xpack.security.audit.index.client.xpack.security.user", DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD);
|
.put("xpack.security.audit.index.client.xpack.security.user", TEST_USER_NAME + ":" + TEST_PASSWORD);
|
||||||
|
|
||||||
addClientSSLSettings(builder, "xpack.security.audit.index.client.");
|
addClientSSLSettings(builder, "xpack.security.audit.index.client.");
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
|
|
@ -5,16 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.authc;
|
package org.elasticsearch.xpack.security.authc;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.Clock;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import org.apache.lucene.util.SetOnce;
|
import org.apache.lucene.util.SetOnce;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
|
@ -24,11 +14,14 @@ import org.elasticsearch.action.get.GetRequest;
|
||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
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.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
|
@ -53,6 +46,18 @@ import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
||||||
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
||||||
import static org.hamcrest.Matchers.arrayContaining;
|
import static org.hamcrest.Matchers.arrayContaining;
|
||||||
|
@ -93,12 +98,16 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
private TokenService tokenService;
|
private TokenService tokenService;
|
||||||
private SecurityLifecycleService lifecycleService;
|
private SecurityLifecycleService lifecycleService;
|
||||||
private Client client;
|
private Client client;
|
||||||
|
private InetSocketAddress remoteAddress;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@SuppressForbidden(reason = "Allow accessing localhost")
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
token = mock(AuthenticationToken.class);
|
token = mock(AuthenticationToken.class);
|
||||||
message = new InternalMessage();
|
message = new InternalMessage();
|
||||||
restRequest = new FakeRestRequest();
|
remoteAddress = new InetSocketAddress(InetAddress.getLocalHost(), 100);
|
||||||
|
message.remoteAddress(new TransportAddress(remoteAddress));
|
||||||
|
restRequest = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY).withRemoteAddress(remoteAddress).build();
|
||||||
threadContext = new ThreadContext(Settings.EMPTY);
|
threadContext = new ThreadContext(Settings.EMPTY);
|
||||||
|
|
||||||
firstRealm = mock(Realm.class);
|
firstRealm = mock(Realm.class);
|
||||||
|
@ -206,7 +215,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
}, this::logAndFail));
|
}, this::logAndFail));
|
||||||
verify(auditTrail).authenticationSuccess(secondRealm.name(), user, "_action", message);
|
verify(auditTrail).authenticationSuccess(secondRealm.name(), user, "_action", message);
|
||||||
verifyNoMoreInteractions(auditTrail);
|
verifyNoMoreInteractions(auditTrail);
|
||||||
verify(firstRealm, never()).authenticate(eq(token), any(ActionListener.class));
|
verify(firstRealm, never()).authenticate(eq(token), any(ActionListener.class), any(IncomingRequest.class));
|
||||||
assertTrue(completed.get());
|
assertTrue(completed.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +571,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
when(secondRealm.token(threadContext)).thenReturn(token);
|
when(secondRealm.token(threadContext)).thenReturn(token);
|
||||||
when(secondRealm.supports(token)).thenReturn(true);
|
when(secondRealm.supports(token)).thenReturn(true);
|
||||||
doThrow(authenticationError("realm doesn't like authenticate"))
|
doThrow(authenticationError("realm doesn't like authenticate"))
|
||||||
.when(secondRealm).authenticate(eq(token), any(ActionListener.class));
|
.when(secondRealm).authenticate(eq(token), any(ActionListener.class), any(IncomingRequest.class));
|
||||||
try {
|
try {
|
||||||
authenticateBlocking("_action", message, null);
|
authenticateBlocking("_action", message, null);
|
||||||
fail("exception should bubble out");
|
fail("exception should bubble out");
|
||||||
|
@ -577,7 +586,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
when(secondRealm.token(threadContext)).thenReturn(token);
|
when(secondRealm.token(threadContext)).thenReturn(token);
|
||||||
when(secondRealm.supports(token)).thenReturn(true);
|
when(secondRealm.supports(token)).thenReturn(true);
|
||||||
doThrow(authenticationError("realm doesn't like authenticate"))
|
doThrow(authenticationError("realm doesn't like authenticate"))
|
||||||
.when(secondRealm).authenticate(eq(token), any(ActionListener.class));
|
.when(secondRealm).authenticate(eq(token), any(ActionListener.class), any(IncomingRequest.class));
|
||||||
try {
|
try {
|
||||||
authenticateBlocking(restRequest);
|
authenticateBlocking(restRequest);
|
||||||
fail("exception should bubble out");
|
fail("exception should bubble out");
|
||||||
|
@ -869,7 +878,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
ActionListener listener = (ActionListener) i.getArguments()[1];
|
ActionListener listener = (ActionListener) i.getArguments()[1];
|
||||||
listener.onResponse(user);
|
listener.onResponse(user);
|
||||||
return null;
|
return null;
|
||||||
}).when(realm).authenticate(eq(token), any(ActionListener.class));
|
}).when(realm).authenticate(eq(token), any(ActionListener.class), any(IncomingRequest.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Authentication authenticateBlocking(RestRequest restRequest) {
|
private Authentication authenticateBlocking(RestRequest restRequest) {
|
||||||
|
|
|
@ -436,7 +436,7 @@ public class RealmsTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticationToken token, ActionListener<User> listener) {
|
public void authenticate(AuthenticationToken token, ActionListener<User> listener, IncomingRequest incomingRequest) {
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_PASSWORD_SECURE_STRING;
|
import static org.elasticsearch.test.SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
@ -40,7 +40,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
private static final String TRANSPORT_CLIENT_USER = "transport_user";
|
private static final String TRANSPORT_CLIENT_USER = "transport_user";
|
||||||
private static final String ROLES =
|
private static final String ROLES =
|
||||||
"run_as_role:\n" +
|
"run_as_role:\n" +
|
||||||
" run_as: [ '" + SecuritySettingsSource.DEFAULT_USER_NAME + "', 'idontexist' ]\n";
|
" run_as: [ '" + SecuritySettingsSource.TEST_USER_NAME + "', 'idontexist' ]\n";
|
||||||
|
|
||||||
// indicates whether the RUN_AS_USER that is being authenticated is also a superuser
|
// indicates whether the RUN_AS_USER that is being authenticated is also a superuser
|
||||||
private static boolean runAsHasSuperUserRole;
|
private static boolean runAsHasSuperUserRole;
|
||||||
|
@ -66,8 +66,8 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
@Override
|
@Override
|
||||||
public String configUsers() {
|
public String configUsers() {
|
||||||
return super.configUsers()
|
return super.configUsers()
|
||||||
+ RUN_AS_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD_HASHED + "\n"
|
+ RUN_AS_USER + ":" + SecuritySettingsSource.TEST_PASSWORD_HASHED + "\n"
|
||||||
+ TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD_HASHED + "\n";
|
+ TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.TEST_PASSWORD_HASHED + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,7 +89,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
public void testUserImpersonation() throws Exception {
|
public void testUserImpersonation() throws Exception {
|
||||||
try (TransportClient client = getTransportClient(Settings.builder()
|
try (TransportClient client = getTransportClient(Settings.builder()
|
||||||
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD).build())) {
|
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.TEST_PASSWORD).build())) {
|
||||||
//ensure the client can connect
|
//ensure the client can connect
|
||||||
assertBusy(() -> assertThat(client.connectedNodes().size(), greaterThan(0)));
|
assertBusy(() -> assertThat(client.connectedNodes().size(), greaterThan(0)));
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
// let's run as without authorization
|
// let's run as without authorization
|
||||||
try {
|
try {
|
||||||
Map<String, String> headers = Collections.singletonMap(AuthenticationService.RUN_AS_USER_HEADER,
|
Map<String, String> headers = Collections.singletonMap(AuthenticationService.RUN_AS_USER_HEADER,
|
||||||
SecuritySettingsSource.DEFAULT_USER_NAME);
|
SecuritySettingsSource.TEST_USER_NAME);
|
||||||
client.filterWithHeader(headers)
|
client.filterWithHeader(headers)
|
||||||
.admin().cluster().prepareHealth().get();
|
.admin().cluster().prepareHealth().get();
|
||||||
fail("run as should be unauthorized for the transport client user");
|
fail("run as should be unauthorized for the transport client user");
|
||||||
|
@ -115,8 +115,8 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray())));
|
||||||
headers.put(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME);
|
headers.put(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.TEST_USER_NAME);
|
||||||
// lets set the user
|
// lets set the user
|
||||||
ClusterHealthResponse response = client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
|
ClusterHealthResponse response = client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
|
||||||
assertThat(response.isTimedOut(), is(false));
|
assertThat(response.isTimedOut(), is(false));
|
||||||
|
@ -129,8 +129,8 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
getRestClient().performRequest("GET", "/_nodes",
|
getRestClient().performRequest("GET", "/_nodes",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(TRANSPORT_CLIENT_USER,
|
UsernamePasswordToken.basicAuthHeaderValue(TRANSPORT_CLIENT_USER,
|
||||||
DEFAULT_PASSWORD_SECURE_STRING)),
|
TEST_PASSWORD_SECURE_STRING)),
|
||||||
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME));
|
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.TEST_USER_NAME));
|
||||||
fail("request should have failed");
|
fail("request should have failed");
|
||||||
} catch(ResponseException e) {
|
} catch(ResponseException e) {
|
||||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||||
|
@ -142,7 +142,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
getRestClient().performRequest("GET", "/_nodes",
|
getRestClient().performRequest("GET", "/_nodes",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
DEFAULT_PASSWORD_SECURE_STRING)));
|
TEST_PASSWORD_SECURE_STRING)));
|
||||||
fail("request should have failed");
|
fail("request should have failed");
|
||||||
} catch (ResponseException e) {
|
} catch (ResponseException e) {
|
||||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||||
|
@ -153,14 +153,14 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
Response response = getRestClient().performRequest("GET", "/_nodes",
|
Response response = getRestClient().performRequest("GET", "/_nodes",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
DEFAULT_PASSWORD_SECURE_STRING)),
|
TEST_PASSWORD_SECURE_STRING)),
|
||||||
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME));
|
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.TEST_USER_NAME));
|
||||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmptyUserImpersonationHeader() throws Exception {
|
public void testEmptyUserImpersonationHeader() throws Exception {
|
||||||
try (TransportClient client = getTransportClient(Settings.builder()
|
try (TransportClient client = getTransportClient(Settings.builder()
|
||||||
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD).build())) {
|
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.TEST_PASSWORD).build())) {
|
||||||
//ensure the client can connect
|
//ensure the client can connect
|
||||||
awaitBusy(() -> {
|
awaitBusy(() -> {
|
||||||
return client.connectedNodes().size() > 0;
|
return client.connectedNodes().size() > 0;
|
||||||
|
@ -169,7 +169,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
try {
|
try {
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray())));
|
||||||
headers.put(AuthenticationService.RUN_AS_USER_HEADER, "");
|
headers.put(AuthenticationService.RUN_AS_USER_HEADER, "");
|
||||||
|
|
||||||
client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
|
client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
|
||||||
|
@ -185,7 +185,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
getRestClient().performRequest("GET", "/_nodes",
|
getRestClient().performRequest("GET", "/_nodes",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
DEFAULT_PASSWORD_SECURE_STRING)),
|
TEST_PASSWORD_SECURE_STRING)),
|
||||||
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, ""));
|
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, ""));
|
||||||
fail("request should have failed");
|
fail("request should have failed");
|
||||||
} catch(ResponseException e) {
|
} catch(ResponseException e) {
|
||||||
|
@ -195,7 +195,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
public void testNonExistentRunAsUser() throws Exception {
|
public void testNonExistentRunAsUser() throws Exception {
|
||||||
try (TransportClient client = getTransportClient(Settings.builder()
|
try (TransportClient client = getTransportClient(Settings.builder()
|
||||||
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.DEFAULT_PASSWORD).build())) {
|
.put(Security.USER_SETTING.getKey(), TRANSPORT_CLIENT_USER + ":" + SecuritySettingsSource.TEST_PASSWORD).build())) {
|
||||||
//ensure the client can connect
|
//ensure the client can connect
|
||||||
awaitBusy(() -> {
|
awaitBusy(() -> {
|
||||||
return client.connectedNodes().size() > 0;
|
return client.connectedNodes().size() > 0;
|
||||||
|
@ -204,7 +204,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
try {
|
try {
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
|
new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray())));
|
||||||
headers.put(AuthenticationService.RUN_AS_USER_HEADER, "idontexist");
|
headers.put(AuthenticationService.RUN_AS_USER_HEADER, "idontexist");
|
||||||
|
|
||||||
client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
|
client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
|
||||||
|
@ -220,7 +220,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
||||||
getRestClient().performRequest("GET", "/_nodes",
|
getRestClient().performRequest("GET", "/_nodes",
|
||||||
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
|
||||||
DEFAULT_PASSWORD_SECURE_STRING)),
|
TEST_PASSWORD_SECURE_STRING)),
|
||||||
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, "idontexist"));
|
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, "idontexist"));
|
||||||
fail("request should have failed");
|
fail("request should have failed");
|
||||||
} catch (ResponseException e) {
|
} catch (ResponseException e) {
|
||||||
|
|
|
@ -50,8 +50,8 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
|
||||||
SecurityClient securityClient = new SecurityClient(client);
|
SecurityClient securityClient = new SecurityClient(client);
|
||||||
CreateTokenResponse response = securityClient.prepareCreateToken()
|
CreateTokenResponse response = securityClient.prepareCreateToken()
|
||||||
.setGrantType("password")
|
.setGrantType("password")
|
||||||
.setUsername(SecuritySettingsSource.DEFAULT_USER_NAME)
|
.setUsername(SecuritySettingsSource.TEST_USER_NAME)
|
||||||
.setPassword(new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))
|
.setPassword(new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray()))
|
||||||
.get();
|
.get();
|
||||||
Instant created = Instant.now();
|
Instant created = Instant.now();
|
||||||
|
|
||||||
|
@ -90,8 +90,8 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
|
||||||
public void testExpireMultipleTimes() throws Exception {
|
public void testExpireMultipleTimes() throws Exception {
|
||||||
CreateTokenResponse response = securityClient().prepareCreateToken()
|
CreateTokenResponse response = securityClient().prepareCreateToken()
|
||||||
.setGrantType("password")
|
.setGrantType("password")
|
||||||
.setUsername(SecuritySettingsSource.DEFAULT_USER_NAME)
|
.setUsername(SecuritySettingsSource.TEST_USER_NAME)
|
||||||
.setPassword(new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))
|
.setPassword(new SecureString(SecuritySettingsSource.TEST_PASSWORD.toCharArray()))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
InvalidateTokenResponse invalidateResponse = securityClient().prepareInvalidateToken(response.getTokenString()).get();
|
InvalidateTokenResponse invalidateResponse = securityClient().prepareInvalidateToken(response.getTokenString()).get();
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CharArrays;
|
import org.elasticsearch.xpack.security.authc.support.CharArrays;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -39,6 +40,11 @@ public class ESNativeMigrateToolTests extends NativeRealmIntegTestCase {
|
||||||
useSSL = randomBoolean();
|
useSSL = randomBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSetReservedUserPasswords() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Settings nodeSettings(int nodeOrdinal) {
|
public Settings nodeSettings(int nodeOrdinal) {
|
||||||
logger.info("--> use SSL? {}", useSSL);
|
logger.info("--> use SSL? {}", useSSL);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.cli.Terminal.Verbosity;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -104,9 +105,12 @@ public class ESNativeRealmMigrateToolTests extends CommandTestCase {
|
||||||
Files.createDirectories(xpackConfDir);
|
Files.createDirectories(xpackConfDir);
|
||||||
|
|
||||||
ESNativeRealmMigrateTool.MigrateUserOrRoles muor = new ESNativeRealmMigrateTool.MigrateUserOrRoles();
|
ESNativeRealmMigrateTool.MigrateUserOrRoles muor = new ESNativeRealmMigrateTool.MigrateUserOrRoles();
|
||||||
OptionSet options = muor.getParser().parse("-u", "elastic", "-p", "changeme", "-U", "http://localhost:9200");
|
|
||||||
|
OptionSet options = muor.getParser().parse("-u", "elastic", "-p", SecuritySettingsSource.TEST_PASSWORD,
|
||||||
|
"-U", "http://localhost:9200");
|
||||||
Settings settings = Settings.builder().put("path.home", homeDir).build();
|
Settings settings = Settings.builder().put("path.home", homeDir).build();
|
||||||
Environment environment = new Environment(settings, confDir);
|
Environment environment = new Environment(settings, confDir);
|
||||||
|
|
||||||
MockTerminal mockTerminal = new MockTerminal();
|
MockTerminal mockTerminal = new MockTerminal();
|
||||||
|
|
||||||
FileNotFoundException fnfe = expectThrows(FileNotFoundException.class,
|
FileNotFoundException fnfe = expectThrows(FileNotFoundException.class,
|
||||||
|
|
|
@ -252,13 +252,13 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
public void testUpdatingUserAndAuthentication() throws Exception {
|
public void testUpdatingUserAndAuthentication() throws Exception {
|
||||||
SecurityClient c = securityClient();
|
SecurityClient c = securityClient();
|
||||||
logger.error("--> creating user");
|
logger.error("--> creating user");
|
||||||
c.preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
c.preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
logger.error("--> waiting for .security index");
|
logger.error("--> waiting for .security index");
|
||||||
ensureGreen(SecurityLifecycleService.SECURITY_INDEX_NAME);
|
ensureGreen(SecurityLifecycleService.SECURITY_INDEX_NAME);
|
||||||
logger.info("--> retrieving user");
|
logger.info("--> retrieving user");
|
||||||
GetUsersResponse resp = c.prepareGetUsers("joe").get();
|
GetUsersResponse resp = c.prepareGetUsers("joe").get();
|
||||||
assertTrue("user should exist", resp.hasUsers());
|
assertTrue("user should exist", resp.hasUsers());
|
||||||
assertThat(resp.users()[0].roles(), arrayContaining(SecuritySettingsSource.DEFAULT_ROLE));
|
assertThat(resp.users()[0].roles(), arrayContaining(SecuritySettingsSource.TEST_ROLE));
|
||||||
|
|
||||||
createIndex("idx");
|
createIndex("idx");
|
||||||
ensureGreen("idx");
|
ensureGreen("idx");
|
||||||
|
@ -269,7 +269,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
|
|
||||||
assertEquals(searchResp.getHits().getTotalHits(), 1L);
|
assertEquals(searchResp.getHits().getTotalHits(), 1L);
|
||||||
|
|
||||||
c.preparePutUser("joe", "s3krit2".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
c.preparePutUser("joe", "s3krit2".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
client().filterWithHeader(Collections.singletonMap("Authorization", token)).prepareSearch("idx").get();
|
client().filterWithHeader(Collections.singletonMap("Authorization", token)).prepareSearch("idx").get();
|
||||||
|
@ -287,13 +287,13 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
public void testCreateDeleteAuthenticate() {
|
public void testCreateDeleteAuthenticate() {
|
||||||
SecurityClient c = securityClient();
|
SecurityClient c = securityClient();
|
||||||
logger.error("--> creating user");
|
logger.error("--> creating user");
|
||||||
c.preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
c.preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
logger.error("--> waiting for .security index");
|
logger.error("--> waiting for .security index");
|
||||||
ensureGreen(SecurityLifecycleService.SECURITY_INDEX_NAME);
|
ensureGreen(SecurityLifecycleService.SECURITY_INDEX_NAME);
|
||||||
logger.info("--> retrieving user");
|
logger.info("--> retrieving user");
|
||||||
GetUsersResponse resp = c.prepareGetUsers("joe").get();
|
GetUsersResponse resp = c.prepareGetUsers("joe").get();
|
||||||
assertTrue("user should exist", resp.hasUsers());
|
assertTrue("user should exist", resp.hasUsers());
|
||||||
assertThat(resp.users()[0].roles(), arrayContaining(SecuritySettingsSource.DEFAULT_ROLE));
|
assertThat(resp.users()[0].roles(), arrayContaining(SecuritySettingsSource.TEST_ROLE));
|
||||||
|
|
||||||
createIndex("idx");
|
createIndex("idx");
|
||||||
ensureGreen("idx");
|
ensureGreen("idx");
|
||||||
|
@ -417,9 +417,9 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
assertThat(client.prepareGetUsers("joes").get().hasUsers(), is(false));
|
assertThat(client.prepareGetUsers("joes").get().hasUsers(), is(false));
|
||||||
|
|
||||||
// create joe with a password and verify the user works
|
// create joe with a password and verify the user works
|
||||||
client.preparePutUser("joe", "changeme".toCharArray(), "admin_role").get();
|
client.preparePutUser("joe", SecuritySettingsSource.TEST_PASSWORD.toCharArray(), "admin_role").get();
|
||||||
assertThat(client.prepareGetUsers("joe").get().hasUsers(), is(true));
|
assertThat(client.prepareGetUsers("joe").get().hasUsers(), is(true));
|
||||||
final String token = basicAuthHeaderValue("joe", new SecureString("changeme".toCharArray()));
|
final String token = basicAuthHeaderValue("joe", SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING);
|
||||||
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster()
|
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster()
|
||||||
.prepareHealth().get();
|
.prepareHealth().get();
|
||||||
assertFalse(response.isTimedOut());
|
assertFalse(response.isTimedOut());
|
||||||
|
@ -445,7 +445,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the user with password and admin role again
|
// update the user with password and admin role again
|
||||||
client.preparePutUser("joe", "changeme2".toCharArray(), "admin_role").fullName("Joe Smith").get();
|
String secondPassword = SecuritySettingsSource.TEST_PASSWORD + "2";
|
||||||
|
client.preparePutUser("joe", secondPassword.toCharArray(), "admin_role").fullName("Joe Smith").get();
|
||||||
getUsersResponse = client.prepareGetUsers("joe").get();
|
getUsersResponse = client.prepareGetUsers("joe").get();
|
||||||
assertThat(getUsersResponse.hasUsers(), is(true));
|
assertThat(getUsersResponse.hasUsers(), is(true));
|
||||||
assertThat(getUsersResponse.users().length, is(1));
|
assertThat(getUsersResponse.users().length, is(1));
|
||||||
|
@ -465,7 +466,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
response = client()
|
response = client()
|
||||||
.filterWithHeader(
|
.filterWithHeader(
|
||||||
Collections.singletonMap("Authorization",
|
Collections.singletonMap("Authorization",
|
||||||
basicAuthHeaderValue("joe", new SecureString("changeme2".toCharArray()))))
|
basicAuthHeaderValue("joe", new SecureString(secondPassword.toCharArray()))))
|
||||||
.admin().cluster().prepareHealth().get();
|
.admin().cluster().prepareHealth().get();
|
||||||
assertFalse(response.isTimedOut());
|
assertFalse(response.isTimedOut());
|
||||||
}
|
}
|
||||||
|
@ -493,7 +494,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
|
|
||||||
SecurityClient client = securityClient();
|
SecurityClient client = securityClient();
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
client.preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
client.preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
} else {
|
} else {
|
||||||
client.preparePutRole("read_role")
|
client.preparePutRole("read_role")
|
||||||
.cluster("none")
|
.cluster("none")
|
||||||
|
@ -512,7 +513,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
public void testOperationsOnReservedUsers() throws Exception {
|
public void testOperationsOnReservedUsers() throws Exception {
|
||||||
final String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
|
final String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
|
||||||
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
|
||||||
() -> securityClient().preparePutUser(username, randomBoolean() ? "changeme".toCharArray() : null, "admin").get());
|
() -> securityClient().preparePutUser(username, randomBoolean() ? SecuritySettingsSource.TEST_PASSWORD.toCharArray()
|
||||||
|
: null, "admin").get());
|
||||||
assertThat(exception.getMessage(), containsString("Username [" + username + "] is reserved"));
|
assertThat(exception.getMessage(), containsString("Username [" + username + "] is reserved"));
|
||||||
|
|
||||||
exception = expectThrows(IllegalArgumentException.class,
|
exception = expectThrows(IllegalArgumentException.class,
|
||||||
|
@ -551,7 +553,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
// authenticate should work
|
// authenticate should work
|
||||||
AuthenticateResponse authenticateResponse = client()
|
AuthenticateResponse authenticateResponse = client()
|
||||||
.filterWithHeader(Collections.singletonMap("Authorization",
|
.filterWithHeader(Collections.singletonMap("Authorization",
|
||||||
basicAuthHeaderValue(username, new SecureString("changeme".toCharArray()))))
|
basicAuthHeaderValue(username, getReservedPassword())))
|
||||||
.execute(AuthenticateAction.INSTANCE, new AuthenticateRequest(username))
|
.execute(AuthenticateAction.INSTANCE, new AuthenticateRequest(username))
|
||||||
.get();
|
.get();
|
||||||
assertThat(authenticateResponse.user().principal(), is(username));
|
assertThat(authenticateResponse.user().principal(), is(username));
|
||||||
|
@ -574,7 +576,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreateAndChangePassword() throws Exception {
|
public void testCreateAndChangePassword() throws Exception {
|
||||||
securityClient().preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
securityClient().preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
final String token = basicAuthHeaderValue("joe", new SecureString("s3krit".toCharArray()));
|
final String token = basicAuthHeaderValue("joe", new SecureString("s3krit".toCharArray()));
|
||||||
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token))
|
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token))
|
||||||
.admin().cluster().prepareHealth().get();
|
.admin().cluster().prepareHealth().get();
|
||||||
|
@ -582,7 +584,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
|
|
||||||
ChangePasswordResponse passwordResponse = securityClient(
|
ChangePasswordResponse passwordResponse = securityClient(
|
||||||
client().filterWithHeader(Collections.singletonMap("Authorization", token)))
|
client().filterWithHeader(Collections.singletonMap("Authorization", token)))
|
||||||
.prepareChangePassword("joe", "changeme".toCharArray())
|
.prepareChangePassword("joe", SecuritySettingsSource.TEST_PASSWORD.toCharArray())
|
||||||
.get();
|
.get();
|
||||||
assertThat(passwordResponse, notNullValue());
|
assertThat(passwordResponse, notNullValue());
|
||||||
|
|
||||||
|
@ -594,7 +596,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
response = client()
|
response = client()
|
||||||
.filterWithHeader(
|
.filterWithHeader(
|
||||||
Collections.singletonMap("Authorization",
|
Collections.singletonMap("Authorization",
|
||||||
basicAuthHeaderValue("joe", new SecureString("changeme".toCharArray()))))
|
basicAuthHeaderValue("joe", SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING)))
|
||||||
.admin().cluster().prepareHealth().get();
|
.admin().cluster().prepareHealth().get();
|
||||||
assertThat(response.isTimedOut(), is(false));
|
assertThat(response.isTimedOut(), is(false));
|
||||||
}
|
}
|
||||||
|
@ -660,7 +662,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSetEnabled() throws Exception {
|
public void testSetEnabled() throws Exception {
|
||||||
securityClient().preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
securityClient().preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
final String token = basicAuthHeaderValue("joe", new SecureString("s3krit".toCharArray()));
|
final String token = basicAuthHeaderValue("joe", new SecureString("s3krit".toCharArray()));
|
||||||
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token))
|
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token))
|
||||||
.admin().cluster().prepareHealth().get();
|
.admin().cluster().prepareHealth().get();
|
||||||
|
@ -720,7 +722,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
* the loader returned a null value, while the other caller(s) would get a null value unexpectedly
|
* the loader returned a null value, while the other caller(s) would get a null value unexpectedly
|
||||||
*/
|
*/
|
||||||
public void testConcurrentRunAs() throws Exception {
|
public void testConcurrentRunAs() throws Exception {
|
||||||
securityClient().preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.DEFAULT_ROLE).get();
|
securityClient().preparePutUser("joe", "s3krit".toCharArray(), SecuritySettingsSource.TEST_ROLE).get();
|
||||||
securityClient().preparePutUser("executor", "s3krit".toCharArray(), "superuser").get();
|
securityClient().preparePutUser("executor", "s3krit".toCharArray(), "superuser").get();
|
||||||
final String token = basicAuthHeaderValue("executor", new SecureString("s3krit".toCharArray()));
|
final String token = basicAuthHeaderValue("executor", new SecureString("s3krit".toCharArray()));
|
||||||
final Client client = client().filterWithHeader(MapBuilder.<String, String>newMapBuilder()
|
final Client client = client().filterWithHeader(MapBuilder.<String, String>newMapBuilder()
|
||||||
|
|
|
@ -168,7 +168,7 @@ public class NativeRealmMigratorTests extends ESTestCase {
|
||||||
this.reservedUsers = Collections.singletonMap(
|
this.reservedUsers = Collections.singletonMap(
|
||||||
KibanaUser.NAME,
|
KibanaUser.NAME,
|
||||||
MapBuilder.<String, Object>newMapBuilder()
|
MapBuilder.<String, Object>newMapBuilder()
|
||||||
.put(User.Fields.PASSWORD.getPreferredName(), new String(Hasher.BCRYPT.hash(ReservedRealm.DEFAULT_PASSWORD_TEXT)))
|
.put(User.Fields.PASSWORD.getPreferredName(), new String(Hasher.BCRYPT.hash(ReservedRealm.EMPTY_PASSWORD_TEXT)))
|
||||||
.put(User.Fields.ENABLED.getPreferredName(), false)
|
.put(User.Fields.ENABLED.getPreferredName(), false)
|
||||||
.immutableMap()
|
.immutableMap()
|
||||||
);
|
);
|
||||||
|
@ -181,7 +181,7 @@ public class NativeRealmMigratorTests extends ESTestCase {
|
||||||
this.reservedUsers = Collections.singletonMap(
|
this.reservedUsers = Collections.singletonMap(
|
||||||
KibanaUser.NAME,
|
KibanaUser.NAME,
|
||||||
MapBuilder.<String, Object>newMapBuilder()
|
MapBuilder.<String, Object>newMapBuilder()
|
||||||
.put(User.Fields.PASSWORD.getPreferredName(), new String(Hasher.BCRYPT.hash(ReservedRealm.DEFAULT_PASSWORD_TEXT)))
|
.put(User.Fields.PASSWORD.getPreferredName(), new String(Hasher.BCRYPT.hash(ReservedRealm.EMPTY_PASSWORD_TEXT)))
|
||||||
.put(User.Fields.ENABLED.getPreferredName(), false)
|
.put(User.Fields.ENABLED.getPreferredName(), false)
|
||||||
.immutableMap()
|
.immutableMap()
|
||||||
);
|
);
|
||||||
|
@ -195,7 +195,7 @@ public class NativeRealmMigratorTests extends ESTestCase {
|
||||||
.stream().collect(Collectors.toMap(Function.identity(),
|
.stream().collect(Collectors.toMap(Function.identity(),
|
||||||
name -> MapBuilder.<String, Object>newMapBuilder()
|
name -> MapBuilder.<String, Object>newMapBuilder()
|
||||||
.put(User.Fields.PASSWORD.getPreferredName(),
|
.put(User.Fields.PASSWORD.getPreferredName(),
|
||||||
new String(Hasher.BCRYPT.hash(ReservedRealm.DEFAULT_PASSWORD_TEXT)))
|
new String(Hasher.BCRYPT.hash(ReservedRealm.EMPTY_PASSWORD_TEXT)))
|
||||||
.put(User.Fields.ENABLED.getPreferredName(), randomBoolean())
|
.put(User.Fields.ENABLED.getPreferredName(), randomBoolean())
|
||||||
.immutableMap()
|
.immutableMap()
|
||||||
));
|
));
|
||||||
|
|
|
@ -111,9 +111,9 @@ public class NativeUsersStoreTests extends ESTestCase {
|
||||||
actionRespond(GetRequest.class, new GetResponse(result));
|
actionRespond(GetRequest.class, new GetResponse(result));
|
||||||
|
|
||||||
final NativeUsersStore.ReservedUserInfo userInfo = future.get();
|
final NativeUsersStore.ReservedUserInfo userInfo = future.get();
|
||||||
assertThat(userInfo.hasDefaultPassword, equalTo(true));
|
assertThat(userInfo.hasEmptyPassword, equalTo(true));
|
||||||
assertThat(userInfo.enabled, equalTo(true));
|
assertThat(userInfo.enabled, equalTo(true));
|
||||||
assertThat(userInfo.passwordHash, equalTo(ReservedRealm.DEFAULT_PASSWORD_HASH));
|
assertThat(userInfo.passwordHash, equalTo(ReservedRealm.EMPTY_PASSWORD_HASH));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInContainerTrueReturnsEmptyPasswordForNonElasticReservedUsers() throws Exception {
|
public void testInContainerTrueReturnsEmptyPasswordForNonElasticReservedUsers() throws Exception {
|
||||||
|
@ -142,9 +142,9 @@ public class NativeUsersStoreTests extends ESTestCase {
|
||||||
actionRespond(GetRequest.class, new GetResponse(result));
|
actionRespond(GetRequest.class, new GetResponse(result));
|
||||||
|
|
||||||
final NativeUsersStore.ReservedUserInfo userInfo = future.get();
|
final NativeUsersStore.ReservedUserInfo userInfo = future.get();
|
||||||
assertThat(userInfo.hasDefaultPassword, equalTo(true));
|
assertThat(userInfo.hasEmptyPassword, equalTo(true));
|
||||||
assertThat(userInfo.enabled, equalTo(true));
|
assertThat(userInfo.enabled, equalTo(true));
|
||||||
assertThat(userInfo.passwordHash, equalTo(ReservedRealm.DEFAULT_PASSWORD_HASH));
|
assertThat(userInfo.passwordHash, equalTo(ReservedRealm.EMPTY_PASSWORD_HASH));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInContainerTrueReturnsBootstrapPasswordForElastic() throws Exception {
|
public void testInContainerTrueReturnsBootstrapPasswordForElastic() throws Exception {
|
||||||
|
@ -171,7 +171,7 @@ public class NativeUsersStoreTests extends ESTestCase {
|
||||||
actionRespond(GetRequest.class, new GetResponse(result));
|
actionRespond(GetRequest.class, new GetResponse(result));
|
||||||
|
|
||||||
final NativeUsersStore.ReservedUserInfo userInfo = future.get();
|
final NativeUsersStore.ReservedUserInfo userInfo = future.get();
|
||||||
assertThat(userInfo.hasDefaultPassword, equalTo(false));
|
assertThat(userInfo.hasEmptyPassword, equalTo(false));
|
||||||
assertThat(userInfo.enabled, equalTo(true));
|
assertThat(userInfo.enabled, equalTo(true));
|
||||||
assertThat(userInfo.passwordHash, equalTo(passwordHash));
|
assertThat(userInfo.passwordHash, equalTo(passwordHash));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,13 @@ package org.elasticsearch.xpack.security.authc.esnative;
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
|
import org.elasticsearch.test.NativeRealmIntegTestCase;
|
||||||
|
import org.elasticsearch.xpack.security.action.user.ChangePasswordResponse;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
|
import org.elasticsearch.xpack.security.user.BeatsSystemUser;
|
||||||
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.action.user.ChangePasswordResponse;
|
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
|
||||||
import org.elasticsearch.test.NativeRealmIntegTestCase;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@ -27,12 +29,10 @@ import static org.hamcrest.Matchers.notNullValue;
|
||||||
*/
|
*/
|
||||||
public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
|
|
||||||
private static final SecureString DEFAULT_PASSWORD = new SecureString("changeme".toCharArray());
|
|
||||||
|
|
||||||
public void testAuthenticate() {
|
public void testAuthenticate() {
|
||||||
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME)) {
|
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, BeatsSystemUser.NAME, LogstashSystemUser.NAME)) {
|
||||||
ClusterHealthResponse response = client()
|
ClusterHealthResponse response = client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
@ -43,15 +43,15 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enabling a user forces a doc to be written to the security index, and "user doc with default password" has a special case code in
|
* Enabling a user forces a doc to be written to the security index, and "user doc with empty password" has a special case code in
|
||||||
* the reserved realm.
|
* the reserved realm.
|
||||||
*/
|
*/
|
||||||
public void testAuthenticateAfterEnablingUser() {
|
public void testAuthenticateAfterEnablingUser() {
|
||||||
final SecurityClient c = securityClient();
|
final SecurityClient c = securityClient();
|
||||||
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME)) {
|
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, BeatsSystemUser.NAME, LogstashSystemUser.NAME)) {
|
||||||
c.prepareSetEnabled(username, true).get();
|
c.prepareSetEnabled(username, true).get();
|
||||||
ClusterHealthResponse response = client()
|
ClusterHealthResponse response = client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
@ -62,12 +62,12 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testChangingPassword() {
|
public void testChangingPassword() {
|
||||||
String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
|
String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME, BeatsSystemUser.NAME, LogstashSystemUser.NAME);
|
||||||
final char[] newPassword = "supersecretvalue".toCharArray();
|
final char[] newPassword = "supersecretvalue".toCharArray();
|
||||||
|
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
ClusterHealthResponse response = client()
|
ClusterHealthResponse response = client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
@ -81,7 +81,7 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
assertThat(response, notNullValue());
|
assertThat(response, notNullValue());
|
||||||
|
|
||||||
ElasticsearchSecurityException elasticsearchSecurityException = expectThrows(ElasticsearchSecurityException.class, () -> client()
|
ElasticsearchSecurityException elasticsearchSecurityException = expectThrows(ElasticsearchSecurityException.class, () -> client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
@ -100,7 +100,7 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
public void testDisablingUser() throws Exception {
|
public void testDisablingUser() throws Exception {
|
||||||
// validate the user works
|
// validate the user works
|
||||||
ClusterHealthResponse response = client()
|
ClusterHealthResponse response = client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(ElasticUser.NAME, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(ElasticUser.NAME, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
@ -110,7 +110,7 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
// disable user
|
// disable user
|
||||||
securityClient().prepareSetEnabled(ElasticUser.NAME, false).get();
|
securityClient().prepareSetEnabled(ElasticUser.NAME, false).get();
|
||||||
ElasticsearchSecurityException elasticsearchSecurityException = expectThrows(ElasticsearchSecurityException.class, () -> client()
|
ElasticsearchSecurityException elasticsearchSecurityException = expectThrows(ElasticsearchSecurityException.class, () -> client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(ElasticUser.NAME, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(ElasticUser.NAME, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
@ -120,7 +120,7 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
|
||||||
//enable
|
//enable
|
||||||
securityClient().prepareSetEnabled(ElasticUser.NAME, true).get();
|
securityClient().prepareSetEnabled(ElasticUser.NAME, true).get();
|
||||||
response = client()
|
response = client()
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(ElasticUser.NAME, DEFAULT_PASSWORD)))
|
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(ElasticUser.NAME, getReservedPassword())))
|
||||||
.admin()
|
.admin()
|
||||||
.cluster()
|
.cluster()
|
||||||
.prepareHealth()
|
.prepareHealth()
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.security.authc.esnative;
|
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
|
||||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.test.NativeRealmIntegTestCase;
|
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
|
||||||
import org.elasticsearch.xpack.security.user.KibanaUser;
|
|
||||||
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integration tests for the built in realm with default passwords disabled
|
|
||||||
*/
|
|
||||||
public class ReservedRealmNoDefaultPasswordIntegTests extends NativeRealmIntegTestCase {
|
|
||||||
|
|
||||||
private static final SecureString DEFAULT_PASSWORD = new SecureString("changeme".toCharArray());
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Settings nodeSettings(int nodeOrdinal) {
|
|
||||||
Settings.Builder builder = Settings.builder()
|
|
||||||
.put(super.nodeSettings(nodeOrdinal))
|
|
||||||
.put(ReservedRealm.ACCEPT_DEFAULT_PASSWORD_SETTING.getKey(), false);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This ensures that if a user is explicitly enabled, thus creating an entry in the security index, but no password is ever set,
|
|
||||||
* then the user is treated as having a default password, and cannot login.
|
|
||||||
*/
|
|
||||||
public void testEnablingUserWithoutPasswordCannotLogin() throws Exception {
|
|
||||||
final SecurityClient c = securityClient();
|
|
||||||
c.prepareSetEnabled(KibanaUser.NAME, true).get();
|
|
||||||
|
|
||||||
ElasticsearchSecurityException elasticsearchSecurityException = expectThrows(ElasticsearchSecurityException.class, () -> client()
|
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(KibanaUser.NAME, DEFAULT_PASSWORD)))
|
|
||||||
.admin()
|
|
||||||
.cluster()
|
|
||||||
.prepareHealth()
|
|
||||||
.get());
|
|
||||||
assertThat(elasticsearchSecurityException.getMessage(), containsString("authenticate"));
|
|
||||||
|
|
||||||
final SecureString newPassword = new SecureString("not-the-default-password".toCharArray());
|
|
||||||
c.prepareChangePassword(KibanaUser.NAME, newPassword.clone().getChars()).get();
|
|
||||||
|
|
||||||
ClusterHealthResponse response = client()
|
|
||||||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(KibanaUser.NAME, newPassword)))
|
|
||||||
.admin()
|
|
||||||
.cluster()
|
|
||||||
.prepareHealth()
|
|
||||||
.get();
|
|
||||||
|
|
||||||
assertThat(response.getClusterName(), is(cluster().getClusterName()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
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;
|
||||||
|
@ -16,6 +17,7 @@ import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.ReservedUserInfo;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.ReservedUserInfo;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
|
@ -28,6 +30,9 @@ import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -58,62 +63,38 @@ import static org.mockito.Mockito.when;
|
||||||
*/
|
*/
|
||||||
public class ReservedRealmTests extends ESTestCase {
|
public class ReservedRealmTests extends ESTestCase {
|
||||||
|
|
||||||
private static final SecureString DEFAULT_PASSWORD = new SecureString("changeme".toCharArray());
|
private static final SecureString EMPTY_PASSWORD = new SecureString("".toCharArray());
|
||||||
public static final String ACCEPT_DEFAULT_PASSWORDS = ReservedRealm.ACCEPT_DEFAULT_PASSWORD_SETTING.getKey();
|
public static final String ACCEPT_DEFAULT_PASSWORDS = ReservedRealm.ACCEPT_DEFAULT_PASSWORD_SETTING.getKey();
|
||||||
private NativeUsersStore usersStore;
|
private NativeUsersStore usersStore;
|
||||||
private SecurityLifecycleService securityLifecycleService;
|
private SecurityLifecycleService securityLifecycleService;
|
||||||
|
private IncomingRequest incomingRequest;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupMocks() throws Exception {
|
public void setupMocks() throws Exception {
|
||||||
usersStore = mock(NativeUsersStore.class);
|
usersStore = mock(NativeUsersStore.class);
|
||||||
securityLifecycleService = mock(SecurityLifecycleService.class);
|
securityLifecycleService = mock(SecurityLifecycleService.class);
|
||||||
|
incomingRequest = mock(IncomingRequest.class);
|
||||||
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
|
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
|
||||||
when(securityLifecycleService.checkSecurityMappingVersion(any())).thenReturn(true);
|
when(securityLifecycleService.checkSecurityMappingVersion(any())).thenReturn(true);
|
||||||
mockGetAllReservedUserInfo(usersStore, Collections.emptyMap());
|
mockGetAllReservedUserInfo(usersStore, Collections.emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMappingVersionFromBeforeUserExisted() throws ExecutionException, InterruptedException {
|
@SuppressForbidden(reason = "allow getting localhost")
|
||||||
|
public void testMappingVersionFromBeforeUserExisted() throws ExecutionException, InterruptedException, UnknownHostException {
|
||||||
when(securityLifecycleService.checkSecurityMappingVersion(any())).thenReturn(false);
|
when(securityLifecycleService.checkSecurityMappingVersion(any())).thenReturn(false);
|
||||||
final ReservedRealm reservedRealm =
|
final ReservedRealm reservedRealm =
|
||||||
new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
||||||
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
||||||
final String principal = randomFrom(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME);
|
final String principal = ElasticUser.NAME;
|
||||||
|
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
reservedRealm.authenticate(new UsernamePasswordToken(principal, DEFAULT_PASSWORD), future);
|
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 100);
|
||||||
|
when(incomingRequest.getRemoteAddress()).thenReturn(address);
|
||||||
|
when(incomingRequest.getType()).thenReturn(IncomingRequest.RequestType.REST);
|
||||||
|
reservedRealm.authenticate(new UsernamePasswordToken(principal, EMPTY_PASSWORD), future, incomingRequest);
|
||||||
assertThat(future.get().enabled(), equalTo(false));
|
assertThat(future.get().enabled(), equalTo(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSuccessfulDefaultPasswordAuthentication() throws Throwable {
|
|
||||||
final User expected = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
|
|
||||||
final String principal = expected.principal();
|
|
||||||
final boolean securityIndexExists = randomBoolean();
|
|
||||||
if (securityIndexExists) {
|
|
||||||
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
|
|
||||||
doAnswer((i) -> {
|
|
||||||
ActionListener listener = (ActionListener) i.getArguments()[1];
|
|
||||||
listener.onResponse(null);
|
|
||||||
return null;
|
|
||||||
}).when(usersStore).getReservedUserInfo(eq(principal), any(ActionListener.class));
|
|
||||||
}
|
|
||||||
final ReservedRealm reservedRealm =
|
|
||||||
new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
|
||||||
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
|
||||||
|
|
||||||
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
|
||||||
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, DEFAULT_PASSWORD), listener);
|
|
||||||
final User authenticated = listener.actionGet();
|
|
||||||
assertEquals(expected, authenticated);
|
|
||||||
verify(securityLifecycleService).isSecurityIndexExisting();
|
|
||||||
if (securityIndexExists) {
|
|
||||||
verify(usersStore).getReservedUserInfo(eq(principal), any(ActionListener.class));
|
|
||||||
}
|
|
||||||
final ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
|
|
||||||
verify(securityLifecycleService).checkSecurityMappingVersion(predicateCaptor.capture());
|
|
||||||
verifyVersionPredicate(principal, predicateCaptor.getValue());
|
|
||||||
verifyNoMoreInteractions(usersStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDisableDefaultPasswordAuthentication() throws Throwable {
|
public void testDisableDefaultPasswordAuthentication() throws Throwable {
|
||||||
final User expected = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
|
final User expected = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
|
||||||
|
|
||||||
|
@ -135,7 +116,51 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
assertThat(e.getMessage(), containsString("failed to authenticate"));
|
assertThat(e.getMessage(), containsString("failed to authenticate"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reservedRealm.doAuthenticate(new UsernamePasswordToken(expected.principal(), DEFAULT_PASSWORD), listener);
|
reservedRealm.doAuthenticate(new UsernamePasswordToken(expected.principal(), EMPTY_PASSWORD), listener, incomingRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testElasticEmptyPasswordAuthenticationFailsFromNonLocalhost() throws Throwable {
|
||||||
|
final User expected = new ElasticUser(true);
|
||||||
|
final String principal = expected.principal();
|
||||||
|
|
||||||
|
Settings settings = Settings.builder().put(ACCEPT_DEFAULT_PASSWORDS, true).build();
|
||||||
|
final ReservedRealm reservedRealm =
|
||||||
|
new ReservedRealm(mock(Environment.class), settings, usersStore,
|
||||||
|
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
||||||
|
|
||||||
|
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
||||||
|
|
||||||
|
InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("128.9.8.1"), 100);
|
||||||
|
|
||||||
|
when(incomingRequest.getRemoteAddress()).thenReturn(address);
|
||||||
|
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, EMPTY_PASSWORD), listener, incomingRequest);
|
||||||
|
|
||||||
|
ElasticsearchSecurityException actual = expectThrows(ElasticsearchSecurityException.class, listener::actionGet);
|
||||||
|
assertThat(actual.getMessage(), containsString("failed to authenticate user [" + principal));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressForbidden(reason = "allow getting localhost")
|
||||||
|
public void testElasticEmptyPasswordAuthenticationSucceedsInSetupModeIfRestRequestComesFromLocalhost() throws Throwable {
|
||||||
|
final User expected = new ElasticUser(true, true);
|
||||||
|
final String principal = expected.principal();
|
||||||
|
|
||||||
|
Settings settings = Settings.builder().put(ACCEPT_DEFAULT_PASSWORDS, true).build();
|
||||||
|
final ReservedRealm reservedRealm =
|
||||||
|
new ReservedRealm(mock(Environment.class), settings, usersStore,
|
||||||
|
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
||||||
|
|
||||||
|
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
||||||
|
|
||||||
|
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 100);
|
||||||
|
|
||||||
|
when(incomingRequest.getRemoteAddress()).thenReturn(address);
|
||||||
|
when(incomingRequest.getType()).thenReturn(IncomingRequest.RequestType.REST);
|
||||||
|
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, EMPTY_PASSWORD), listener, incomingRequest);
|
||||||
|
|
||||||
|
User user = listener.actionGet();
|
||||||
|
|
||||||
|
assertEquals(expected, user);
|
||||||
|
assertNotEquals(new ElasticUser(true, false), user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAuthenticationDisabled() throws Throwable {
|
public void testAuthenticationDisabled() throws Throwable {
|
||||||
|
@ -151,7 +176,7 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
final String principal = expected.principal();
|
final String principal = expected.principal();
|
||||||
|
|
||||||
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
||||||
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, DEFAULT_PASSWORD), listener);
|
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, EMPTY_PASSWORD), listener, mock(IncomingRequest.class));
|
||||||
final User authenticated = listener.actionGet();
|
final User authenticated = listener.actionGet();
|
||||||
assertNull(authenticated);
|
assertNull(authenticated);
|
||||||
verifyZeroInteractions(usersStore);
|
verifyZeroInteractions(usersStore);
|
||||||
|
@ -179,9 +204,9 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
return null;
|
return null;
|
||||||
}).when(usersStore).getReservedUserInfo(eq(principal), any(ActionListener.class));
|
}).when(usersStore).getReservedUserInfo(eq(principal), any(ActionListener.class));
|
||||||
|
|
||||||
// test default password
|
// test empty password
|
||||||
final PlainActionFuture<User> listener = new PlainActionFuture<>();
|
final PlainActionFuture<User> listener = new PlainActionFuture<>();
|
||||||
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, DEFAULT_PASSWORD), listener);
|
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, EMPTY_PASSWORD), listener, incomingRequest);
|
||||||
ElasticsearchSecurityException expected = expectThrows(ElasticsearchSecurityException.class, listener::actionGet);
|
ElasticsearchSecurityException expected = expectThrows(ElasticsearchSecurityException.class, listener::actionGet);
|
||||||
assertThat(expected.getMessage(), containsString("failed to authenticate user [" + principal));
|
assertThat(expected.getMessage(), containsString("failed to authenticate user [" + principal));
|
||||||
|
|
||||||
|
@ -194,7 +219,7 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
|
|
||||||
// test new password
|
// test new password
|
||||||
final PlainActionFuture<User> authListener = new PlainActionFuture<>();
|
final PlainActionFuture<User> authListener = new PlainActionFuture<>();
|
||||||
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, newPassword), authListener);
|
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, newPassword), authListener, incomingRequest);
|
||||||
final User authenticated = authListener.actionGet();
|
final User authenticated = authListener.actionGet();
|
||||||
assertEquals(expectedUser, authenticated);
|
assertEquals(expectedUser, authenticated);
|
||||||
assertThat(expectedUser.enabled(), is(enabled));
|
assertThat(expectedUser.enabled(), is(enabled));
|
||||||
|
@ -211,7 +236,7 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
final ReservedRealm reservedRealm =
|
final ReservedRealm reservedRealm =
|
||||||
new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
||||||
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
||||||
final User expectedUser = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
|
final User expectedUser = randomFrom(new ElasticUser(true, true), new KibanaUser(true), new LogstashSystemUser(true));
|
||||||
final String principal = expectedUser.principal();
|
final String principal = expectedUser.principal();
|
||||||
|
|
||||||
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
PlainActionFuture<User> listener = new PlainActionFuture<>();
|
||||||
|
@ -299,7 +324,7 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
||||||
PlainActionFuture<Collection<User>> userFuture = new PlainActionFuture<>();
|
PlainActionFuture<Collection<User>> userFuture = new PlainActionFuture<>();
|
||||||
reservedRealm.users(userFuture);
|
reservedRealm.users(userFuture);
|
||||||
assertThat(userFuture.actionGet(), containsInAnyOrder(new ElasticUser(true), new KibanaUser(true),
|
assertThat(userFuture.actionGet(), containsInAnyOrder(new ElasticUser(true, true), new KibanaUser(true),
|
||||||
new LogstashSystemUser(true), new BeatsSystemUser(true)));
|
new LogstashSystemUser(true), new BeatsSystemUser(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,19 +346,29 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFailedAuthentication() {
|
@SuppressForbidden(reason = "allow getting localhost")
|
||||||
|
public void testFailedAuthentication() throws UnknownHostException {
|
||||||
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore,
|
||||||
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
|
||||||
|
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 100);
|
||||||
|
|
||||||
// maybe cache a successful auth
|
// maybe cache a successful auth
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
reservedRealm.authenticate(new UsernamePasswordToken(ElasticUser.NAME, new SecureString("changeme".toCharArray())), future);
|
|
||||||
|
IncomingRequest r = mock(IncomingRequest.class);
|
||||||
|
when(r.getRemoteAddress()).thenReturn(address);
|
||||||
|
when(r.getType()).thenReturn(IncomingRequest.RequestType.REST);
|
||||||
|
reservedRealm.authenticate(new UsernamePasswordToken(ElasticUser.NAME, EMPTY_PASSWORD), future, r);
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertEquals(new ElasticUser(true), user);
|
assertEquals(new ElasticUser(true, true), user);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
reservedRealm.authenticate(new UsernamePasswordToken(ElasticUser.NAME, new SecureString("foobar".toCharArray())), future);
|
IncomingRequest r = mock(IncomingRequest.class);
|
||||||
|
when(r.getRemoteAddress()).thenReturn(address);
|
||||||
|
when(r.getType()).thenReturn(IncomingRequest.RequestType.REST);
|
||||||
|
reservedRealm.authenticate(new UsernamePasswordToken(ElasticUser.NAME, new SecureString("foobar".toCharArray())), future, r);
|
||||||
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, future::actionGet);
|
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, future::actionGet);
|
||||||
assertThat(e.getMessage(), containsString("failed to authenticate"));
|
assertThat(e.getMessage(), containsString("failed to authenticate"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class SetupPasswordToolTests extends CommandTestCase {
|
||||||
execute("auto", pathHomeParameter, "-b", "true");
|
execute("auto", pathHomeParameter, "-b", "true");
|
||||||
|
|
||||||
ArgumentCaptor<String> passwordCaptor = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> passwordCaptor = ArgumentCaptor.forClass(String.class);
|
||||||
SecureString defaultPassword = new SecureString("changeme".toCharArray());
|
SecureString defaultPassword = new SecureString("".toCharArray());
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(httpClient);
|
InOrder inOrder = Mockito.inOrder(httpClient);
|
||||||
String elasticUrl = "http://localhost:9200/_xpack/security/user/elastic/_password";
|
String elasticUrl = "http://localhost:9200/_xpack/security/user/elastic/_password";
|
||||||
|
@ -80,7 +80,7 @@ public class SetupPasswordToolTests extends CommandTestCase {
|
||||||
execute("auto", pathHomeParameter, "-u", url, "-b");
|
execute("auto", pathHomeParameter, "-u", url, "-b");
|
||||||
|
|
||||||
ArgumentCaptor<String> passwordCaptor = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> passwordCaptor = ArgumentCaptor.forClass(String.class);
|
||||||
SecureString defaultPassword = new SecureString("changeme".toCharArray());
|
SecureString defaultPassword = new SecureString("".toCharArray());
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(httpClient);
|
InOrder inOrder = Mockito.inOrder(httpClient);
|
||||||
String elasticUrl = url + "/_xpack/security/user/elastic/_password";
|
String elasticUrl = url + "/_xpack/security/user/elastic/_password";
|
||||||
|
@ -99,7 +99,7 @@ public class SetupPasswordToolTests extends CommandTestCase {
|
||||||
|
|
||||||
execute("interactive", pathHomeParameter);
|
execute("interactive", pathHomeParameter);
|
||||||
|
|
||||||
SecureString defaultPassword = new SecureString("changeme".toCharArray());
|
SecureString defaultPassword = new SecureString("".toCharArray());
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(httpClient);
|
InOrder inOrder = Mockito.inOrder(httpClient);
|
||||||
String elasticUrl = "http://localhost:9200/_xpack/security/user/elastic/_password";
|
String elasticUrl = "http://localhost:9200/_xpack/security/user/elastic/_password";
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.settings.SecureString;
|
||||||
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.env.Environment;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
|
@ -53,7 +54,7 @@ public class FileRealmTests extends ESTestCase {
|
||||||
RealmConfig config = new RealmConfig("file-test", Settings.EMPTY, globalSettings, new Environment(globalSettings), new ThreadContext(globalSettings));
|
RealmConfig config = new RealmConfig("file-test", Settings.EMPTY, globalSettings, new Environment(globalSettings), new ThreadContext(globalSettings));
|
||||||
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, notNullValue());
|
assertThat(user, notNullValue());
|
||||||
assertThat(user.principal(), equalTo("user1"));
|
assertThat(user.principal(), equalTo("user1"));
|
||||||
|
@ -71,10 +72,10 @@ public class FileRealmTests extends ESTestCase {
|
||||||
when(userRolesStore.roles("user1")).thenReturn(new String[]{"role1", "role2"});
|
when(userRolesStore.roles("user1")).thenReturn(new String[]{"role1", "role2"});
|
||||||
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user1 = future.actionGet();
|
User user1 = future.actionGet();
|
||||||
future = new PlainActionFuture<>();
|
future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user2 = future.actionGet();
|
User user2 = future.actionGet();
|
||||||
assertThat(user1, sameInstance(user2));
|
assertThat(user1, sameInstance(user2));
|
||||||
}
|
}
|
||||||
|
@ -87,32 +88,32 @@ public class FileRealmTests extends ESTestCase {
|
||||||
doReturn(new String[] { "role1", "role2" }).when(userRolesStore).roles("user1");
|
doReturn(new String[] { "role1", "role2" }).when(userRolesStore).roles("user1");
|
||||||
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
FileRealm realm = new FileRealm(config, userPasswdStore, userRolesStore);
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user1 = future.actionGet();
|
User user1 = future.actionGet();
|
||||||
future = new PlainActionFuture<>();
|
future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user2 = future.actionGet();
|
User user2 = future.actionGet();
|
||||||
assertThat(user1, sameInstance(user2));
|
assertThat(user1, sameInstance(user2));
|
||||||
|
|
||||||
userPasswdStore.notifyRefresh();
|
userPasswdStore.notifyRefresh();
|
||||||
|
|
||||||
future = new PlainActionFuture<>();
|
future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user3 = future.actionGet();
|
User user3 = future.actionGet();
|
||||||
assertThat(user2, not(sameInstance(user3)));
|
assertThat(user2, not(sameInstance(user3)));
|
||||||
future = new PlainActionFuture<>();
|
future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user4 = future.actionGet();
|
User user4 = future.actionGet();
|
||||||
assertThat(user3, sameInstance(user4));
|
assertThat(user3, sameInstance(user4));
|
||||||
|
|
||||||
userRolesStore.notifyRefresh();
|
userRolesStore.notifyRefresh();
|
||||||
|
|
||||||
future = new PlainActionFuture<>();
|
future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user5 = future.actionGet();
|
User user5 = future.actionGet();
|
||||||
assertThat(user4, not(sameInstance(user5)));
|
assertThat(user4, not(sameInstance(user5)));
|
||||||
future = new PlainActionFuture<>();
|
future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future);
|
realm.authenticate(new UsernamePasswordToken("user1", new SecureString("test123")), future, mock(IncomingRequest.class));
|
||||||
User user6 = future.actionGet();
|
User user6 = future.actionGet();
|
||||||
assertThat(user5, sameInstance(user6));
|
assertThat(user5, sameInstance(user6));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.common.io.PathUtilsForTesting;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
||||||
|
@ -64,10 +65,11 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
IOUtils.rm(homeDir);
|
IOUtils.rm(homeDir);
|
||||||
confDir = homeDir.resolve("config").resolve(XPackPlugin.NAME);
|
confDir = homeDir.resolve("config").resolve(XPackPlugin.NAME);
|
||||||
Files.createDirectories(confDir);
|
Files.createDirectories(confDir);
|
||||||
|
String defaultPassword = SecuritySettingsSource.TEST_PASSWORD;
|
||||||
Files.write(confDir.resolve("users"), Arrays.asList(
|
Files.write(confDir.resolve("users"), Arrays.asList(
|
||||||
"existing_user:" + new String(Hasher.BCRYPT.hash(new SecureString("changeme".toCharArray()))),
|
"existing_user:" + new String(Hasher.BCRYPT.hash(SecuritySettingsSource.TEST_PASSWORD_SECURE_STRING)),
|
||||||
"existing_user2:" + new String(Hasher.BCRYPT.hash(new SecureString("changeme2".toCharArray()))),
|
"existing_user2:" + new String(Hasher.BCRYPT.hash(new SecureString((defaultPassword + "2").toCharArray()))),
|
||||||
"existing_user3:" + new String(Hasher.BCRYPT.hash(new SecureString("changeme3".toCharArray())))
|
"existing_user3:" + new String(Hasher.BCRYPT.hash(new SecureString((defaultPassword + "3").toCharArray())))
|
||||||
), StandardCharsets.UTF_8);
|
), StandardCharsets.UTF_8);
|
||||||
Files.write(confDir.resolve("users_roles"), Arrays.asList(
|
Files.write(confDir.resolve("users_roles"), Arrays.asList(
|
||||||
"test_admin:existing_user,existing_user2",
|
"test_admin:existing_user,existing_user2",
|
||||||
|
@ -268,20 +270,20 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUseraddNoPassword() throws Exception {
|
public void testUseraddNoPassword() throws Exception {
|
||||||
terminal.addSecretInput("changeme");
|
terminal.addSecretInput(SecuritySettingsSource.TEST_PASSWORD);
|
||||||
terminal.addSecretInput("changeme");
|
terminal.addSecretInput(SecuritySettingsSource.TEST_PASSWORD);
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, "username");
|
execute("useradd", pathHomeParameter, fileTypeParameter, "username");
|
||||||
assertUser("username", "changeme");
|
assertUser("username", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUseraddPasswordOption() throws Exception {
|
public void testUseraddPasswordOption() throws Exception {
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", "changeme");
|
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
assertUser("username", "changeme");
|
assertUser("username", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUseraddUserExists() throws Exception {
|
public void testUseraddUserExists() throws Exception {
|
||||||
UserException e = expectThrows(UserException.class, () -> {
|
UserException e = expectThrows(UserException.class, () -> {
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, "existing_user", "-p", "changeme");
|
execute("useradd", pathHomeParameter, fileTypeParameter, "existing_user", "-p", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
});
|
});
|
||||||
assertEquals(ExitCodes.CODE_ERROR, e.exitCode);
|
assertEquals(ExitCodes.CODE_ERROR, e.exitCode);
|
||||||
assertEquals("User [existing_user] already exists", e.getMessage());
|
assertEquals("User [existing_user] already exists", e.getMessage());
|
||||||
|
@ -290,7 +292,7 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
public void testUseraddReservedUser() throws Exception {
|
public void testUseraddReservedUser() throws Exception {
|
||||||
final String name = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
|
final String name = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
|
||||||
UserException e = expectThrows(UserException.class, () -> {
|
UserException e = expectThrows(UserException.class, () -> {
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, name, "-p", "changeme");
|
execute("useradd", pathHomeParameter, fileTypeParameter, name, "-p", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
});
|
});
|
||||||
assertEquals(ExitCodes.DATA_ERROR, e.exitCode);
|
assertEquals(ExitCodes.DATA_ERROR, e.exitCode);
|
||||||
assertEquals("Invalid username [" + name + "]... Username [" + name + "] is reserved and may not be used.", e.getMessage());
|
assertEquals("Invalid username [" + name + "]... Username [" + name + "] is reserved and may not be used.", e.getMessage());
|
||||||
|
@ -299,7 +301,7 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
public void testUseraddNoRoles() throws Exception {
|
public void testUseraddNoRoles() throws Exception {
|
||||||
Files.delete(confDir.resolve("users_roles"));
|
Files.delete(confDir.resolve("users_roles"));
|
||||||
Files.createFile(confDir.resolve("users_roles"));
|
Files.createFile(confDir.resolve("users_roles"));
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", "changeme");
|
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
List<String> lines = Files.readAllLines(confDir.resolve("users_roles"), StandardCharsets.UTF_8);
|
List<String> lines = Files.readAllLines(confDir.resolve("users_roles"), StandardCharsets.UTF_8);
|
||||||
assertTrue(lines.toString(), lines.isEmpty());
|
assertTrue(lines.toString(), lines.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -319,7 +321,7 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
|
|
||||||
public void testPasswdUnknownUser() throws Exception {
|
public void testPasswdUnknownUser() throws Exception {
|
||||||
UserException e = expectThrows(UserException.class, () -> {
|
UserException e = expectThrows(UserException.class, () -> {
|
||||||
execute("passwd", pathHomeParameter, fileTypeParameter, "unknown", "-p", "changeme");
|
execute("passwd", pathHomeParameter, fileTypeParameter, "unknown", "-p", SecuritySettingsSource.TEST_PASSWORD);
|
||||||
});
|
});
|
||||||
assertEquals(ExitCodes.NO_USER, e.exitCode);
|
assertEquals(ExitCodes.NO_USER, e.exitCode);
|
||||||
assertTrue(e.getMessage(), e.getMessage().contains("User [unknown] doesn't exist"));
|
assertTrue(e.getMessage(), e.getMessage().contains("User [unknown] doesn't exist"));
|
||||||
|
@ -365,7 +367,8 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRolesRemoveLeavesExisting() throws Exception {
|
public void testRolesRemoveLeavesExisting() throws Exception {
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", "changeme", "-r", "test_admin");
|
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", SecuritySettingsSource.TEST_PASSWORD,
|
||||||
|
"-r", "test_admin");
|
||||||
execute("roles", pathHomeParameter, fileTypeParameter, "existing_user", "-r", "test_admin");
|
execute("roles", pathHomeParameter, fileTypeParameter, "existing_user", "-r", "test_admin");
|
||||||
assertRole("test_admin", "username");
|
assertRole("test_admin", "username");
|
||||||
}
|
}
|
||||||
|
@ -407,7 +410,8 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testListUnknownRoles() throws Exception {
|
public void testListUnknownRoles() throws Exception {
|
||||||
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", "changeme", "-r", "test_r1,r2,r3");
|
execute("useradd", pathHomeParameter, fileTypeParameter, "username", "-p", SecuritySettingsSource.TEST_PASSWORD,
|
||||||
|
"-r", "test_r1,r2,r3");
|
||||||
String output = execute("list", pathHomeParameter, fileTypeParameter, "username");
|
String output = execute("list", pathHomeParameter, fileTypeParameter, "username");
|
||||||
assertTrue(output, output.contains("username"));
|
assertTrue(output, output.contains("username"));
|
||||||
assertTrue(output, output.contains("r2*,r3*,test_r1"));
|
assertTrue(output, output.contains("r2*,r3*,test_r1"));
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.elasticsearch.common.settings.SecureString;
|
||||||
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.env.Environment;
|
||||||
|
import org.elasticsearch.xpack.security.authc.IncomingRequest;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.DownLevelADAuthenticator;
|
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.DownLevelADAuthenticator;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.UpnADAuthenticator;
|
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactory.UpnADAuthenticator;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
@ -53,6 +54,7 @@ import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
@ -138,7 +140,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
||||||
|
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, is(notNullValue()));
|
assertThat(user, is(notNullValue()));
|
||||||
assertThat(user.roles(), arrayContaining(containsString("Avengers")));
|
assertThat(user.roles(), arrayContaining(containsString("Avengers")));
|
||||||
|
@ -153,7 +155,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
|
|
||||||
// Thor does not have a UPN of form CN=Thor@ad.test.elasticsearch.com
|
// Thor does not have a UPN of form CN=Thor@ad.test.elasticsearch.com
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=Thor", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=Thor", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, is(notNullValue()));
|
assertThat(user, is(notNullValue()));
|
||||||
assertThat(user.roles(), arrayContaining(containsString("Avengers")));
|
assertThat(user.roles(), arrayContaining(containsString("Avengers")));
|
||||||
|
@ -178,7 +180,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
int count = randomIntBetween(2, 10);
|
int count = randomIntBetween(2, 10);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
future.actionGet();
|
future.actionGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +198,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
int count = randomIntBetween(2, 10);
|
int count = randomIntBetween(2, 10);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
future.actionGet();
|
future.actionGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +216,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
int count = randomIntBetween(2, 10);
|
int count = randomIntBetween(2, 10);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
future.actionGet();
|
future.actionGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +228,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
future.actionGet();
|
future.actionGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +245,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
||||||
|
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=ironman", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, is(notNullValue()));
|
assertThat(user, is(notNullValue()));
|
||||||
assertThat(user.roles(), arrayContaining(equalTo("group_role")));
|
assertThat(user.roles(), arrayContaining(equalTo("group_role")));
|
||||||
|
@ -259,7 +261,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
LdapRealm realm = new LdapRealm(LdapRealm.AD_TYPE, config, sessionFactory, roleMapper, threadPool);
|
||||||
|
|
||||||
PlainActionFuture<User> future = new PlainActionFuture<>();
|
PlainActionFuture<User> future = new PlainActionFuture<>();
|
||||||
realm.authenticate(new UsernamePasswordToken("CN=Thor", new SecureString(PASSWORD)), future);
|
realm.authenticate(new UsernamePasswordToken("CN=Thor", new SecureString(PASSWORD)), future, mock(IncomingRequest.class));
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, is(notNullValue()));
|
assertThat(user, is(notNullValue()));
|
||||||
assertThat(user.roles(), arrayContainingInAnyOrder(equalTo("group_role"), equalTo("user_role")));
|
assertThat(user.roles(), arrayContainingInAnyOrder(equalTo("group_role"), equalTo("user_role")));
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue