security: ssl by default on the transport layer

This commit adds the necessary changes to make SSL work on the transport layer by default. A large
portion of the SSL configuration/settings was re-worked with this change. Some notable highlights
include support for PEM cert/keys, reloadable SSL configuration, separate HTTP ssl configuration, and
separate LDAP configuration.

The following is a list of specific items addressed:

* `SSLSettings` renamed to `SSLConfiguration`
* `KeyConfig` and `TrustConfig` abstractions created. These hide the details of how `KeyManager[]` and `TrustManager[]` are loaded. These are also responsible for settings validation (ie keystore password is not null)
* Configuration fallback is changed. Previously any setting would fallback to the "global" value (`xpack.security.ssl.*`). Now a keystore path, key path, ca paths, or truststore path must be specified otherwise the configuration for that key/trust will fallback to the global configuration. In other words if you want to change part of a keystore or truststore in a profile you need to supply all the information. This could be considered breaking if a user relied on the old fallback
* JDK trusted certificates (`cacerts`) are trusted by default (breaking change). This can be disabled via a setting.
* We now monitor the SSL files for changes and enable dynamic reloading of the configuration. This will make it easier for users when they are getting set up with certificates so they do not need to restart every time. This can be disabled via a setting
* LDAP realms can now have their own SSL configurations
* HTTP can now have its own SSL configuration
* SSL is enabled by default on the transport layer only. Hostname verification is enabled as well. On startup if no global SSL settings are present and SSL is configured to be used, we auto generate one based on the default CA that is shipped. This process includes a best effort attempt to generate the subject alternative names.
* `xpack.security.ssl.hostname_verification` is deprecated in favor of `xpack.security.ssl.hostname_verification.enabled`
* added Bouncy Castle info to NOTICE
* consolidated NOTICE and LICENSE files

Closes elastic/elasticsearch#14
Closes elastic/elasticsearch#34
Closes elastic/elasticsearch#1483
Closes elastic/elasticsearch#1933
Addresses security portion of elastic/elasticsearch#673

Original commit: elastic/x-pack-elasticsearch@7c359db90b
This commit is contained in:
jaymode 2016-04-12 09:19:07 -04:00
parent 29b996ea1d
commit 773876caee
80 changed files with 4029 additions and 2254 deletions

View File

@ -4,6 +4,14 @@ dependencies {
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
}
String outputDir = "generated-resources/${project.name}"
task copyXPackPluginProps(type: Copy) {
from project(':x-plugins:elasticsearch:x-pack').file('src/main/plugin-metadata')
from project(':x-plugins:elasticsearch:x-pack').tasks.pluginProperties
into outputDir
}
project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps)
integTest {
cluster {
plugin 'x-pack', project(':x-plugins:elasticsearch:x-pack')

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.shield.audit;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.action.search.SearchResponse;
@ -38,13 +39,22 @@ public class IndexAuditIT extends ESIntegTestCase {
assertThat(response.getStatusCode(), is(200));
boolean found = awaitBusy(() -> {
if (client().admin().cluster().prepareState().get().getState().getMetaData().getIndices().size() < 1) {
boolean exists = false;
for (ObjectCursor<String> cursor :
client().admin().cluster().prepareState().get().getState().getMetaData().getIndices().keys()) {
if (cursor.value.startsWith(".shield_audit_log")) {
exists = true;
break;
}
}
if (exists == false) {
return false;
}
client().admin().indices().prepareRefresh().get();
return client().prepareSearch(".shield_audit_log*").setQuery(QueryBuilders.matchQuery("principal", USER))
.get().getHits().totalHits() > 0;
}, 5L, TimeUnit.SECONDS);
}, 10L, TimeUnit.SECONDS);
assertThat(found, is(true));

View File

@ -4,6 +4,14 @@ dependencies {
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
}
String outputDir = "generated-resources/${project.name}"
task copyXPackPluginProps(type: Copy) {
from project(':x-plugins:elasticsearch:x-pack').file('src/main/plugin-metadata')
from project(':x-plugins:elasticsearch:x-pack').tasks.pluginProperties
into outputDir
}
project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps)
integTest {
cluster {
plugin 'x-pack', project(':x-plugins:elasticsearch:x-pack')

View File

@ -23,6 +23,14 @@ Map generateSubstitutions() {
]
}
String outputDir = "generated-resources/${project.name}"
task copyXPackPluginProps(type: Copy) {
from project(':x-plugins:elasticsearch:x-pack').file('src/main/plugin-metadata')
from project(':x-plugins:elasticsearch:x-pack').tasks.pluginProperties
into outputDir
}
project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps)
processResources {
MavenFilteringHack.filter(it, generateSubstitutions())
}

View File

@ -7,6 +7,14 @@ dependencies {
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
}
String outputDir = "generated-resources/${project.name}"
task copyXPackPluginProps(type: Copy) {
from project(':x-plugins:elasticsearch:x-pack').file('src/main/plugin-metadata')
from project(':x-plugins:elasticsearch:x-pack').tasks.pluginProperties
into outputDir
}
project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps)
// needed to be consistent with ssl host checking
Object san = new SanEvaluator()

View File

@ -1,5 +1,5 @@
Elasticsearch Shield
Copyright 2009-2015 Elasticsearch
Elasticsearch X-Pack
Copyright 2009-2016 Elasticsearch
---
This product includes software developed by The Apache Software
@ -131,4 +131,76 @@ remaining provisions shall in no way be affected or impaired thereby. This
Agreement and performance hereunder shall be governed by and construed in
accordance with the laws of the State of Texas without regard to its conflict
of laws rules. Any disputes related to this Agreement shall be exclusively
litigated in the state or federal courts located in Travis County, Texas.
litigated in the state or federal courts located in Travis County, Texas.
---
This product contains software distributed under
Common Development and Distribution License 1.0 (CDDL)
JavaMail API 1.5.3
https://java.net/projects/javamail/pages/Home
https://java.net/projects/javamail/pages/License
JavaBeans Activation Framework 1.1.1
http://www.oracle.com/technetwork/articles/java/index-135046.html
---
This product contains software developed by Mike Samuel. The
following is the copyright and notice text for this software:
Copyright (c) 2011, Mike Samuel
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the OWASP nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---
This product contains software developed by The Legion of the Bouncy Castle.
The following is the copyright and notice text for this software:
Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc.
(http://www.bouncycastle.org)
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -27,6 +27,8 @@ dependencies {
// shield deps
compile 'dk.brics.automaton:automaton:1.11-8'
compile 'com.unboundid:unboundid-ldapsdk:2.3.8'
compile 'org.bouncycastle:bcprov-jdk15on:1.54'
compile 'org.bouncycastle:bcpkix-jdk15on:1.54'
testCompile 'com.google.jimfs:jimfs:1.0'
// watcher deps
@ -47,7 +49,6 @@ dependencies {
testCompile "com.squareup.okhttp:okhttp:${versions.okhttp}"
testCompile "com.squareup.okhttp:okhttp-ws:${versions.okhttp}"
testCompile 'com.squareup.okio:okio:1.6.0'
testCompile 'org.bouncycastle:bcprov-jdk15on:1.50'
}
// we keep the source directories in the original structure of split plugins,
@ -99,6 +100,7 @@ processTestResources {
forbiddenPatterns {
exclude '**/*.key'
exclude '**/*.p12'
exclude '**/*.der'
}
// TODO: standardize packaging config for plugins

View File

@ -1,439 +0,0 @@
SOFTWARE END USER LICENSE AGREEMENT
READ THIS AGREEMENT CAREFULLY, WHICH CONSTITUTES A LEGALLY BINDING AGREEMENT AND GOVERNS YOUR USE OF
ELASTICSEARCHS PROPRIETARY SOFTWARE. BY INSTALLING AND/OR USING SUCH SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO THE
TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH SUCH TERMS AND CONDITIONS, YOU MAY NOT
INSTALL OR USE ANY OF THE SOFTWARE.
This END USER LICENSE AGREEMENT (this “Agreement") is entered into by and between the applicable Elasticsearch
entity referenced in Attachment 1 hereto (“Elasticsearch”) and the person or entity (“You”) that has downloaded any of
Elasticsearchs proprietary software to which this Agreement is attached or in connection with which this Agreement is
presented to You (collectively, the “Software”). This Agreement is effective upon the earlier of the date on the
commencement of any License granted pursuant to Section 1.1. below (as applicable, the “Effective Date”).
1. SOFTWARE LICENSE AND RESTRICTIONS
1.1 License Grants.
(a) Trial Version License. Subject to the terms and conditions of this Agreement, Elasticsearch agrees to
grant, and does hereby grant to You, for a period of thirty (30) days from the date on which You first install the
Software (the “Trial Term”), a License to the to use the Eligible Features and Functions of the Software that are
applicable to the Trial Version of the Software. You understand and agree that upon the expiration of a Trial Term,
You will no longer be able to use the Software, unless you either (i) purchase a Subscription, in which case You will
receive a License under Section 1.1(b) below to use the Eligible Features and Functions of the Software that are
applicable to the Subscription that You purchase, (ii) undertake the Registration of Your use of the Software with
Elasticsearch, in which case You will receive a License under Section 1.1(c) below to the Basic Version of the Software
or (iii) obtain from Elasticsearch written consent (e-mail sufficient) to extend the Trial Term, which may be granted by
Elasticsearch in its sole and absolute discretion.
(b) Subscription License. Subject to the terms and conditions of this Agreement and complete payment of any and
all applicable Subscription fees, Elasticsearch agrees to grant, and does hereby grant to You during the Subscription
Term, and for the restricted scope of this Agreement, a License (i) to use the Eligible Features and Functions of the
Software that are applicable to the Subscription level that You have purchased, (ii) for the number of Nodes (as defined
in the Elasticsearch Support Services Policy) and (iii) for the specific project for which you have purchased a
Subscription. The level of Subscription, the number of Nodes and specific project for which you have purchased such
Subscription, are set forth on the applicable ordering document entered into by Elasticsearch and You for the purchase
of the applicable Subscription (“Order Form”).
(c) Basic Version License. Subject to the terms and conditions of this Agreement, and in consideration of the
Registration of Your use the Software, Elasticsearch agrees to grant, and does hereby grant to You, for a period of one
(1) year from the date of Registration (“Basic Term”), a License to use the Eligible Features and Functions of the
Software that are applicable to the Basic Version of the Software.
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You, Elasticsearch owns all right title and
interest in and to the Software and any derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Software is granted to You by implication, estoppel or otherwise. You agree not to: (i)
reverse engineer or decompile, decrypt, disassemble or otherwise reduce any Software or any portion thereof to
human-readable form, except and only to the extent any such restriction is prohibited by applicable law, (ii) deploy the
Software on more Nodes (as defined in Elasticsearchs Support Services Policy) than are permitted under the applicable
License grant in Section 1.1 above (iii) where You have purchased a Subscription, use the Software in connection with
any project other than the project for which you have purchased such Subscription, as identified on the applicable Order
Form, (iv) prepare derivative works from, modify, copy or use the Software in any manner except as expressly permitted
in this Agreement; (v) except as expressly permitted in Section 1.1 above, transfer, sell, rent, lease, distribute,
sublicense, loan or otherwise transfer the Software in whole or in part to any third party; (vi) except as may be
expressly permitted on an applicable Order Form, use the Software for providing time-sharing services, any
software-as-a-service offering (“SaaS”), service bureau services or as part of an application services provider or other
service offering; (vii) circumvent the limitations on use of the Software that are imposed or preserved by any License
Key, (viii) alter or remove any proprietary notices in the Software; or (ix) make available to any third party any
analysis of the results of operation of the Software, including benchmarking results, without the prior written consent
of Elasticsearch. The Software may contain or be provided with open source libraries, components, utilities and other
open source software (collectively, “Open Source Software”), which Open Source Software may have applicable license
terms as identified on a website designated by Elasticsearch or otherwise provided with the Software or Documentation.
Notwithstanding anything to the contrary herein, use of the Open Source Software shall be subject to the license terms
and conditions applicable to such Open Source Software, to the extent required by the applicable licensor (which terms
shall not restrict the license rights granted to You hereunder, but may contain additional rights).
1.3 Audit Rights. You agree that, unless such right is waived in writing by Elasticsearch, Elasticsearch shall have the
right, upon fifteen (15) days notice to You, to audit Your use of the Software for compliance with any quantitative
limitations on Your use of the Software that are set forth in the applicable Order Form. You agree to provide
Elasticsearch with the necessary access to the Software to conduct such an audit either (i) remotely, or (ii) if remote
performance is not possible, at Your facilities, during normal business hours and no more than one (1) time in any
twelve (12) month period. In the event any such audit reveals that You have used the Software in excess of the
applicable quantitative limitations, You agree to solely for Your internal business operations, a limited,
non-exclusive, non-transferable, fully paid up, right and license (without the right to grant or authorize sublicenses)
promptly pay to Elasticsearch an amount equal to the difference between the fees actually paid and the fees that You
should have paid to remain in compliance with such quantitative limitations. This Section 1.3 shall survive for a
period of two (2) years from the termination or expiration of this Agreement.
1.4 Cluster Metadata. You understand and agree that once deployed, and on a daily basis, the Software may provide
metadata to Elasticsearch about Your cluster statistics and associates that metadata with Your IP address. However, no
other information is provided to Elasticsearch by the Software, including any information about the data You process or
store in connection with your use of the Software. Instructions for disabling this feature are contained in the
Software, however leaving this feature active enables Elasticsearch to gather cluster statistics and provide an improved
level of support to You.
2. TERM AND TERMINATION
2.1 Term. Unless earlier terminated under Section 2.2 below, this Agreement shall commence on the Effective Date, and
shall continue in force for the term of the last to expire applicable license set forth in Section 1.1 above.
2.2 Termination. Either party may, upon written notice to the other party, terminate this Agreement for material breach
by the other party automatically and without any other formality, if such party has failed to cure such material breach
within thirty (30) days of receiving written notice of such material breach from the non-breaching party.
Notwithstanding the foregoing, this Agreement shall automatically terminate in the event that You intentionally breach
the scope of the license granted in Section 1.1 of this Agreement, provided that Elasticsearch reserves the right to
retroactively waive such automatic termination upon written notice to You.
2.3 Post Termination or Expiration. Upon termination or expiration of this Agreement, for any reason, You shall
promptly cease the use of the Software and Documentation and destroy (and certify to Elasticsearch in writing the fact
of such destruction), or return to Elasticsearch, all copies of the Software and Documentation then in Your possession
or under Your control.
2.4 Survival. Sections 2.3, 2.4, 3, 4 and 5 (as any such Sections may be modified by Attachment 1, if applicable) shall
survive any termination or expiration of this Agreement.
3. LIMITED WARRANTY AND DISCLAIMER OF WARRANTIES
3.1 Limited Performance Warranty. Subject to You purchasing a Subscription, Elasticsearch warrants that during the
applicable Subscription Term, the Software will perform in all material respects in accordance with the Documentation.
In the event of a breach of the foregoing warranty, Elasticsearchs sole obligation, and Your exclusive remedy shall be
for Elasticsearch to (i) correct any failure(s) of the Software to perform in all material respects in accordance with
the Documentation or (ii) if Elasticsearch is unable to provide such a correction within thirty (30) days of receipt of
notice of the applicable non-conformity, promptly refund to Customer any pre-paid, unused fees paid by You to
Elasticsearch for the applicable Subscription. The warranty set forth in this Section 3.1 does not apply if the
applicable Software or any portion thereof: (a) has been altered, except by or on behalf Elasticsearch; (b) has not been
used, installed, operated, repaired, or maintained in accordance with this Agreement and/or the Documentation; (c) has
been subjected to abnormal physical or electrical stress, misuse, negligence, or accident; or (d) is used on equipment,
products, or systems not meeting specifications identified by Elasticsearch in the Documentation. Additionally, the
warranties set forth herein only apply when notice of a warranty claim is provided to Elasticsearch within the
applicable warranty period specified herein and do not apply to any bug, defect or error caused by or attributable to
software or hardware not supplied by Elasticsearch.
3.2 Malicious Code. Elasticsearch represents and warrants that prior to making it available for delivery to You,
Elasticsearch will use standard industry practices including, without limitation, the use of an updated commercial
anti-virus program, to test the Software for Malicious Code and remove any Malicious Code it discovers. In the event of
a breach of the foregoing warranty, Elasticsearchs sole obligation, and Your exclusive remedy shall be for
Elasticsearch to replace the Software with Software that does not contain any Malicious Code.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS” WITHOUT
WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR STATUTORY
REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF
THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE SOFTWARE WILL BE UNINTERRUPTED.
4. LIMITATION OF LIABILITY
The provisions of this Section 4 apply if You have not purchased a Subscription. If you have purchased a Subscription,
then the limitations of liability set forth in the applicable Subscription Agreement will apply in lieu of those set
forth in this Section 4.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO
USE THE SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT OR
TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH THROUGH GROSS NEGLIGENCE
OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT
CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
4.2 Damages Cap. IN NO EVENT SHALL ELASTICSEARCHS OR ITS LICENSORS AGGREGATE, CUMULATIVE LIABILITY UNDER THIS
AGREEMENT EXCEED ONE THOUSAND DOLLARS ($1,000).
4.3 YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A REASONABLE ALLOCATION OF THE RISK BETWEEN
THE PARTIES AND WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL
PURPOSE.
5. DEFINITIONS
The following terms have the meanings ascribed:
5.1 “License” means a limited, non-exclusive, non-transferable, fully paid up, right and license (without the right to
grant or authorize sublicenses) solely for Your internal business operations to (i) install and use, in object code
format, the applicable Eligible Features and Functions of the Software, (ii) use, and distribute internally a reasonable
number of copies of the Documentation, provided that You must include on such copies all Marks and Notices; (iii) permit
Contractors to use the Software and Documentation as set forth in (i) and (ii) above, provided that such use must be
solely for Your benefit, and You shall be responsible for all acts and omissions of such Contractors in connection with
their use of the Software that are contrary to the terms and conditions of this Agreement..
5.2 “License Key” means an alphanumeric code that enables the Eligible Features and Functions of the Software.
5.3 “Basic Version” means that version of the Software available for use without the purchase of a Qualifying
Subscription, but which does require Registration.
5.4 “Contractor” means third party contractors performing services on Your behalf.
5.5 “Documentation” means the published end user documentation provided by Elasticsearch with the Software.
5.6 “Eligible Features and Functions” means those features and functions of the Software that are eligible for use with
respect to a particular version of the Software or level of the Subscription. A list of the Eligible Features and
Functions that correspond to each version of the Software and Subscription levels may be found at
https://www.elastic.co/subscriptions.
5.7 “Malicious Code” means any code that is designed to harm, or otherwise disrupt in any unauthorized manner, the
operation of a recipients computer programs or computer systems or destroy or damage recipients data. For clarity,
Malicious Code shall not include any software bugs or errors handled through Support Services, or any standard features
of functions of the Software and/or any License Key that are intended to enforce the temporal and other limitations on
the scope of the use of the Software to the scope of the license purchased by You.
5.8 “Marks and Notices” means all Elasticsearch trademarks, trade names, logos and notices present on the Documentation
as originally provided by Elasticsearch.
5.9 “Registration” means Elasticsearchs then-current process under which You may register Your use of the Software with
Elasticsearch by providing certain information to Elasticsearch regarding your use of the Software.
5.10 “Subscription” means the right to receive Support Services and a License to the Software.
5.11 “Subscription Term” means the period of time for which You have purchased a Subscription.
5.12 “Trial Version” means that version of the Software available for use without the purchase of a Qualifying
Subscription and without Registration.
6. MISCELLANEOUS
This Agreement, including Attachment 1 hereto, which is hereby incorporated herein by this reference, completely and
exclusively states the entire agreement of the parties regarding the subject matter herein, and it supersedes, and its
terms govern, all prior proposals, agreements, or other communications between the parties, oral or written, regarding
such subject matter. For the avoidance of doubt, the parties hereby expressly acknowledge and agree that if You issue
any purchase order or similar document in connection with its purchase of a license to the Software, You will do so only
for Your internal, administrative purposes and not with the intent to provide any contractual terms. This Agreement may
not be modified except by a subsequently dated, written amendment that expressly amends this Agreement and which is
signed on behalf of Elasticsearch and You, by duly authorized representatives. If any provision hereof is held
unenforceable, this Agreement will continue without said provision and be interpreted to reflect the original intent of
the parties.
ATTACHMENT 1
ADDITIONAL TERMS AND CONDITIONS
A. The following additional terms and conditions apply to all Customers with principal offices in the United States
of America:
(1) Applicable Elasticsearch Entity. The entity providing the license is Elasticsearch, Inc., a Delaware corporation.
(2) Government Rights. The Software product is "Commercial Computer Software," as that term is defined in 48 C.F.R.
2.101, and as the term is used in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial computer
software" and "commercial computer software documentation". If acquired by or on behalf of a civilian agency, the U.S.
Government acquires this commercial computer software and/or commercial computer software documentation subject to the
terms of this Agreement, as specified in 48 C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal
Acquisition Regulation ("FAR") and its successors. If acquired by or on behalf of any agency within the Department of
Defense ("DOD"), the U.S. Government acquires this commercial computer software and/or commercial computer software
documentation subject to the terms of the Elasticsearch Software License Agreement as specified in 48 C.F.R. 227.7202-3
and 48 C.F.R. 227.7202-4 of the DOD FAR Supplement ("DFARS") and its successors, and consistent with 48 C.F.R. 227.7202.
This U.S. Government Rights clause, consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202 is in lieu of, and
supersedes, any other FAR, DFARS, or other clause or provision that addresses Government rights in computer software,
computer software documentation or technical data related to the Software under this Agreement and in any Subcontract
under which this commercial computer software and commercial computer software documentation is acquired or licensed.
(3) Export Control. You acknowledge that the goods, software and technology acquired from Elasticsearch are subject
to U.S. export control laws and regulations, including but not limited to the International Traffic In Arms Regulations
(“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export Administration Regulations ("EAR") (15 C.F.R. Parts 730-774
(2010)); the U.S. antiboycott regulations in the EAR and U.S. Department of the Treasury regulations; the economic
sanctions regulations and guidelines of the U.S. Department of the Treasury, Office of Foreign Assets Control, and the
USA Patriot Act (Title III of Pub. L. 107-56, signed into law October 26, 2001), as amended. You are now and will
remain in the future compliant with all such export control laws and regulations, and will not export, re-export,
otherwise transfer any Elasticsearch goods, software or technology or disclose any Elasticsearch software or technology
to any person contrary to such laws or regulations. You acknowledge that remote access to the Software may in certain
circumstances be considered a re-export of Software, and accordingly, may not be granted in contravention of U.S. export
control laws and regulations.
(4) Governing Law, Jurisdiction and Venue.
(a) Customers in California. If Customer is located in California (as determined by the Customer address on the
applicable Order Form, or for a trial license under 1.1(a), the location of person who installed the Software), this
Agreement will be governed by the laws of the State of California, without regard to its conflict of laws principles,
and all suits hereunder will be brought solely in Federal Court for the Northern District of California, or if that
court lacks subject matter jurisdiction, in any California State Court located in Santa Clara County.
(b) Customers Outside of California. If Customer is located anywhere other than California (as determined by the
Customer address on the applicable Order Form, or for a trial license under 1.1(a), the location of person who installed
the Software), this Agreement will be governed by the laws of the State of Delaware, without regard to its conflict of
laws principles, and all suits hereunder will be brought solely in Federal Court for the District of Delaware, or if
that court lacks subject matter jurisdiction, in any Delaware State Court located in Wilmington, Delaware.
(c) All Customers. This Agreement shall not be governed by the 1980 UN Convention on Contracts for the International
Sale of Goods. The parties hereby irrevocably waive any and all claims and defenses either might otherwise have in any
action or proceeding in any of the applicable courts set forth in (a) or (b) above, based upon any alleged lack of
personal jurisdiction, improper venue, forum non conveniens, or any similar claim or defense.
(d) Equitable Relief. A breach or threatened breach, by either party of Section 4 may cause irreparable harm for
which the non-breaching party shall be entitled to seek injunctive relief without being required to post a bond.
B. The following additional terms and conditions apply to all Customers with principal offices in Canada:
(1) Applicable Elasticsearch Entity. The entity providing the license is Elasticsearch B.C. Ltd., a corporation
incorporated under laws of the Province of British Columbia.
(2) Export Control. You acknowledge that the goods, software and technology acquired from Elasticsearch are subject
to the restrictions and controls set out in Section A(3) above as well as those imposed by the Export and Import Permits
Act (Canada) and the regulations thereunder and that you will comply with all applicable laws and regulations. Without
limitation, You acknowledge that the Marvel Software, or any portion thereof, will not be exported: (a) to any country
on Canada's Area Control List; (b) to any country subject to UN Security Council embargo or action; or (c) contrary to
Canada's Export Control List Item 5505. You are now and will remain in the future compliant with all such export control
laws and regulations, and will not export, re-export, otherwise transfer any Elasticsearch goods, software or technology
or disclose any Elasticsearch software or technology to any person contrary to such laws or regulations. You will not
export or re-export the Marvel Software, or any portion thereof, directly or indirectly, in violation of the Canadian
export administration laws and regulations to any country or end user, or to any end user who you know or have reason to
know will utilize them in the design, development or production of nuclear, chemical or biological weapons. You further
acknowledge that the Marvel Software product may include technical data subject to such Canadian export regulations.
Elasticsearch does not represent that the Marvel Software is appropriate or available for use in all countries.
Elasticsearch prohibits accessing materials from countries or states where contents are illegal. You are using the
Marvel Software on your own initiative and you are responsible for compliance with all applicable laws. You hereby agree
to indemnify Elasticsearch and its affiliates from any claims, actions, liability or expenses (including reasonable
lawyers' fees) resulting from Your failure to act in accordance with the acknowledgements, agreements, and
representations in this Section B(2).
(3) Governing Law and Dispute Resolution. This Agreement shall be governed by the Province of Ontario and the
federal laws of Canada applicable therein without regard to conflict of laws provisions. The parties hereby irrevocably
waive any and all claims and defenses either might otherwise have in any such action or proceeding in any of such courts
based upon any alleged lack of personal jurisdiction, improper venue, forum non conveniens or any similar claim or
defense. Any dispute, claim or controversy arising out of or relating to this Agreement or the existence, breach,
termination, enforcement, interpretation or validity thereof, including the determination of the scope or applicability
of this agreement to arbitrate, (each, a “Dispute”), which the parties are unable to resolve after good faith
negotiations, shall be submitted first to the upper management level of the parties. The parties, through their upper
management level representatives shall meet within thirty (30) days of the Dispute being referred to them and if the
parties are unable to resolve such Dispute within thirty (30) days of meeting, the parties agree to seek to resolve the
Dispute through mediation with ADR Chambers in the City of Toronto, Ontario, Canada before pursuing any other
proceedings. The costs of the mediator shall be shared equally by the parties. If the Dispute has not been resolved
within thirty (30) days of the notice to desire to mediate, any party may terminate the mediation and proceed to
arbitration and the matter shall be referred to and finally resolved by arbitration at ADR Chambers pursuant to the
general ADR Chambers Rules for Arbitration in the City of Toronto, Ontario, Canada. The arbitration shall proceed in
accordance with the provisions of the Arbitration Act (Ontario). The arbitral panel shall consist of three (3)
arbitrators, selected as follows: each party shall appoint one (1) arbitrator; and those two (2) arbitrators shall
discuss and select a chairman. If the two (2) party-appointed arbitrators are unable to agree on the chairman, the
chairman shall be selected in accordance with the applicable rules of the arbitration body. Each arbitrator shall be
independent of each of the parties. The arbitrators shall have the authority to grant specific performance and to
allocate between the parties the costs of arbitration (including service fees, arbitrator fees and all other fees
related to the arbitration) in such equitable manner as the arbitrators may determine. The prevailing party in any
arbitration shall be entitled to receive reimbursement of its reasonable expenses incurred in connection therewith.
Judgment upon the award so rendered may be entered in a court having jurisdiction or application may be made to such
court for judicial acceptance of any award and an order of enforcement, as the case may be. Notwithstanding the
foregoing, Elasticsearch shall have the right to institute an action in a court of proper jurisdiction for preliminary
injunctive relief pending a final decision by the arbitrator, provided that a permanent injunction and damages shall
only be awarded by the arbitrator. The language to be used in the arbitral proceedings shall be English.
(4) Language. Any translation of this Agreement is done for local requirements and in the event of a dispute
between the English and any non-English version, the English version of this Agreement shall govern. At the request of
the parties, the official language of this Agreement and all communications and documents relating hereto is the English
language, and the English-language version shall govern all interpretation of the Agreement. À la demande des parties,
la langue officielle de la présente convention ainsi que toutes communications et tous documents s'y rapportant est la
langue anglaise, et la version anglaise est celle qui régit toute interprétation de la présente convention.
(5) Warranty Disclaimer. For Customers with principal offices in the Province of Québec, the following new sentence
is to be added to the end of Section 3.3: “SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS OR EXCLUSIONS OF CERTAIN TYPES OF
DAMAGES AND/OR WARRANTIES AND CONDITIONS. THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SET FORTH IN THIS AGREEMENT SHALL
NOT APPLY IF AND ONLY IF AND TO THE EXTENT THAT THE LAWS OF A COMPETENT JURISDICTION REQUIRE LIABILITIES BEYOND AND
DESPITE THESE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS.”
(6) Limitation of Liability. For Customers with principal offices in the Province of Québec, the following new
sentence is to be added to the end of Section 4.1: “SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS OR EXCLUSIONS OF CERTAIN
TYPES OF DAMAGES AND/OR WARRANTIES AND CONDITIONS. THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SET FORTH IN THIS
AGREEMENT SHALL NOT APPLY IF AND ONLY IF AND TO THE EXTENT THAT THE LAWS OF A COMPETENT JURISDICTION REQUIRE LIABILITIES
BEYOND AND DESPITE THESE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS.”
C. The following additional terms and conditions apply to all Customers with principal offices outside of the United
States of America and Canada:
(1) Applicable Elasticsearch Entity. The entity providing the license in Germany is Elasticsearch Gmbh; in France is
Elasticsearch SARL, in the United Kingdom is Elasticsearch Ltd, in Australia is Elasticsearch Pty Ltd., in Japan is
Elasticsearch KK, in Sweden is Elasticsearch AB, in Norway is Elasticsearch AS and in all other countries is
Elasticsearch BV.
(2) Choice of Law. This Agreement shall be governed by and construed in accordance with the laws of the State of New
York, without reference to or application of choice of law rules or principles. Notwithstanding any choice of law
provision or otherwise, the Uniform Computer Information Transactions Act (UCITA) and the United Nations Convention on
the International Sale of Goods shall not apply.
(3) Arbitration. Any dispute, claim or controversy arising out of or relating to this Agreement or the existence,
breach, termination, enforcement, interpretation or validity thereof, including the determination of the scope or
applicability of this agreement to arbitrate, (each, a “Dispute”) shall be referred to and finally resolved by
arbitration under the rules and at the location identified below. The arbitral panel shall consist of three (3)
arbitrators, selected as follows: each party shall appoint one (1) arbitrator; and those two (2) arbitrators shall
discuss and select a chairman. If the two party-appointed arbitrators are unable to agree on the chairman, the chairman
shall be selected in accordance with the applicable rules of the arbitration body. Each arbitrator shall be independent
of each of the parties. The arbitrators shall have the authority to grant specific performance and to allocate between
the parties the costs of arbitration (including service fees, arbitrator fees and all other fees related to the
arbitration) in such equitable manner as the arbitrators may determine. The prevailing party in any arbitration shall
be entitled to receive reimbursement of its reasonable expenses incurred in connection therewith. Judgment upon the
award so rendered may be entered in a court having jurisdiction or application may be made to such court for judicial
acceptance of any award and an order of enforcement, as the case may be. Notwithstanding the foregoing, Elasticsearch
shall have the right to institute an action in a court of proper jurisdiction for preliminary injunctive relief pending
a final decision by the arbitrator, provided that a permanent injunction and damages shall only be awarded by the
arbitrator. The language to be used in the arbitral proceedings shall be English.
In addition, the following terms only apply to Customers with principal offices within Europe, the Middle East or Africa
(EMEA):
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under the London
Court of International Arbitration (“LCIA”) Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York, USA. The seat, or legal place, of
arbitration shall be London, England.
(b) In addition, the following terms only apply to Customers with principal offices within Asia Pacific, Australia &
New Zealand:
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under the Rules of
Conciliation and Arbitration of the International Chamber of Commerce (“ICC”) in force on the date when the notice of
arbitration is submitted in accordance with such Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York, USA. The seat, or legal place, of
arbitration shall be Singapore.
(c) In addition, the following terms only apply to Customers with principal offices within the Americas (excluding
North America):
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under
International Dispute Resolution Procedures of the American Arbitration Association (“AAA”) in force on the date when
the notice of arbitration is submitted in accordance with such Procedures (which Procedures are deemed to be
incorporated by reference into this clause) on the basis that the governing law is the law of the State of New York,
USA. The seat, or legal place, of arbitration shall be New York, New York, USA.
(4) In addition, for Customers with principal offices within the UK, the following new sentence is added to the end
of Section 4.1:
Nothing in this Agreement shall have effect so as to limit or exclude a partys liability for death or personal injury
caused by negligence or for fraud including fraudulent misrepresentation and this Section 4.1 shall take effect subject
to this provision.
(5) In addition, for Customers with principal offices within France, Sections 1.2, 3 and 4.1 of the Agreement are
deleted and replaced with the following new Sections 1.2, 3.3 and 4.1:
1.2 Reservation of Rights; Restrictions. Elasticsearch owns all right title and interest in and to the Software and
any derivative works thereof, and except as expressly set forth in Section 1.1 above, no other license to the Software
is granted to You by implication, or otherwise. You agree not to prepare derivative works from, modify, copy or use the
Software in any manner except as expressly permitted in this Agreement; provided that You may copy the Software for
archival purposes, only where such software is provided on a non-durable medium; and You may decompile the Software,
where necessary for interoperability purposes and where necessary for the correction of errors making the software unfit
for its intended purpose, if such right is not reserved by Elasticsearch as editor of the Software. Pursuant to article
L122-6-1 of the French intellectual property code, Elasticsearch reserves the right to correct any bugs as necessary for
the Software to serve its intended purpose. You agree not to: (i) transfer, sell, rent, lease, distribute, sublicense,
loan or otherwise transfer the Software in whole or in part to any third party; (ii) use the Software for providing
time-sharing services, any software-as-a-service offering (“SaaS”), service bureau services or as part of an application
services provider or other service offering; (iii) alter or remove any proprietary notices in the Software; or (iv) make
available to any third party any analysis of the results of operation of the Software, including benchmarking results,
without the prior written consent of Elasticsearch.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS”
WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR
STATUTORY REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE WITH
RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER, ELASTICSEARCH DOES
NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE SOFTWARE WILL BE
UNINTERRUPTED.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT OR
UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE, OR THE
PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT,
INCLUDING NEGLIGENCE. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH, THROUGH
GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1, OR IN CASE OF
DEATH OR PERSONAL INJURY.
(6) In addition, for Customers with principal offices within Australia, Sections 4.1, 4.2 and 4.3 of the Agreement
are deleted and replaced with the following new Sections 4.1, 4.2 and 4.3:
4.1 Disclaimer of Certain Damages. Subject to clause 4.3, a party is not liable for Consequential Loss however
caused (including by the negligence of that party) suffered or incurred by the other party in connection with this
agreement. “Consequential Loss” means loss of revenues, loss of reputation, indirect loss, loss of profits,
consequential loss, loss of actual or anticipated savings, indirect loss, lost opportunities, including opportunities to
enter into arrangements with third parties, loss or damage in connection with claims against by third parties, or loss
or corruption or data.
4.2 Damages Cap. SUBJECT TO CLAUSES 4.1 AND 4.3, ANY LIABILITY OF ELASTICSEARCH FOR ANY LOSS OR DAMAGE, HOWEVER
CAUSED (INCLUDING BY THE NEGLIGENCE OF ELASTICSEARCH), SUFFERED BY YOU IN CONNECTION WITH THIS AGREEMENT IS LIMITED TO
THE AMOUNT YOU PAID, IN THE TWELVE (12) MONTHS IMMEDIATELY PRIOR TO THE EVENT GIVING RISE TO LIABILITY, UNDER THE
ELASTICSEARCH SUPPORT SERVICES AGREEMENT IN CONNECTION WITH WHICH YOU OBTAINED THE LICENSE TO USE THE SOFTWARE. THE
LIMITATION SET OUT IN THIS SECTION 4.2 IS AN AGGREGATE LIMIT FOR ALL CLAIMS, WHENEVER MADE.
4.3 Limitation and Disclaimer Exceptions. If the Competition and Consumer Act 2010 (Cth) or any other legislation or
any other legislation states that there is a guarantee in relation to any good or service supplied by Elasticsearch in
connection with this agreement, and Elasticsearchs liability for failing to comply with that guarantee cannot be
excluded but may be limited, Sections 4.1 and 4.2 do not apply to that liability and instead Elasticsearchs liability
for such failure is limited (at Elasticsearchs election) to, in the case of a supply of goods, the Elasticsearch
replacing the goods or supplying equivalent goods or repairing the goods, or in the case of a supply of services,
Elasticsearch supplying the services again or paying the cost of having the services supplied again.
(7) In addition, for Customers with principal offices within Japan, Sections 1.2, 3 and 4.1 of the Agreement are
deleted and replaced with the following new Sections 1.2, 3.3 and 4.1:
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You, Elasticsearch owns all right title and
interest in and to the Software and any derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Software is granted to You by implication or otherwise. You agree not to: (i) prepare
derivative works from, modify, copy or use the Software in any manner except as expressly permitted in this Agreement or
applicable law; (ii) transfer, sell, rent, lease, distribute, sublicense, loan or otherwise transfer the Software in
whole or in part to any third party; (iii) use the Software for providing time-sharing services, any
software-as-a-service offering (“SaaS”), service bureau services or as part of an application services provider or other
service offering; (iv) alter or remove any proprietary notices in the Software; or (v) make available to any third party
any analysis of the results of operation of the Software, including benchmarking results, without the prior written
consent of Elasticsearch.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS”
WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR
STATUTORY REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF
THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE SOFTWARE WILL BE UNINTERRUPTED.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
SPECIALINDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE
OR INABILITY TO USE THE SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH
THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY
OTHER LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.

View File

@ -1,6 +0,0 @@
Elasticsearch License
Copyright 2009-2015 Elastic
---
This product includes software developed by The Apache Software
Foundation (http://www.apache.org/).

View File

@ -1,439 +0,0 @@
SOFTWARE END USER LICENSE AGREEMENT
READ THIS AGREEMENT CAREFULLY, WHICH CONSTITUTES A LEGALLY BINDING AGREEMENT AND GOVERNS YOUR USE OF
ELASTICSEARCHS PROPRIETARY SOFTWARE. BY INSTALLING AND/OR USING SUCH SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO THE
TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH SUCH TERMS AND CONDITIONS, YOU MAY NOT
INSTALL OR USE ANY OF THE SOFTWARE.
This END USER LICENSE AGREEMENT (this “Agreement") is entered into by and between the applicable Elasticsearch
entity referenced in Attachment 1 hereto (“Elasticsearch”) and the person or entity (“You”) that has downloaded any of
Elasticsearchs proprietary software to which this Agreement is attached or in connection with which this Agreement is
presented to You (collectively, the “Software”). This Agreement is effective upon the earlier of the date on the
commencement of any License granted pursuant to Section 1.1. below (as applicable, the “Effective Date”).
1. SOFTWARE LICENSE AND RESTRICTIONS
1.1 License Grants.
(a) Trial Version License. Subject to the terms and conditions of this Agreement, Elasticsearch agrees to
grant, and does hereby grant to You, for a period of thirty (30) days from the date on which You first install the
Software (the “Trial Term”), a License to the to use the Eligible Features and Functions of the Software that are
applicable to the Trial Version of the Software. You understand and agree that upon the expiration of a Trial Term,
You will no longer be able to use the Software, unless you either (i) purchase a Subscription, in which case You will
receive a License under Section 1.1(b) below to use the Eligible Features and Functions of the Software that are
applicable to the Subscription that You purchase, (ii) undertake the Registration of Your use of the Software with
Elasticsearch, in which case You will receive a License under Section 1.1(c) below to the Basic Version of the Software
or (iii) obtain from Elasticsearch written consent (e-mail sufficient) to extend the Trial Term, which may be granted by
Elasticsearch in its sole and absolute discretion.
(b) Subscription License. Subject to the terms and conditions of this Agreement and complete payment of any and
all applicable Subscription fees, Elasticsearch agrees to grant, and does hereby grant to You during the Subscription
Term, and for the restricted scope of this Agreement, a License (i) to use the Eligible Features and Functions of the
Software that are applicable to the Subscription level that You have purchased, (ii) for the number of Nodes (as defined
in the Elasticsearch Support Services Policy) and (iii) for the specific project for which you have purchased a
Subscription. The level of Subscription, the number of Nodes and specific project for which you have purchased such
Subscription, are set forth on the applicable ordering document entered into by Elasticsearch and You for the purchase
of the applicable Subscription (“Order Form”).
(c) Basic Version License. Subject to the terms and conditions of this Agreement, and in consideration of the
Registration of Your use the Software, Elasticsearch agrees to grant, and does hereby grant to You, for a period of one
(1) year from the date of Registration (“Basic Term”), a License to use the Eligible Features and Functions of the
Software that are applicable to the Basic Version of the Software.
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You, Elasticsearch owns all right title and
interest in and to the Software and any derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Software is granted to You by implication, estoppel or otherwise. You agree not to: (i)
reverse engineer or decompile, decrypt, disassemble or otherwise reduce any Software or any portion thereof to
human-readable form, except and only to the extent any such restriction is prohibited by applicable law, (ii) deploy the
Software on more Nodes (as defined in Elasticsearchs Support Services Policy) than are permitted under the applicable
License grant in Section 1.1 above (iii) where You have purchased a Subscription, use the Software in connection with
any project other than the project for which you have purchased such Subscription, as identified on the applicable Order
Form, (iv) prepare derivative works from, modify, copy or use the Software in any manner except as expressly permitted
in this Agreement; (v) except as expressly permitted in Section 1.1 above, transfer, sell, rent, lease, distribute,
sublicense, loan or otherwise transfer the Software in whole or in part to any third party; (vi) except as may be
expressly permitted on an applicable Order Form, use the Software for providing time-sharing services, any
software-as-a-service offering (“SaaS”), service bureau services or as part of an application services provider or other
service offering; (vii) circumvent the limitations on use of the Software that are imposed or preserved by any License
Key, (viii) alter or remove any proprietary notices in the Software; or (ix) make available to any third party any
analysis of the results of operation of the Software, including benchmarking results, without the prior written consent
of Elasticsearch. The Software may contain or be provided with open source libraries, components, utilities and other
open source software (collectively, “Open Source Software”), which Open Source Software may have applicable license
terms as identified on a website designated by Elasticsearch or otherwise provided with the Software or Documentation.
Notwithstanding anything to the contrary herein, use of the Open Source Software shall be subject to the license terms
and conditions applicable to such Open Source Software, to the extent required by the applicable licensor (which terms
shall not restrict the license rights granted to You hereunder, but may contain additional rights).
1.3 Audit Rights. You agree that, unless such right is waived in writing by Elasticsearch, Elasticsearch shall have the
right, upon fifteen (15) days notice to You, to audit Your use of the Software for compliance with any quantitative
limitations on Your use of the Software that are set forth in the applicable Order Form. You agree to provide
Elasticsearch with the necessary access to the Software to conduct such an audit either (i) remotely, or (ii) if remote
performance is not possible, at Your facilities, during normal business hours and no more than one (1) time in any
twelve (12) month period. In the event any such audit reveals that You have used the Software in excess of the
applicable quantitative limitations, You agree to solely for Your internal business operations, a limited,
non-exclusive, non-transferable, fully paid up, right and license (without the right to grant or authorize sublicenses)
promptly pay to Elasticsearch an amount equal to the difference between the fees actually paid and the fees that You
should have paid to remain in compliance with such quantitative limitations. This Section 1.3 shall survive for a
period of two (2) years from the termination or expiration of this Agreement.
1.4 Cluster Metadata. You understand and agree that once deployed, and on a daily basis, the Software may provide
metadata to Elasticsearch about Your cluster statistics and associates that metadata with Your IP address. However, no
other information is provided to Elasticsearch by the Software, including any information about the data You process or
store in connection with your use of the Software. Instructions for disabling this feature are contained in the
Software, however leaving this feature active enables Elasticsearch to gather cluster statistics and provide an improved
level of support to You.
2. TERM AND TERMINATION
2.1 Term. Unless earlier terminated under Section 2.2 below, this Agreement shall commence on the Effective Date, and
shall continue in force for the term of the last to expire applicable license set forth in Section 1.1 above.
2.2 Termination. Either party may, upon written notice to the other party, terminate this Agreement for material breach
by the other party automatically and without any other formality, if such party has failed to cure such material breach
within thirty (30) days of receiving written notice of such material breach from the non-breaching party.
Notwithstanding the foregoing, this Agreement shall automatically terminate in the event that You intentionally breach
the scope of the license granted in Section 1.1 of this Agreement, provided that Elasticsearch reserves the right to
retroactively waive such automatic termination upon written notice to You.
2.3 Post Termination or Expiration. Upon termination or expiration of this Agreement, for any reason, You shall
promptly cease the use of the Software and Documentation and destroy (and certify to Elasticsearch in writing the fact
of such destruction), or return to Elasticsearch, all copies of the Software and Documentation then in Your possession
or under Your control.
2.4 Survival. Sections 2.3, 2.4, 3, 4 and 5 (as any such Sections may be modified by Attachment 1, if applicable) shall
survive any termination or expiration of this Agreement.
3. LIMITED WARRANTY AND DISCLAIMER OF WARRANTIES
3.1 Limited Performance Warranty. Subject to You purchasing a Subscription, Elasticsearch warrants that during the
applicable Subscription Term, the Software will perform in all material respects in accordance with the Documentation.
In the event of a breach of the foregoing warranty, Elasticsearchs sole obligation, and Your exclusive remedy shall be
for Elasticsearch to (i) correct any failure(s) of the Software to perform in all material respects in accordance with
the Documentation or (ii) if Elasticsearch is unable to provide such a correction within thirty (30) days of receipt of
notice of the applicable non-conformity, promptly refund to Customer any pre-paid, unused fees paid by You to
Elasticsearch for the applicable Subscription. The warranty set forth in this Section 3.1 does not apply if the
applicable Software or any portion thereof: (a) has been altered, except by or on behalf Elasticsearch; (b) has not been
used, installed, operated, repaired, or maintained in accordance with this Agreement and/or the Documentation; (c) has
been subjected to abnormal physical or electrical stress, misuse, negligence, or accident; or (d) is used on equipment,
products, or systems not meeting specifications identified by Elasticsearch in the Documentation. Additionally, the
warranties set forth herein only apply when notice of a warranty claim is provided to Elasticsearch within the
applicable warranty period specified herein and do not apply to any bug, defect or error caused by or attributable to
software or hardware not supplied by Elasticsearch.
3.2 Malicious Code. Elasticsearch represents and warrants that prior to making it available for delivery to You,
Elasticsearch will use standard industry practices including, without limitation, the use of an updated commercial
anti-virus program, to test the Software for Malicious Code and remove any Malicious Code it discovers. In the event of
a breach of the foregoing warranty, Elasticsearchs sole obligation, and Your exclusive remedy shall be for
Elasticsearch to replace the Software with Software that does not contain any Malicious Code.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS” WITHOUT
WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR STATUTORY
REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF
THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE SOFTWARE WILL BE UNINTERRUPTED.
4. LIMITATION OF LIABILITY
The provisions of this Section 4 apply if You have not purchased a Subscription. If you have purchased a Subscription,
then the limitations of liability set forth in the applicable Subscription Agreement will apply in lieu of those set
forth in this Section 4.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO
USE THE SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT OR
TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH THROUGH GROSS NEGLIGENCE
OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT
CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
4.2 Damages Cap. IN NO EVENT SHALL ELASTICSEARCHS OR ITS LICENSORS AGGREGATE, CUMULATIVE LIABILITY UNDER THIS
AGREEMENT EXCEED ONE THOUSAND DOLLARS ($1,000).
4.3 YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A REASONABLE ALLOCATION OF THE RISK BETWEEN
THE PARTIES AND WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL
PURPOSE.
5. DEFINITIONS
The following terms have the meanings ascribed:
5.1 “License” means a limited, non-exclusive, non-transferable, fully paid up, right and license (without the right to
grant or authorize sublicenses) solely for Your internal business operations to (i) install and use, in object code
format, the applicable Eligible Features and Functions of the Software, (ii) use, and distribute internally a reasonable
number of copies of the Documentation, provided that You must include on such copies all Marks and Notices; (iii) permit
Contractors to use the Software and Documentation as set forth in (i) and (ii) above, provided that such use must be
solely for Your benefit, and You shall be responsible for all acts and omissions of such Contractors in connection with
their use of the Software that are contrary to the terms and conditions of this Agreement..
5.2 “License Key” means an alphanumeric code that enables the Eligible Features and Functions of the Software.
5.3 “Basic Version” means that version of the Software available for use without the purchase of a Qualifying
Subscription, but which does require Registration.
5.4 “Contractor” means third party contractors performing services on Your behalf.
5.5 “Documentation” means the published end user documentation provided by Elasticsearch with the Software.
5.6 “Eligible Features and Functions” means those features and functions of the Software that are eligible for use with
respect to a particular version of the Software or level of the Subscription. A list of the Eligible Features and
Functions that correspond to each version of the Software and Subscription levels may be found at
https://www.elastic.co/subscriptions.
5.7 “Malicious Code” means any code that is designed to harm, or otherwise disrupt in any unauthorized manner, the
operation of a recipients computer programs or computer systems or destroy or damage recipients data. For clarity,
Malicious Code shall not include any software bugs or errors handled through Support Services, or any standard features
of functions of the Software and/or any License Key that are intended to enforce the temporal and other limitations on
the scope of the use of the Software to the scope of the license purchased by You.
5.8 “Marks and Notices” means all Elasticsearch trademarks, trade names, logos and notices present on the Documentation
as originally provided by Elasticsearch.
5.9 “Registration” means Elasticsearchs then-current process under which You may register Your use of the Software with
Elasticsearch by providing certain information to Elasticsearch regarding your use of the Software.
5.10 “Subscription” means the right to receive Support Services and a License to the Software.
5.11 “Subscription Term” means the period of time for which You have purchased a Subscription.
5.12 “Trial Version” means that version of the Software available for use without the purchase of a Qualifying
Subscription and without Registration.
6. MISCELLANEOUS
This Agreement, including Attachment 1 hereto, which is hereby incorporated herein by this reference, completely and
exclusively states the entire agreement of the parties regarding the subject matter herein, and it supersedes, and its
terms govern, all prior proposals, agreements, or other communications between the parties, oral or written, regarding
such subject matter. For the avoidance of doubt, the parties hereby expressly acknowledge and agree that if You issue
any purchase order or similar document in connection with its purchase of a license to the Software, You will do so only
for Your internal, administrative purposes and not with the intent to provide any contractual terms. This Agreement may
not be modified except by a subsequently dated, written amendment that expressly amends this Agreement and which is
signed on behalf of Elasticsearch and You, by duly authorized representatives. If any provision hereof is held
unenforceable, this Agreement will continue without said provision and be interpreted to reflect the original intent of
the parties.
ATTACHMENT 1
ADDITIONAL TERMS AND CONDITIONS
A. The following additional terms and conditions apply to all Customers with principal offices in the United States
of America:
(1) Applicable Elasticsearch Entity. The entity providing the license is Elasticsearch, Inc., a Delaware corporation.
(2) Government Rights. The Software product is "Commercial Computer Software," as that term is defined in 48 C.F.R.
2.101, and as the term is used in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial computer
software" and "commercial computer software documentation". If acquired by or on behalf of a civilian agency, the U.S.
Government acquires this commercial computer software and/or commercial computer software documentation subject to the
terms of this Agreement, as specified in 48 C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal
Acquisition Regulation ("FAR") and its successors. If acquired by or on behalf of any agency within the Department of
Defense ("DOD"), the U.S. Government acquires this commercial computer software and/or commercial computer software
documentation subject to the terms of the Elasticsearch Software License Agreement as specified in 48 C.F.R. 227.7202-3
and 48 C.F.R. 227.7202-4 of the DOD FAR Supplement ("DFARS") and its successors, and consistent with 48 C.F.R. 227.7202.
This U.S. Government Rights clause, consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202 is in lieu of, and
supersedes, any other FAR, DFARS, or other clause or provision that addresses Government rights in computer software,
computer software documentation or technical data related to the Software under this Agreement and in any Subcontract
under which this commercial computer software and commercial computer software documentation is acquired or licensed.
(3) Export Control. You acknowledge that the goods, software and technology acquired from Elasticsearch are subject
to U.S. export control laws and regulations, including but not limited to the International Traffic In Arms Regulations
(“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export Administration Regulations ("EAR") (15 C.F.R. Parts 730-774
(2010)); the U.S. antiboycott regulations in the EAR and U.S. Department of the Treasury regulations; the economic
sanctions regulations and guidelines of the U.S. Department of the Treasury, Office of Foreign Assets Control, and the
USA Patriot Act (Title III of Pub. L. 107-56, signed into law October 26, 2001), as amended. You are now and will
remain in the future compliant with all such export control laws and regulations, and will not export, re-export,
otherwise transfer any Elasticsearch goods, software or technology or disclose any Elasticsearch software or technology
to any person contrary to such laws or regulations. You acknowledge that remote access to the Software may in certain
circumstances be considered a re-export of Software, and accordingly, may not be granted in contravention of U.S. export
control laws and regulations.
(4) Governing Law, Jurisdiction and Venue.
(a) Customers in California. If Customer is located in California (as determined by the Customer address on the
applicable Order Form, or for a trial license under 1.1(a), the location of person who installed the Software), this
Agreement will be governed by the laws of the State of California, without regard to its conflict of laws principles,
and all suits hereunder will be brought solely in Federal Court for the Northern District of California, or if that
court lacks subject matter jurisdiction, in any California State Court located in Santa Clara County.
(b) Customers Outside of California. If Customer is located anywhere other than California (as determined by the
Customer address on the applicable Order Form, or for a trial license under 1.1(a), the location of person who installed
the Software), this Agreement will be governed by the laws of the State of Delaware, without regard to its conflict of
laws principles, and all suits hereunder will be brought solely in Federal Court for the District of Delaware, or if
that court lacks subject matter jurisdiction, in any Delaware State Court located in Wilmington, Delaware.
(c) All Customers. This Agreement shall not be governed by the 1980 UN Convention on Contracts for the International
Sale of Goods. The parties hereby irrevocably waive any and all claims and defenses either might otherwise have in any
action or proceeding in any of the applicable courts set forth in (a) or (b) above, based upon any alleged lack of
personal jurisdiction, improper venue, forum non conveniens, or any similar claim or defense.
(d) Equitable Relief. A breach or threatened breach, by either party of Section 4 may cause irreparable harm for
which the non-breaching party shall be entitled to seek injunctive relief without being required to post a bond.
B. The following additional terms and conditions apply to all Customers with principal offices in Canada:
(1) Applicable Elasticsearch Entity. The entity providing the license is Elasticsearch B.C. Ltd., a corporation
incorporated under laws of the Province of British Columbia.
(2) Export Control. You acknowledge that the goods, software and technology acquired from Elasticsearch are subject
to the restrictions and controls set out in Section A(3) above as well as those imposed by the Export and Import Permits
Act (Canada) and the regulations thereunder and that you will comply with all applicable laws and regulations. Without
limitation, You acknowledge that the Marvel Software, or any portion thereof, will not be exported: (a) to any country
on Canada's Area Control List; (b) to any country subject to UN Security Council embargo or action; or (c) contrary to
Canada's Export Control List Item 5505. You are now and will remain in the future compliant with all such export control
laws and regulations, and will not export, re-export, otherwise transfer any Elasticsearch goods, software or technology
or disclose any Elasticsearch software or technology to any person contrary to such laws or regulations. You will not
export or re-export the Marvel Software, or any portion thereof, directly or indirectly, in violation of the Canadian
export administration laws and regulations to any country or end user, or to any end user who you know or have reason to
know will utilize them in the design, development or production of nuclear, chemical or biological weapons. You further
acknowledge that the Marvel Software product may include technical data subject to such Canadian export regulations.
Elasticsearch does not represent that the Marvel Software is appropriate or available for use in all countries.
Elasticsearch prohibits accessing materials from countries or states where contents are illegal. You are using the
Marvel Software on your own initiative and you are responsible for compliance with all applicable laws. You hereby agree
to indemnify Elasticsearch and its affiliates from any claims, actions, liability or expenses (including reasonable
lawyers' fees) resulting from Your failure to act in accordance with the acknowledgements, agreements, and
representations in this Section B(2).
(3) Governing Law and Dispute Resolution. This Agreement shall be governed by the Province of Ontario and the
federal laws of Canada applicable therein without regard to conflict of laws provisions. The parties hereby irrevocably
waive any and all claims and defenses either might otherwise have in any such action or proceeding in any of such courts
based upon any alleged lack of personal jurisdiction, improper venue, forum non conveniens or any similar claim or
defense. Any dispute, claim or controversy arising out of or relating to this Agreement or the existence, breach,
termination, enforcement, interpretation or validity thereof, including the determination of the scope or applicability
of this agreement to arbitrate, (each, a “Dispute”), which the parties are unable to resolve after good faith
negotiations, shall be submitted first to the upper management level of the parties. The parties, through their upper
management level representatives shall meet within thirty (30) days of the Dispute being referred to them and if the
parties are unable to resolve such Dispute within thirty (30) days of meeting, the parties agree to seek to resolve the
Dispute through mediation with ADR Chambers in the City of Toronto, Ontario, Canada before pursuing any other
proceedings. The costs of the mediator shall be shared equally by the parties. If the Dispute has not been resolved
within thirty (30) days of the notice to desire to mediate, any party may terminate the mediation and proceed to
arbitration and the matter shall be referred to and finally resolved by arbitration at ADR Chambers pursuant to the
general ADR Chambers Rules for Arbitration in the City of Toronto, Ontario, Canada. The arbitration shall proceed in
accordance with the provisions of the Arbitration Act (Ontario). The arbitral panel shall consist of three (3)
arbitrators, selected as follows: each party shall appoint one (1) arbitrator; and those two (2) arbitrators shall
discuss and select a chairman. If the two (2) party-appointed arbitrators are unable to agree on the chairman, the
chairman shall be selected in accordance with the applicable rules of the arbitration body. Each arbitrator shall be
independent of each of the parties. The arbitrators shall have the authority to grant specific performance and to
allocate between the parties the costs of arbitration (including service fees, arbitrator fees and all other fees
related to the arbitration) in such equitable manner as the arbitrators may determine. The prevailing party in any
arbitration shall be entitled to receive reimbursement of its reasonable expenses incurred in connection therewith.
Judgment upon the award so rendered may be entered in a court having jurisdiction or application may be made to such
court for judicial acceptance of any award and an order of enforcement, as the case may be. Notwithstanding the
foregoing, Elasticsearch shall have the right to institute an action in a court of proper jurisdiction for preliminary
injunctive relief pending a final decision by the arbitrator, provided that a permanent injunction and damages shall
only be awarded by the arbitrator. The language to be used in the arbitral proceedings shall be English.
(4) Language. Any translation of this Agreement is done for local requirements and in the event of a dispute
between the English and any non-English version, the English version of this Agreement shall govern. At the request of
the parties, the official language of this Agreement and all communications and documents relating hereto is the English
language, and the English-language version shall govern all interpretation of the Agreement. À la demande des parties,
la langue officielle de la présente convention ainsi que toutes communications et tous documents s'y rapportant est la
langue anglaise, et la version anglaise est celle qui régit toute interprétation de la présente convention.
(5) Warranty Disclaimer. For Customers with principal offices in the Province of Québec, the following new sentence
is to be added to the end of Section 3.3: “SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS OR EXCLUSIONS OF CERTAIN TYPES OF
DAMAGES AND/OR WARRANTIES AND CONDITIONS. THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SET FORTH IN THIS AGREEMENT SHALL
NOT APPLY IF AND ONLY IF AND TO THE EXTENT THAT THE LAWS OF A COMPETENT JURISDICTION REQUIRE LIABILITIES BEYOND AND
DESPITE THESE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS.”
(6) Limitation of Liability. For Customers with principal offices in the Province of Québec, the following new
sentence is to be added to the end of Section 4.1: “SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS OR EXCLUSIONS OF CERTAIN
TYPES OF DAMAGES AND/OR WARRANTIES AND CONDITIONS. THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SET FORTH IN THIS
AGREEMENT SHALL NOT APPLY IF AND ONLY IF AND TO THE EXTENT THAT THE LAWS OF A COMPETENT JURISDICTION REQUIRE LIABILITIES
BEYOND AND DESPITE THESE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS.”
C. The following additional terms and conditions apply to all Customers with principal offices outside of the United
States of America and Canada:
(1) Applicable Elasticsearch Entity. The entity providing the license in Germany is Elasticsearch Gmbh; in France is
Elasticsearch SARL, in the United Kingdom is Elasticsearch Ltd, in Australia is Elasticsearch Pty Ltd., in Japan is
Elasticsearch KK, in Sweden is Elasticsearch AB, in Norway is Elasticsearch AS and in all other countries is
Elasticsearch BV.
(2) Choice of Law. This Agreement shall be governed by and construed in accordance with the laws of the State of New
York, without reference to or application of choice of law rules or principles. Notwithstanding any choice of law
provision or otherwise, the Uniform Computer Information Transactions Act (UCITA) and the United Nations Convention on
the International Sale of Goods shall not apply.
(3) Arbitration. Any dispute, claim or controversy arising out of or relating to this Agreement or the existence,
breach, termination, enforcement, interpretation or validity thereof, including the determination of the scope or
applicability of this agreement to arbitrate, (each, a “Dispute”) shall be referred to and finally resolved by
arbitration under the rules and at the location identified below. The arbitral panel shall consist of three (3)
arbitrators, selected as follows: each party shall appoint one (1) arbitrator; and those two (2) arbitrators shall
discuss and select a chairman. If the two party-appointed arbitrators are unable to agree on the chairman, the chairman
shall be selected in accordance with the applicable rules of the arbitration body. Each arbitrator shall be independent
of each of the parties. The arbitrators shall have the authority to grant specific performance and to allocate between
the parties the costs of arbitration (including service fees, arbitrator fees and all other fees related to the
arbitration) in such equitable manner as the arbitrators may determine. The prevailing party in any arbitration shall
be entitled to receive reimbursement of its reasonable expenses incurred in connection therewith. Judgment upon the
award so rendered may be entered in a court having jurisdiction or application may be made to such court for judicial
acceptance of any award and an order of enforcement, as the case may be. Notwithstanding the foregoing, Elasticsearch
shall have the right to institute an action in a court of proper jurisdiction for preliminary injunctive relief pending
a final decision by the arbitrator, provided that a permanent injunction and damages shall only be awarded by the
arbitrator. The language to be used in the arbitral proceedings shall be English.
In addition, the following terms only apply to Customers with principal offices within Europe, the Middle East or Africa
(EMEA):
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under the London
Court of International Arbitration (“LCIA”) Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York, USA. The seat, or legal place, of
arbitration shall be London, England.
(b) In addition, the following terms only apply to Customers with principal offices within Asia Pacific, Australia &
New Zealand:
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under the Rules of
Conciliation and Arbitration of the International Chamber of Commerce (“ICC”) in force on the date when the notice of
arbitration is submitted in accordance with such Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York, USA. The seat, or legal place, of
arbitration shall be Singapore.
(c) In addition, the following terms only apply to Customers with principal offices within the Americas (excluding
North America):
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under
International Dispute Resolution Procedures of the American Arbitration Association (“AAA”) in force on the date when
the notice of arbitration is submitted in accordance with such Procedures (which Procedures are deemed to be
incorporated by reference into this clause) on the basis that the governing law is the law of the State of New York,
USA. The seat, or legal place, of arbitration shall be New York, New York, USA.
(4) In addition, for Customers with principal offices within the UK, the following new sentence is added to the end
of Section 4.1:
Nothing in this Agreement shall have effect so as to limit or exclude a partys liability for death or personal injury
caused by negligence or for fraud including fraudulent misrepresentation and this Section 4.1 shall take effect subject
to this provision.
(5) In addition, for Customers with principal offices within France, Sections 1.2, 3 and 4.1 of the Agreement are
deleted and replaced with the following new Sections 1.2, 3.3 and 4.1:
1.2 Reservation of Rights; Restrictions. Elasticsearch owns all right title and interest in and to the Software and
any derivative works thereof, and except as expressly set forth in Section 1.1 above, no other license to the Software
is granted to You by implication, or otherwise. You agree not to prepare derivative works from, modify, copy or use the
Software in any manner except as expressly permitted in this Agreement; provided that You may copy the Software for
archival purposes, only where such software is provided on a non-durable medium; and You may decompile the Software,
where necessary for interoperability purposes and where necessary for the correction of errors making the software unfit
for its intended purpose, if such right is not reserved by Elasticsearch as editor of the Software. Pursuant to article
L122-6-1 of the French intellectual property code, Elasticsearch reserves the right to correct any bugs as necessary for
the Software to serve its intended purpose. You agree not to: (i) transfer, sell, rent, lease, distribute, sublicense,
loan or otherwise transfer the Software in whole or in part to any third party; (ii) use the Software for providing
time-sharing services, any software-as-a-service offering (“SaaS”), service bureau services or as part of an application
services provider or other service offering; (iii) alter or remove any proprietary notices in the Software; or (iv) make
available to any third party any analysis of the results of operation of the Software, including benchmarking results,
without the prior written consent of Elasticsearch.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS”
WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR
STATUTORY REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE WITH
RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER, ELASTICSEARCH DOES
NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE SOFTWARE WILL BE
UNINTERRUPTED.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT OR
UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE, OR THE
PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT,
INCLUDING NEGLIGENCE. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH, THROUGH
GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1, OR IN CASE OF
DEATH OR PERSONAL INJURY.
(6) In addition, for Customers with principal offices within Australia, Sections 4.1, 4.2 and 4.3 of the Agreement
are deleted and replaced with the following new Sections 4.1, 4.2 and 4.3:
4.1 Disclaimer of Certain Damages. Subject to clause 4.3, a party is not liable for Consequential Loss however
caused (including by the negligence of that party) suffered or incurred by the other party in connection with this
agreement. “Consequential Loss” means loss of revenues, loss of reputation, indirect loss, loss of profits,
consequential loss, loss of actual or anticipated savings, indirect loss, lost opportunities, including opportunities to
enter into arrangements with third parties, loss or damage in connection with claims against by third parties, or loss
or corruption or data.
4.2 Damages Cap. SUBJECT TO CLAUSES 4.1 AND 4.3, ANY LIABILITY OF ELASTICSEARCH FOR ANY LOSS OR DAMAGE, HOWEVER
CAUSED (INCLUDING BY THE NEGLIGENCE OF ELASTICSEARCH), SUFFERED BY YOU IN CONNECTION WITH THIS AGREEMENT IS LIMITED TO
THE AMOUNT YOU PAID, IN THE TWELVE (12) MONTHS IMMEDIATELY PRIOR TO THE EVENT GIVING RISE TO LIABILITY, UNDER THE
ELASTICSEARCH SUPPORT SERVICES AGREEMENT IN CONNECTION WITH WHICH YOU OBTAINED THE LICENSE TO USE THE SOFTWARE. THE
LIMITATION SET OUT IN THIS SECTION 4.2 IS AN AGGREGATE LIMIT FOR ALL CLAIMS, WHENEVER MADE.
4.3 Limitation and Disclaimer Exceptions. If the Competition and Consumer Act 2010 (Cth) or any other legislation or
any other legislation states that there is a guarantee in relation to any good or service supplied by Elasticsearch in
connection with this agreement, and Elasticsearchs liability for failing to comply with that guarantee cannot be
excluded but may be limited, Sections 4.1 and 4.2 do not apply to that liability and instead Elasticsearchs liability
for such failure is limited (at Elasticsearchs election) to, in the case of a supply of goods, the Elasticsearch
replacing the goods or supplying equivalent goods or repairing the goods, or in the case of a supply of services,
Elasticsearch supplying the services again or paying the cost of having the services supplied again.
(7) In addition, for Customers with principal offices within Japan, Sections 1.2, 3 and 4.1 of the Agreement are
deleted and replaced with the following new Sections 1.2, 3.3 and 4.1:
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You, Elasticsearch owns all right title and
interest in and to the Software and any derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Software is granted to You by implication or otherwise. You agree not to: (i) prepare
derivative works from, modify, copy or use the Software in any manner except as expressly permitted in this Agreement or
applicable law; (ii) transfer, sell, rent, lease, distribute, sublicense, loan or otherwise transfer the Software in
whole or in part to any third party; (iii) use the Software for providing time-sharing services, any
software-as-a-service offering (“SaaS”), service bureau services or as part of an application services provider or other
service offering; (iv) alter or remove any proprietary notices in the Software; or (v) make available to any third party
any analysis of the results of operation of the Software, including benchmarking results, without the prior written
consent of Elasticsearch.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS”
WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR
STATUTORY REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF
THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE SOFTWARE WILL BE UNINTERRUPTED.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
SPECIALINDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE
OR INABILITY TO USE THE SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH
THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY
OTHER LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.

View File

@ -1,6 +0,0 @@
Elasticsearch License
Copyright 2009-2015 Elastic
---
This product includes software developed by The Apache Software
Foundation (http://www.apache.org/).

View File

@ -0,0 +1,22 @@
/*
* 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.common.network;
import java.net.InetAddress;
import java.net.SocketException;
/**
* We use this class to access the package private method in NetworkUtils to resolve anyLocalAddress InetAddresses for certificate
* generation
*/
public class InetAddressHelper {
private InetAddressHelper() {}
public static InetAddress[] getAllAddresses() throws SocketException {
return NetworkUtils.getAllAddresses();
}
}

View File

@ -74,8 +74,8 @@ import org.elasticsearch.shield.rest.action.user.RestDeleteUserAction;
import org.elasticsearch.shield.rest.action.user.RestGetUsersAction;
import org.elasticsearch.shield.rest.action.user.RestPutUserAction;
import org.elasticsearch.shield.ssl.SSLModule;
import org.elasticsearch.shield.ssl.SSLSettings;
import org.elasticsearch.shield.support.OptionalStringSetting;
import org.elasticsearch.shield.ssl.SSLConfiguration;
import org.elasticsearch.shield.support.OptionalSettings;
import org.elasticsearch.shield.transport.ShieldClientTransportService;
import org.elasticsearch.shield.transport.ShieldServerTransportService;
import org.elasticsearch.shield.transport.ShieldTransportModule;
@ -106,7 +106,7 @@ public class Security {
public static final String NAME = "security";
public static final String DLS_FLS_FEATURE = "security.dls_fls";
public static final String OPT_OUT_QUERY_CACHE = "opt_out_cache";
public static final Setting<Optional<String>> USER_SETTING = OptionalStringSetting.create(setting("user"), Property.NodeScope);
public static final Setting<Optional<String>> USER_SETTING = OptionalSettings.createString(setting("user"), Property.NodeScope);
private final Settings settings;
private final boolean enabled;
@ -170,7 +170,6 @@ public class Security {
list.add(FileRolesStore.class);
list.add(Realms.class);
return list;
}
public Settings additionalSettings() {
@ -193,7 +192,7 @@ public class Security {
settingsModule.registerSetting(USER_SETTING);
// SSL settings
SSLSettings.registerSettings(settingsModule);
SSLConfiguration.Global.registerSettings(settingsModule);
// transport settings
ShieldNettyTransport.registerSettings(settingsModule);

View File

@ -147,7 +147,7 @@ public abstract class SessionFactory {
protected ServerSet serverSet(Settings settings, ClientSSLService clientSSLService, LDAPServers ldapServers) {
SocketFactory socketFactory = null;
if (ldapServers.ssl()) {
socketFactory = clientSSLService.sslSocketFactory();
socketFactory = clientSSLService.sslSocketFactory(settings.getByPrefix("ssl."));
if (settings.getAsBoolean(HOSTNAME_VERIFICATION_SETTING, true)) {
logger.debug("using encryption for LDAP connections with hostname verification");
} else {

View File

@ -11,24 +11,24 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Custom;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.TrustConfig.Reloadable.Listener;
import org.elasticsearch.watcher.ResourceWatcherService;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@ -39,34 +39,41 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public abstract class AbstractSSLService extends AbstractComponent {
private final ConcurrentHashMap<SSLSettings, SSLContext> sslContexts = new ConcurrentHashMap<>();
private final ConcurrentHashMap<SSLConfiguration, SSLContext> sslContexts = new ConcurrentHashMap<>();
private final SSLContextCacheLoader cacheLoader = new SSLContextCacheLoader();
protected Environment env;
public AbstractSSLService(Settings settings, Environment environment) {
protected SSLConfiguration globalSSLConfiguration;
protected Environment env;
protected ResourceWatcherService resourceWatcherService;
public AbstractSSLService(Settings settings, Environment environment, Global globalSSLConfiguration,
ResourceWatcherService resourceWatcherService) {
super(settings);
this.env = environment;
}
/**
* @return A SSLSocketFactory (for client-side SSL handshaking)
*/
public SSLSocketFactory sslSocketFactory() {
SSLSocketFactory socketFactory = sslContext().getSocketFactory();
return new ShieldSSLSocketFactory(socketFactory, supportedProtocols(), supportedCiphers(socketFactory.getSupportedCipherSuites(),
ciphers()));
this.globalSSLConfiguration = globalSSLConfiguration;
this.resourceWatcherService = resourceWatcherService;
}
public String[] supportedProtocols() {
return SSLSettings.Globals.SUPPORTED_PROTOCOLS_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
return globalSSLConfiguration.supportedProtocols().toArray(Strings.EMPTY_ARRAY);
}
public String[] ciphers() {
return SSLSettings.Globals.CIPHERS_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
return globalSSLConfiguration.ciphers().toArray(Strings.EMPTY_ARRAY);
}
public SSLSocketFactory sslSocketFactory(Settings settings) {
return sslSocketFactory(sslContext(settings));
}
protected SSLSocketFactory sslSocketFactory(SSLContext sslContext) {
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
return new ShieldSSLSocketFactory(socketFactory, supportedProtocols(),
supportedCiphers(socketFactory.getSupportedCipherSuites(), ciphers(), false));
}
public SSLEngine createSSLEngine() {
return createSSLEngine(Settings.EMPTY);
return createSSLEngine(globalSSLConfiguration, null, -1);
}
public SSLEngine createSSLEngine(Settings settings) {
@ -74,28 +81,46 @@ public abstract class AbstractSSLService extends AbstractComponent {
}
public SSLEngine createSSLEngine(Settings settings, String host, int port) {
String[] ciphers = SSLSettings.Globals.CIPHERS_SETTING.get(settings, this.settings).toArray(Strings.EMPTY_ARRAY);
String[] supportedProtocols = SSLSettings.Globals.SUPPORTED_PROTOCOLS_SETTING.get(settings, this.settings)
.toArray(Strings.EMPTY_ARRAY);
return createSSLEngine(sslContext(settings), ciphers, supportedProtocols, host, port);
if (settings.isEmpty()) {
return createSSLEngine(globalSSLConfiguration, host, port);
}
return createSSLEngine(sslConfiguration(settings), host, port);
}
public SSLEngine createSSLEngine(SSLConfiguration configuration, String host, int port) {
return createSSLEngine(sslContext(configuration),
configuration.ciphers().toArray(Strings.EMPTY_ARRAY),
configuration.supportedProtocols().toArray(Strings.EMPTY_ARRAY),
host, port);
}
public SSLContext sslContext() {
return sslContext(Settings.EMPTY);
return sslContext(globalSSLConfiguration);
}
protected SSLContext sslContext(Settings settings) {
SSLSettings sslSettings = sslSettings(settings);
return sslContexts.computeIfAbsent(sslSettings, (theSettings) ->
cacheLoader.load(theSettings));
public SSLContext sslContext(Settings settings) {
if (settings.isEmpty()) {
return sslContext();
}
SSLConfiguration sslConfiguration = sslConfiguration(settings);
return sslContext(sslConfiguration);
}
protected abstract SSLSettings sslSettings(Settings customSettings);
protected SSLContext sslContext(SSLConfiguration sslConfiguration) {
return sslContexts.computeIfAbsent(sslConfiguration, cacheLoader::load);
}
protected SSLConfiguration sslConfiguration(Settings customSettings) {
return new Custom(customSettings, globalSSLConfiguration);
}
protected abstract void validateSSLConfiguration(SSLConfiguration configuration);
SSLEngine createSSLEngine(SSLContext sslContext, String[] ciphers, String[] supportedProtocols, String host, int port) {
SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
try {
sslEngine.setEnabledCipherSuites(supportedCiphers(sslEngine.getSupportedCipherSuites(), ciphers));
sslEngine.setEnabledCipherSuites(supportedCiphers(sslEngine.getSupportedCipherSuites(), ciphers, false));
} catch (ElasticsearchException e) {
throw e;
} catch (Throwable t) {
@ -110,7 +135,7 @@ public abstract class AbstractSSLService extends AbstractComponent {
return sslEngine;
}
String[] supportedCiphers(String[] supportedCiphers, String[] requestedCiphers) {
String[] supportedCiphers(String[] supportedCiphers, String[] requestedCiphers, boolean log) {
List<String> requestedCiphersList = new ArrayList<>(requestedCiphers.length);
List<String> unsupportedCiphers = new LinkedList<>();
boolean found;
@ -130,58 +155,36 @@ public abstract class AbstractSSLService extends AbstractComponent {
}
if (requestedCiphersList.isEmpty()) {
throw new IllegalArgumentException("none of the ciphers [" + Arrays.asList(requestedCiphers) + "] are supported by this JVM");
throw new IllegalArgumentException("none of the ciphers " + Arrays.asList(requestedCiphers) + " are supported by this JVM");
}
if (!unsupportedCiphers.isEmpty()) {
logger.error("unsupported ciphers [{}] were requested but cannot be used in this JVM. If you are trying to use ciphers\n" +
"with a key length greater than 128 bits on an Oracle JVM, you will need to install the unlimited strength\n" +
"JCE policy files. Additionally, please ensure the PKCS11 provider is enabled for your JVM.", unsupportedCiphers);
if (log && !unsupportedCiphers.isEmpty()) {
logger.error("unsupported ciphers [{}] were requested but cannot be used in this JVM, however there are supported ciphers " +
"that will be used [{}]. If you are trying to use ciphers with a key length greater than 128 bits on an Oracle JVM, " +
"you will need to install the unlimited strength JCE policy files.", unsupportedCiphers, requestedCiphersList);
}
return requestedCiphersList.toArray(new String[requestedCiphersList.size()]);
}
protected Path resolvePath(String location) {
return env.configFile().resolve(location);
}
private class SSLContextCacheLoader {
public SSLContext load(SSLSettings sslSettings) {
public SSLContext load(SSLConfiguration sslConfiguration) {
validateSSLConfiguration(sslConfiguration);
if (logger.isDebugEnabled()) {
logger.debug("using keystore[{}], key_algorithm[{}], truststore[{}], truststore_algorithm[{}], tls_protocol[{}], " +
"session_cache_size[{}], session_cache_timeout[{}]",
sslSettings.keyStorePath, sslSettings.keyStoreAlgorithm, sslSettings.trustStorePath,
sslSettings.trustStoreAlgorithm, sslSettings.sslProtocol, sslSettings.sessionCacheSize,
sslSettings.sessionCacheTimeout);
logger.debug("using ssl settings [{}]", sslConfiguration);
}
TrustManager[] trustManagers = trustManagers(sslSettings.trustStorePath, sslSettings.trustStorePassword,
sslSettings.trustStoreAlgorithm);
KeyManager[] keyManagers = keyManagers(sslSettings.keyStorePath, sslSettings.keyStorePassword, sslSettings.keyStoreAlgorithm,
sslSettings.keyPassword);
return createSslContext(keyManagers, trustManagers, sslSettings.sslProtocol, sslSettings.sessionCacheSize,
sslSettings.sessionCacheTimeout);
}
ConfigRefreshListener configRefreshListener = new ConfigRefreshListener(sslConfiguration);
TrustManager[] trustManagers = sslConfiguration.trustConfig().trustManagers(env, resourceWatcherService, configRefreshListener);
KeyManager[] keyManagers = sslConfiguration.keyConfig().keyManagers(env, resourceWatcherService, configRefreshListener);
SSLContext sslContext = createSslContext(keyManagers, trustManagers, sslConfiguration.protocol(),
sslConfiguration.sessionCacheSize(), sslConfiguration.sessionCacheTimeout());
private KeyManager[] keyManagers(String keyStore, String keyStorePassword, String keyStoreAlgorithm, String keyPassword) {
if (keyStore == null) {
return null;
}
try {
// Load KeyStore
KeyStore ks = readKeystore(keyStore, keyStorePassword);
// Initialize KeyManagerFactory
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyStoreAlgorithm);
kmf.init(ks, keyPassword.toCharArray());
return kmf.getKeyManagers();
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
}
// check the supported ciphers and log them here
supportedCiphers(sslContext.getSupportedSSLParameters().getCipherSuites(),
sslConfiguration.ciphers().toArray(Strings.EMPTY_ARRAY), true);
return sslContext;
}
private SSLContext createSslContext(KeyManager[] keyManagers, TrustManager[] trustManagers, String sslProtocol,
@ -197,34 +200,37 @@ public abstract class AbstractSSLService extends AbstractComponent {
throw new ElasticsearchException("failed to initialize the SSLContext", e);
}
}
}
private TrustManager[] trustManagers(String trustStorePath, String trustStorePassword, String trustStoreAlgorithm) {
try {
// Load TrustStore
KeyStore ks = null;
if (trustStorePath != null) {
ks = readKeystore(trustStorePath, trustStorePassword);
}
class ConfigRefreshListener implements Listener {
// Initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
trustFactory.init(ks);
return trustFactory.getTrustManagers();
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
private final SSLConfiguration sslConfiguration;
ConfigRefreshListener(SSLConfiguration sslConfiguration) {
this.sslConfiguration = sslConfiguration;
}
@Override
public void onReload() {
SSLContext context = sslContexts.get(sslConfiguration);
if (context != null) {
invalidateSessions(context.getClientSessionContext());
invalidateSessions(context.getServerSessionContext());
}
}
private KeyStore readKeystore(String path, String password) throws Exception {
try (InputStream in = Files.newInputStream(resolvePath(path))) {
// Load TrustStore
KeyStore ks = KeyStore.getInstance("jks");
assert password != null;
ks.load(in, password.toCharArray());
return ks;
void invalidateSessions(SSLSessionContext sslSessionContext) {
Enumeration<byte[]> sessionIds = sslSessionContext.getIds();
while (sessionIds.hasMoreElements()) {
byte[] sessionId = sessionIds.nextElement();
sslSessionContext.getSession(sessionId).invalidate();
}
}
@Override
public void onFailure(Exception e) {
logger.error("failed to load updated ssl context for [{}]", e, sslConfiguration);
}
}
/**

View File

@ -0,0 +1,222 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.env.Environment;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*/
class AutoGeneratedKeyConfig extends KeyConfig {
private static final char[] PASSWORD = "changeme".toCharArray();
private final Set<InetAddress> certificateAddresses = new HashSet<>();
private final X509ExtendedKeyManager[] keyManagers;
private final X509ExtendedTrustManager[] trustManagers;
private final KeyPair keyPair;
private final Certificate caCert;
private final Exception failure;
private boolean certGenerated = false;
AutoGeneratedKeyConfig(boolean includeSystem) {
super(includeSystem, false);
Exception thrown = null;
X509ExtendedTrustManager trustManager;
Certificate caCert = null;
KeyPair keyPair = null;
try {
keyPair = CertUtils.generateKeyPair();
caCert = readCACert();
X509ExtendedTrustManager[] managers = CertUtils.trustManagers(new Certificate[] { caCert });
trustManager = managers[0];
} catch (Exception e) {
thrown = e;
trustManager = new EmptyX509TrustManager();
}
this.failure = thrown;
this.caCert = caCert;
this.keyPair = keyPair;
this.keyManagers = new X509ExtendedKeyManager[] { new ReloadableX509KeyManager(new EmptyX509KeyManager(), null) };
this.trustManagers = new X509ExtendedTrustManager[] { new ReloadableTrustManager(trustManager, null) };
}
@Override
X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) {
return keyManagers;
}
@Override
X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) {
return trustManagers;
}
@Override
void validate() {
if (failure != null) {
throw new ElasticsearchException("failed to auto generate keypair and read CA cert", failure);
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.emptyList();
}
@Override
public String toString() {
return "AutoGeneratedKeyConfig";
}
synchronized void generateCertIfNecessary(boolean resolveHostnames, String nodeName, Set<InetAddress> addresses, ESLogger logger)
throws Exception {
if (failure != null) {
throw new ElasticsearchException("failed to auto generate keypair and read CA cert", failure);
}
// we shouldn't regenerate if we have no new addresses
if (certGenerated && Sets.difference(addresses, certificateAddresses).isEmpty()) {
return;
}
this.certificateAddresses.addAll(addresses);
final PrivateKey caPrivateKey = readCAPrivateKey();
final X509Certificate signedCert =
CertUtils.generateSignedCertificate(resolveHostnames, nodeName, certificateAddresses, keyPair, caCert, caPrivateKey);
Certificate[] certChain = new Certificate[] { signedCert, caCert };
X509ExtendedKeyManager[] keyManagers = CertUtils.keyManagers(certChain, keyPair.getPrivate(), PASSWORD);
X509ExtendedTrustManager[] trustManagers = CertUtils.trustManagers(certChain);
((ReloadableX509KeyManager) this.keyManagers[0]).setKeyManager(keyManagers[0]);
((ReloadableTrustManager) this.trustManagers[0]).setTrustManager(trustManagers[0]);
this.certGenerated = true;
logMessages(signedCert, logger);
}
static Certificate readCACert() throws Exception {
try (InputStream inputStream = AutoGeneratedKeyConfig.class.getResourceAsStream("/cacert.pem");
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<Certificate> certificateList = new ArrayList<>(1);
CertUtils.readCertificates(reader, certificateList, certificateFactory);
if (certificateList.size() != 1) {
throw new IllegalStateException("expected [1] default CA certificate but found [" + certificateList.size() + "]");
}
return certificateList.get(0);
}
}
static PrivateKey readCAPrivateKey() throws Exception {
try (InputStream inputStream = AutoGeneratedKeyConfig.class.getResourceAsStream("/cakey.pem");
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
return CertUtils.readPrivateKey(reader, PASSWORD);
}
}
static void logMessages(X509Certificate signedCert, ESLogger logger) {
logger.info("auto generated a X.509 certificate and private/public key pair for SSL use. this should never be used in production " +
"as the signing certificate authority is the same for every installation of X-Pack.{}generated certificate:{}{}",
System.lineSeparator(), System.lineSeparator(), signedCert.toString());
}
private static class EmptyX509KeyManager extends X509ExtendedKeyManager {
@Override
public String[] getClientAliases(String s, Principal[] principals) {
return null;
}
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return null;
}
@Override
public String[] getServerAliases(String s, Principal[] principals) {
return null;
}
@Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return null;
}
@Override
public X509Certificate[] getCertificateChain(String s) {
return null;
}
@Override
public PrivateKey getPrivateKey(String s) {
return null;
}
}
private static class EmptyX509TrustManager extends X509ExtendedTrustManager {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
throw new CertificateException("trust nothing");
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
throw new CertificateException("trust nothing");
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
throw new CertificateException("trust nothing");
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
throw new CertificateException("trust nothing");
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
throw new CertificateException("trust nothing");
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
throw new CertificateException("trust nothing");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}

View File

@ -0,0 +1,263 @@
/*
* 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.shield.ssl;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.X509TrustedCertificateBlock;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.network.InetAddressHelper;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.env.Environment;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.ByteArrayInputStream;
import java.io.Reader;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
class CertUtils {
private static final int SERIAL_BIT_LENGTH = 20 * 8;
static final BouncyCastleProvider BC_PROV = new BouncyCastleProvider();
private CertUtils() {}
@SuppressForbidden(reason = "we don't have the environment to resolve files from when running in a transport client")
static Path resolvePath(String path, @Nullable Environment environment) {
if (environment != null) {
return environment.configFile().resolve(path);
}
return PathUtils.get(Strings.cleanPath(path));
}
static X509ExtendedKeyManager[] keyManagers(Certificate[] certificateChain, PrivateKey privateKey, char[] password) throws Exception {
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
// password must be non-null for keystore...
keyStore.setKeyEntry("key", privateKey, password, certificateChain);
return keyManagers(keyStore, password, KeyManagerFactory.getDefaultAlgorithm());
}
static X509ExtendedKeyManager[] keyManagers(KeyStore keyStore, char[] password, String algorithm) throws Exception {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore, password);
KeyManager[] keyManagers = kmf.getKeyManagers();
for (KeyManager keyManager : keyManagers) {
if (keyManager instanceof X509ExtendedKeyManager) {
return new X509ExtendedKeyManager[] { (X509ExtendedKeyManager) keyManager };
}
}
throw new IllegalStateException("failed to find a X509ExtendedKeyManager");
}
static X509ExtendedTrustManager[] trustManagers(Certificate[] certificates) throws Exception {
KeyStore store = KeyStore.getInstance("jks");
store.load(null, null);
int counter = 0;
for (Certificate certificate : certificates) {
store.setCertificateEntry("cert" + counter, certificate);
counter++;
}
return trustManagers(store, TrustManagerFactory.getDefaultAlgorithm());
}
static X509ExtendedTrustManager[] trustManagers(KeyStore keyStore, String algorithm) throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keyStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509ExtendedTrustManager) {
return new X509ExtendedTrustManager[] { (X509ExtendedTrustManager) trustManager };
}
}
throw new IllegalStateException("failed to find a X509ExtendedTrustManager");
}
static Certificate[] readCertificates(List<String> certPaths, Environment environment) throws Exception {
List<Certificate> certificates = new ArrayList<>(certPaths.size());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
for (String path : certPaths) {
try (Reader reader = Files.newBufferedReader(resolvePath(path, environment), StandardCharsets.UTF_8)) {
readCertificates(reader, certificates, certFactory);
}
}
return certificates.toArray(new Certificate[certificates.size()]);
}
static void readCertificates(Reader reader, List<Certificate> certificates, CertificateFactory certFactory) throws Exception {
try (PEMParser pemParser = new PEMParser(reader)) {
Object parsed = pemParser.readObject();
if (parsed == null) {
throw new IllegalArgumentException("could not parse pem certificate");
}
while (parsed != null) {
X509CertificateHolder holder;
if (parsed instanceof X509CertificateHolder) {
holder = (X509CertificateHolder) parsed;
} else if (parsed instanceof X509TrustedCertificateBlock) {
X509TrustedCertificateBlock certificateBlock = (X509TrustedCertificateBlock) parsed;
holder = certificateBlock.getCertificateHolder();
} else {
throw new IllegalArgumentException("parsed an unsupported object [" +
parsed.getClass().getSimpleName() + "]");
}
certificates.add(certFactory.generateCertificate(new ByteArrayInputStream(holder.getEncoded())));
parsed = pemParser.readObject();
}
}
}
static PrivateKey readPrivateKey(Reader reader, char[] keyPassword) throws Exception {
try (PEMParser parser = new PEMParser(reader)) {
Object parsed;
List<Object> list = new ArrayList<>(1);
do {
parsed = parser.readObject();
if (parsed != null) {
list.add(parsed);
}
} while (parsed != null);
if (list.size() != 1) {
throw new IllegalStateException("key file contained [" + list.size() + "] entries, expected one");
}
PrivateKeyInfo privateKeyInfo;
Object parsedObject = list.get(0);
if (parsedObject instanceof PEMEncryptedKeyPair) {
if (keyPassword == null) {
throw new IllegalArgumentException("cannot read encrypted key without a password");
}
// we have an encrypted key pair so we need to decrypt it
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) parsedObject;
privateKeyInfo = encryptedKeyPair
.decryptKeyPair(new JcePEMDecryptorProviderBuilder().setProvider(BC_PROV).build(keyPassword))
.getPrivateKeyInfo();
} else if (parsedObject instanceof PEMKeyPair) {
privateKeyInfo = ((PEMKeyPair) parsedObject).getPrivateKeyInfo();
} else if (parsedObject instanceof PrivateKeyInfo) {
privateKeyInfo = (PrivateKeyInfo) parsedObject;
} else {
throw new IllegalArgumentException("parsed an unsupported object [" +
parsedObject.getClass().getSimpleName() + "]");
}
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
return converter.getPrivateKey(privateKeyInfo);
}
}
static X509Certificate generateSignedCertificate(boolean resolveHostname, String nodeName, Set<InetAddress> addresses, KeyPair keyPair,
Certificate caCert, PrivateKey caPrivKey) throws Exception {
final DateTime notBefore = new DateTime(DateTimeZone.UTC);
final DateTime notAfter = notBefore.plusYears(1);
final BigInteger serial = getSerial();
X509Certificate x509CACert = (X509Certificate) caCert;
X500Name subject = new X500Name("CN=" + nodeName);
JcaX509v3CertificateBuilder builder =
new JcaX509v3CertificateBuilder(X500Name.getInstance(x509CACert.getIssuerX500Principal().getEncoded()), serial,
new Time(notBefore.toDate(), Locale.ROOT), new Time(notAfter.toDate(), Locale.ROOT), subject, keyPair.getPublic());
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
builder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
builder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(x509CACert));
if (addresses.isEmpty() == false) {
builder.addExtension(Extension.subjectAlternativeName, false, getSubjectAlternativeNames(resolveHostname, addresses));
}
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(caPrivKey);
X509CertificateHolder certificateHolder = builder.build(signer);
return new JcaX509CertificateConverter().getCertificate(certificateHolder);
}
static BigInteger getSerial() {
SecureRandom random = new SecureRandom();
BigInteger serial = new BigInteger(SERIAL_BIT_LENGTH, random);
assert serial.compareTo(BigInteger.valueOf(0L)) >= 0;
return serial;
}
static KeyPair generateKeyPair() throws Exception {
// generate a private key
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
}
static GeneralNames getSubjectAlternativeNames(boolean resolveName, Set<InetAddress> addresses) throws Exception {
Set<GeneralName> generalNameList = new HashSet<>();
for (InetAddress address : addresses) {
if (address.isAnyLocalAddress()) {
// it is a wildcard address
for (InetAddress inetAddress : InetAddressHelper.getAllAddresses()) {
addSubjectAlternativeNames(resolveName, inetAddress, generalNameList);
}
} else {
addSubjectAlternativeNames(resolveName, address, generalNameList);
}
}
return new GeneralNames(generalNameList.toArray(new GeneralName[generalNameList.size()]));
}
@SuppressForbidden(reason = "need to use getHostName to resolve DNS name and getHostAddress to ensure we resolved the name")
static void addSubjectAlternativeNames(boolean resolveName, InetAddress inetAddress, Set<GeneralName> list) {
String hostaddress = inetAddress.getHostAddress();
String ip = NetworkAddress.format(inetAddress);
list.add(new GeneralName(GeneralName.iPAddress, ip));
if (resolveName && (inetAddress.isLinkLocalAddress() == false)) {
String possibleHostName = inetAddress.getHostName();
if (possibleHostName.equals(hostaddress) == false) {
list.add(new GeneralName(GeneralName.dNSName, possibleHostName));
}
}
}
}

View File

@ -5,21 +5,17 @@
*/
package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.watcher.ResourceWatcherService;
import java.nio.file.Path;
@SuppressForbidden(reason = "we don't have the environment to resolve files from when running in a transport client")
public class ClientSSLService extends AbstractSSLService {
@Inject
public ClientSSLService(Settings settings) {
super(settings, null);
public ClientSSLService(Settings settings, Global globalSSLConfiguration) {
super(settings, null, globalSSLConfiguration, null);
}
@Inject(optional = true)
@ -27,31 +23,14 @@ public class ClientSSLService extends AbstractSSLService {
this.env = environment;
}
@Override
protected SSLSettings sslSettings(Settings customSettings) {
SSLSettings sslSettings = new SSLSettings(customSettings, settings);
if (sslSettings.keyStorePath != null) {
if (sslSettings.keyStorePassword == null) {
throw new IllegalArgumentException("no keystore password configured");
}
assert sslSettings.keyPassword != null;
}
if (sslSettings.trustStorePath != null) {
if (sslSettings.trustStorePassword == null) {
throw new IllegalArgumentException("no truststore password configured");
}
}
return sslSettings;
@Inject(optional = true)
public void setResourceWatcherService(ResourceWatcherService resourceWatcherService) {
this.resourceWatcherService = resourceWatcherService;
}
@Override
protected Path resolvePath(String location) {
if (env == null) {
return PathUtils.get(Strings.cleanPath(location));
}
return super.resolvePath(location);
protected void validateSSLConfiguration(SSLConfiguration sslConfiguration) {
sslConfiguration.keyConfig().validate();
sslConfiguration.trustConfig().validate();
}
}

View File

@ -0,0 +1,143 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.TrustConfig.Reloadable.Listener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.watcher.ResourceWatcherService.Frequency;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Path;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
abstract class KeyConfig extends TrustConfig {
KeyConfig(boolean includeSystem, boolean reloadEnabled) {
super(includeSystem, reloadEnabled);
}
static final KeyConfig NONE = new KeyConfig(false, false) {
@Override
X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) {
return null;
}
@Override
X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) {
return null;
}
@Override
void validate() {
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.emptyList();
}
@Override
public String toString() {
return "NONE";
}
};
final KeyManager[] keyManagers(@Nullable Environment environment, @Nullable ResourceWatcherService resourceWatcherService,
@Nullable Listener listener) {
X509ExtendedKeyManager[] keyManagers = loadKeyManagers(environment);
if (reloadEnabled && resourceWatcherService != null && listener != null) {
ReloadableX509KeyManager reloadableX509KeyManager = new ReloadableX509KeyManager(keyManagers[0], environment);
List<Path> filesToMonitor = filesToMonitor(environment);
ChangeListener changeListener = new ChangeListener(filesToMonitor, reloadableX509KeyManager, listener);
try {
for (Path dir : directoriesToMonitor(filesToMonitor)) {
FileWatcher fileWatcher = new FileWatcher(dir);
fileWatcher.addListener(changeListener);
resourceWatcherService.add(fileWatcher, Frequency.HIGH);
}
return new X509ExtendedKeyManager[] { reloadableX509KeyManager };
} catch (IOException e) {
throw new ElasticsearchException("failed to add file watcher", e);
}
}
return keyManagers;
}
abstract X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment);
final class ReloadableX509KeyManager extends X509ExtendedKeyManager implements Reloadable {
private final Environment environment;
private volatile X509ExtendedKeyManager keyManager;
ReloadableX509KeyManager(X509ExtendedKeyManager keyManager, @Nullable Environment environment) {
this.keyManager = keyManager;
this.environment = environment;
}
@Override
public String[] getClientAliases(String s, Principal[] principals) {
return keyManager.getClientAliases(s, principals);
}
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return keyManager.chooseClientAlias(strings, principals, socket);
}
@Override
public String[] getServerAliases(String s, Principal[] principals) {
return keyManager.getServerAliases(s, principals);
}
@Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return keyManager.chooseServerAlias(s, principals, socket);
}
@Override
public X509Certificate[] getCertificateChain(String s) {
return keyManager.getCertificateChain(s);
}
@Override
public PrivateKey getPrivateKey(String s) {
return keyManager.getPrivateKey(s);
}
@Override
public String chooseEngineClientAlias(String[] strings, Principal[] principals, SSLEngine engine) {
return keyManager.chooseEngineClientAlias(strings, principals, engine);
}
@Override
public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine engine) {
return keyManager.chooseEngineServerAlias(s, principals, engine);
}
public synchronized void reload() {
X509ExtendedKeyManager[] keyManagers = loadKeyManagers(environment);
this.keyManager = keyManagers[0];
}
synchronized void setKeyManager(X509ExtendedKeyManager x509ExtendedKeyManager) {
this.keyManager = x509ExtendedKeyManager;
}
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
class PEMKeyConfig extends KeyConfig {
final String keyPath;
final String keyPassword;
final List<String> certPaths;
PEMKeyConfig(boolean includeSystem, boolean reloadEnabled, String keyPath, String keyPassword, List<String> certPaths) {
super(includeSystem, reloadEnabled);
this.keyPath = keyPath;
this.keyPassword = keyPassword;
this.certPaths = certPaths;
}
@Override
X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) {
try {
PrivateKey privateKey = readPrivateKey(CertUtils.resolvePath(keyPath, environment));
Certificate[] certificateChain = CertUtils.readCertificates(certPaths, environment);
// password must be non-null for keystore...
char[] password = keyPassword == null ? new char[0] : keyPassword.toCharArray();
return CertUtils.keyManagers(certificateChain, privateKey, password);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
}
}
PrivateKey readPrivateKey(Path keyPath) throws Exception {
try (Reader reader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
char[] password = keyPassword == null ? null : keyPassword.toCharArray();
return CertUtils.readPrivateKey(reader, password);
}
}
@Override
X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) {
try {
Certificate[] certificates = CertUtils.readCertificates(certPaths, environment);
return CertUtils.trustManagers(certificates);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
}
}
@Override
void validate() {
if (keyPath == null) {
throw new IllegalArgumentException("no key file configured");
} else if (certPaths == null || certPaths.isEmpty()) {
throw new IllegalArgumentException("no certificate provided");
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
List<Path> paths = new ArrayList<>(1 + certPaths.size());
paths.add(CertUtils.resolvePath(keyPath, environment));
for (String certPath : certPaths) {
paths.add(CertUtils.resolvePath(certPath, environment));
}
return paths;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PEMKeyConfig that = (PEMKeyConfig) o;
if (keyPath != null ? !keyPath.equals(that.keyPath) : that.keyPath != null) return false;
if (keyPassword != null ? !keyPassword.equals(that.keyPassword) : that.keyPassword != null) return false;
return certPaths != null ? certPaths.equals(that.certPaths) : that.certPaths == null;
}
@Override
public int hashCode() {
int result = keyPath != null ? keyPath.hashCode() : 0;
result = 31 * result + (keyPassword != null ? keyPassword.hashCode() : 0);
result = 31 * result + (certPaths != null ? certPaths.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "keyPath=[" + keyPath +
"], certPaths=[" + Strings.collectionToCommaDelimitedString(certPaths) +
"]";
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
class PEMTrustConfig extends TrustConfig {
final List<String> caPaths;
PEMTrustConfig(boolean includeSystem, boolean reloadEnabled, List<String> caPaths) {
super(includeSystem, reloadEnabled);
this.caPaths = caPaths;
}
@Override
X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) {
try {
Certificate[] certificates = CertUtils.readCertificates(caPaths, environment);
return CertUtils.trustManagers(certificates);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
}
}
@Override
void validate() {
if (caPaths == null) {
throw new IllegalArgumentException("no ca paths have been configured");
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
List<Path> paths = new ArrayList<>(caPaths.size());
for (String path : caPaths) {
paths.add(CertUtils.resolvePath(path, environment));
}
return paths;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PEMTrustConfig that = (PEMTrustConfig) o;
return caPaths != null ? caPaths.equals(that.caPaths) : that.caPaths == null;
}
@Override
public int hashCode() {
return caPaths != null ? caPaths.hashCode() : 0;
}
@Override
public String toString() {
return "ca=[" + Strings.collectionToCommaDelimitedString(caPaths) + "]";
}
}

View File

@ -0,0 +1,560 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.node.Node;
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
import org.elasticsearch.transport.TransportSettings;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import static org.elasticsearch.shield.Security.featureEnabledSetting;
import static org.elasticsearch.shield.Security.setting;
import static org.elasticsearch.shield.support.OptionalSettings.createInt;
import static org.elasticsearch.shield.support.OptionalSettings.createString;
import static org.elasticsearch.shield.support.OptionalSettings.createTimeValue;
/**
* Class that contains all configuration related to SSL use within x-pack
*/
public abstract class SSLConfiguration {
public abstract KeyConfig keyConfig();
public abstract TrustConfig trustConfig();
public abstract String protocol();
public abstract int sessionCacheSize();
public abstract TimeValue sessionCacheTimeout();
public abstract List<String> ciphers();
public abstract List<String> supportedProtocols();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SSLConfiguration)) return false;
SSLConfiguration that = (SSLConfiguration) o;
if (this.sessionCacheSize() != that.sessionCacheSize()) {
return false;
}
if (this.keyConfig() != null ? !this.keyConfig().equals(that.keyConfig()) : that.keyConfig() != null) {
return false;
}
if (this.trustConfig() != null ? !this.trustConfig().equals(that.trustConfig()) : that.trustConfig() != null) {
return false;
}
if (this.protocol() != null ? !this.protocol().equals(that.protocol()) : that.protocol() != null) {
return false;
}
if (this.sessionCacheTimeout() != null ?
!this.sessionCacheTimeout().equals(that.sessionCacheTimeout()) : that.sessionCacheTimeout() != null) {
return false;
}
if (this.ciphers() != null ? !this.ciphers().equals(that.ciphers()) : that.ciphers() != null) {
return false;
}
return this.supportedProtocols() != null ?
this.supportedProtocols().equals(that.supportedProtocols()) : that.supportedProtocols() == null;
}
@Override
public int hashCode() {
int result = this.keyConfig() != null ? this.keyConfig().hashCode() : 0;
result = 31 * result + (this.trustConfig() != null ? this.trustConfig().hashCode() : 0);
result = 31 * result + (this.protocol() != null ? this.protocol().hashCode() : 0);
result = 31 * result + this.sessionCacheSize();
result = 31 * result + (this.sessionCacheTimeout() != null ? this.sessionCacheTimeout().hashCode() : 0);
result = 31 * result + (this.ciphers() != null ? this.ciphers().hashCode() : 0);
result = 31 * result + (this.supportedProtocols() != null ? this.supportedProtocols().hashCode() : 0);
return result;
}
public static class Global extends SSLConfiguration {
public static final List<String> DEFAULT_SUPPORTED_PROTOCOLS = Arrays.asList("TLSv1", "TLSv1.1", "TLSv1.2");
public static final List<String> DEFAULT_CIPHERS =
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
public static final TimeValue DEFAULT_SESSION_CACHE_TIMEOUT = TimeValue.timeValueHours(24);
public static final int DEFAULT_SESSION_CACHE_SIZE = 1000;
public static final String DEFAULT_PROTOCOL = "TLSv1.2";
public static final Setting<Boolean> AUTO_GENERATE_SSL_SETTING =
Setting.boolSetting(featureEnabledSetting("ssl.auto_generate"), true, Property.NodeScope, Property.Filtered);
static final Setting<Boolean> AUTO_GEN_RESOLVE_HOST_SETTING =
Setting.boolSetting(setting("ssl.auto_generate.resolve_name"), true, Property.NodeScope, Property.Filtered);
// common settings
static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting(globalKey(Custom.CIPHERS_SETTING), DEFAULT_CIPHERS,
Function.identity(), Property.NodeScope, Property.Filtered);
static final Setting<List<String>> SUPPORTED_PROTOCOLS_SETTING = Setting.listSetting(globalKey(Custom.SUPPORTED_PROTOCOLS_SETTING),
DEFAULT_SUPPORTED_PROTOCOLS, Function.identity(), Property.NodeScope, Property.Filtered);
static final Setting<String> PROTOCOL_SETTING = new Setting<>(globalKey(Custom.PROTOCOL_SETTING), DEFAULT_PROTOCOL,
Function.identity(), Property.NodeScope, Property.Filtered);
static final Setting<Integer> SESSION_CACHE_SIZE_SETTING = Setting.intSetting(globalKey(Custom.CACHE_SIZE_SETTING),
DEFAULT_SESSION_CACHE_SIZE, Property.NodeScope, Property.Filtered);
static final Setting<TimeValue> SESSION_CACHE_TIMEOUT_SETTING = Setting.timeSetting(globalKey(Custom.CACHE_TIMEOUT_SETTING),
DEFAULT_SESSION_CACHE_TIMEOUT, Property.NodeScope, Property.Filtered);
static final Setting<Boolean> RELOAD_ENABLED_SETTING =
Setting.boolSetting(globalKey(Custom.RELOAD_ENABLED_SETTING), true, Property.NodeScope, Property.Filtered);
// keystore settings
static final Setting<Optional<String>> KEYSTORE_PATH_SETTING = createString(globalKey(Custom.KEYSTORE_PATH_SETTING),
s -> System.getProperty("javax.net.ssl.keyStore"), Property.NodeScope, Property.Filtered);
static final Setting<Optional<String>> KEYSTORE_PASSWORD_SETTING = createString(globalKey(Custom.KEYSTORE_PASSWORD_SETTING),
s -> System.getProperty("javax.net.ssl.keyStorePassword"), Property.NodeScope, Property.Filtered);
static final Setting<String> KEYSTORE_ALGORITHM_SETTING = new Setting<>(globalKey(Custom.KEYSTORE_ALGORITHM_SETTING),
s -> System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()),
Function.identity(), Property.NodeScope, Property.Filtered);
static final Setting<Optional<String>> KEYSTORE_KEY_PASSWORD_SETTING =
createString(globalKey(Custom.KEYSTORE_KEY_PASSWORD_SETTING), KEYSTORE_PASSWORD_SETTING,
Property.NodeScope, Property.Filtered);
// truststore settings
static final Setting<Optional<String>> TRUSTSTORE_PATH_SETTING = createString(globalKey(Custom.TRUSTSTORE_PATH_SETTING),
s -> System.getProperty("javax.net.ssl.trustStore"), Property.NodeScope, Property.Filtered);
static final Setting<Optional<String>> TRUSTSTORE_PASSWORD_SETTING = createString(globalKey(Custom.TRUSTSTORE_PASSWORD_SETTING),
s -> System.getProperty("javax.net.ssl.trustStorePassword"), Property.NodeScope, Property.Filtered);
static final Setting<String> TRUSTSTORE_ALGORITHM_SETTING = new Setting<>(globalKey(Custom.TRUSTSTORE_ALGORITHM_SETTING),
s -> System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()),
Function.identity(), Property.NodeScope, Property.Filtered);
// PEM key and cert settings
static final Setting<Optional<String>> KEY_PATH_SETTING = createString(globalKey(Custom.KEY_PATH_SETTING),
Property.NodeScope, Property.Filtered);
static final Setting<Optional<String>> KEY_PASSWORD_SETTING = createString(globalKey(Custom.KEY_PASSWORD_SETTING),
Property.NodeScope, Property.Filtered);
static final Setting<List<String>> CERT_SETTING = Setting.listSetting(globalKey(Custom.CERT_SETTING), Collections.emptyList(),
s -> s, Property.NodeScope, Property.Filtered);
// PEM trusted certs
static final Setting<List<String>> CA_PATHS_SETTING = Setting.listSetting(globalKey(Custom.CA_PATHS_SETTING),
Collections.emptyList(), s -> s, Property.NodeScope, Property.Filtered);
// Default system trusted certs
static final Setting<Boolean> INCLUDE_JDK_CERTS_SETTING = Setting.boolSetting(globalKey(Custom.INCLUDE_JDK_CERTS_SETTING), true,
Property.NodeScope, Property.Filtered);
public static void registerSettings(SettingsModule settingsModule) {
settingsModule.registerSetting(Global.CIPHERS_SETTING);
settingsModule.registerSetting(Global.SUPPORTED_PROTOCOLS_SETTING);
settingsModule.registerSetting(Global.KEYSTORE_PATH_SETTING);
settingsModule.registerSetting(Global.KEYSTORE_PASSWORD_SETTING);
settingsModule.registerSetting(Global.KEYSTORE_ALGORITHM_SETTING);
settingsModule.registerSetting(Global.KEYSTORE_KEY_PASSWORD_SETTING);
settingsModule.registerSetting(Global.KEY_PATH_SETTING);
settingsModule.registerSetting(Global.KEY_PASSWORD_SETTING);
settingsModule.registerSetting(Global.CERT_SETTING);
settingsModule.registerSetting(Global.TRUSTSTORE_PATH_SETTING);
settingsModule.registerSetting(Global.TRUSTSTORE_PASSWORD_SETTING);
settingsModule.registerSetting(Global.TRUSTSTORE_ALGORITHM_SETTING);
settingsModule.registerSetting(Global.PROTOCOL_SETTING);
settingsModule.registerSetting(Global.SESSION_CACHE_SIZE_SETTING);
settingsModule.registerSetting(Global.SESSION_CACHE_TIMEOUT_SETTING);
settingsModule.registerSetting(Global.CA_PATHS_SETTING);
settingsModule.registerSetting(Global.AUTO_GENERATE_SSL_SETTING);
settingsModule.registerSetting(Global.AUTO_GEN_RESOLVE_HOST_SETTING);
settingsModule.registerSetting(Global.INCLUDE_JDK_CERTS_SETTING);
settingsModule.registerSetting(Global.RELOAD_ENABLED_SETTING);
}
private final ESLogger logger;
private final Settings settings;
private final KeyConfig keyConfig;
private final TrustConfig trustConfig;
private final String sslProtocol;
private final int sessionCacheSize;
private final TimeValue sessionCacheTimeout;
private final List<String> ciphers;
private final List<String> supportedProtocols;
/**
* This constructor should be used with the global settings of the service
*
* @param settings the global settings to build the SSL configuration from
*/
@Inject
public Global(Settings settings) {
this.settings = settings;
this.logger = Loggers.getLogger(getClass(), settings);
this.keyConfig = createGlobalKeyConfig(settings);
this.trustConfig = createGlobalTrustConfig(settings, keyConfig);
this.sslProtocol = PROTOCOL_SETTING.get(settings);
this.sessionCacheSize = SESSION_CACHE_SIZE_SETTING.get(settings);
this.sessionCacheTimeout = SESSION_CACHE_TIMEOUT_SETTING.get(settings);
this.ciphers = CIPHERS_SETTING.get(settings);
this.supportedProtocols = SUPPORTED_PROTOCOLS_SETTING.get(settings);
}
@Override
public KeyConfig keyConfig() {
return keyConfig;
}
@Override
public TrustConfig trustConfig() {
return trustConfig;
}
@Override
public String protocol() {
return sslProtocol;
}
@Override
public int sessionCacheSize() {
return sessionCacheSize;
}
@Override
public TimeValue sessionCacheTimeout() {
return sessionCacheTimeout;
}
@Override
public List<String> ciphers() {
return ciphers;
}
@Override
public List<String> supportedProtocols() {
return supportedProtocols;
}
@Override
public String toString() {
return "SSLConfiguration{" +
", keyConfig=[" + keyConfig +
"], trustConfig=" + trustConfig +
"], sslProtocol=['" + sslProtocol + '\'' +
"], sessionCacheSize=[" + sessionCacheSize +
"], sessionCacheTimeout=[" + sessionCacheTimeout +
"], ciphers=[" + ciphers +
"], supportedProtocols=[" + supportedProtocols +
"]}";
}
public void onTransportStart(BoundTransportAddress boundAddress, Map<String, BoundTransportAddress> profileBoundAddresses) {
if (shouldAutoGenerateKeyAndCertificate(settings) == false) {
return;
}
Set<InetAddress> uniqueAddresses = new HashSet<>();
if (boundAddress != null) {
// this could be null if we came from a transport client
addInetAddresses(uniqueAddresses, boundAddress.boundAddresses());
addInetAddresses(uniqueAddresses, boundAddress.publishAddress());
}
for (BoundTransportAddress profileAddress : profileBoundAddresses.values()) {
addInetAddresses(uniqueAddresses, profileAddress.boundAddresses());
addInetAddresses(uniqueAddresses, profileAddress.publishAddress());
}
try {
((AutoGeneratedKeyConfig) keyConfig).generateCertIfNecessary(AUTO_GEN_RESOLVE_HOST_SETTING.get(settings),
Node.NODE_NAME_SETTING.get(settings), uniqueAddresses, logger);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize auto generated certificate and key");
}
}
private static String globalKey(Setting setting) {
return setting("ssl." + setting.getKey());
}
static void addInetAddresses(Set<InetAddress> addresses, TransportAddress... transportAddresses) {
for (TransportAddress transportAddress : transportAddresses) {
addresses.add(((InetSocketTransportAddress)transportAddress).address().getAddress());
}
}
static boolean shouldAutoGenerateKeyAndCertificate(Settings settings) {
if (AUTO_GENERATE_SSL_SETTING.get(settings) == false) {
return false;
}
// did they configure some SSL settings other than auto generate
Settings.Builder builder = Settings.builder().put(settings);
builder.remove(AUTO_GEN_RESOLVE_HOST_SETTING.getKey());
builder.remove(AUTO_GENERATE_SSL_SETTING.getKey());
builder.remove(INCLUDE_JDK_CERTS_SETTING.getKey());
Settings nonAutoGen = builder.build();
if (nonAutoGen.getByPrefix(setting("ssl.")).isEmpty() == false) {
return false;
}
// SSL needs to be enabled somewhere
final boolean transportEnabled = ShieldNettyTransport.SSL_SETTING.get(settings);
final boolean httpEnabled = ShieldNettyHttpServerTransport.SSL_SETTING.get(settings);
if (transportEnabled || httpEnabled) {
return true;
}
// check the profiles... maybe disabled SSL on default transport and enabled on a profile
Map<String, Settings> profiles = TransportSettings.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(true);
for (Settings profileSettings : profiles.values()) {
if (ShieldNettyTransport.profileSsl(profileSettings, settings)) {
return true;
}
}
return false;
}
static KeyConfig createGlobalKeyConfig(Settings settings) {
if (shouldAutoGenerateKeyAndCertificate(settings)) {
return new AutoGeneratedKeyConfig(INCLUDE_JDK_CERTS_SETTING.get(settings));
}
String keyStorePath = KEYSTORE_PATH_SETTING.get(settings).orElse(null);
String keyPath = KEY_PATH_SETTING.get(settings).orElse(null);
if (keyPath != null && keyStorePath != null) {
throw new IllegalArgumentException("you cannot specify a keystore and key file");
} else if (keyStorePath == null && keyPath == null) {
return KeyConfig.NONE;
}
boolean includeSystem = INCLUDE_JDK_CERTS_SETTING.get(settings);
boolean reloadEnabled = RELOAD_ENABLED_SETTING.get(settings);
if (keyPath != null) {
String keyPassword = KEY_PASSWORD_SETTING.get(settings).orElse(null);
List<String> certPaths = getListOrNull(CERT_SETTING, settings);
if (certPaths == null) {
throw new IllegalArgumentException("you must specify the certificates to use with the key");
}
return new PEMKeyConfig(includeSystem, reloadEnabled, keyPath, keyPassword, certPaths);
} else {
assert keyStorePath != null;
String keyStorePassword = KEYSTORE_PASSWORD_SETTING.get(settings).orElse(null);
String keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings);
String keyStoreKeyPassword = KEYSTORE_KEY_PASSWORD_SETTING.get(settings).orElse(keyStorePassword);
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
return new StoreKeyConfig(includeSystem, reloadEnabled, keyStorePath, keyStorePassword, keyStoreKeyPassword,
keyStoreAlgorithm, trustStoreAlgorithm);
}
}
static TrustConfig createGlobalTrustConfig(Settings settings, KeyConfig keyInfo) {
if (keyInfo instanceof AutoGeneratedKeyConfig) {
assert shouldAutoGenerateKeyAndCertificate(settings);
return keyInfo;
}
String trustStorePath = TRUSTSTORE_PATH_SETTING.get(settings).orElse(null);
List<String> caPaths = getListOrNull(CA_PATHS_SETTING, settings);
boolean includeSystem = INCLUDE_JDK_CERTS_SETTING.get(settings);
boolean reloadEnabled = RELOAD_ENABLED_SETTING.get(settings);
if (trustStorePath != null && caPaths != null) {
throw new IllegalArgumentException("you cannot specify a truststore and ca files");
} else if (caPaths != null) {
return new PEMTrustConfig(includeSystem, reloadEnabled, caPaths);
} else if (trustStorePath != null) {
String trustStorePassword = TRUSTSTORE_PASSWORD_SETTING.get(settings).orElse(null);
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
return new StoreTrustConfig(includeSystem, reloadEnabled, trustStorePath, trustStorePassword, trustStoreAlgorithm);
} else if (keyInfo != KeyConfig.NONE) {
return keyInfo;
} else {
return new StoreTrustConfig(includeSystem, reloadEnabled, null, null, null);
}
}
}
public static class Custom extends SSLConfiguration {
static final Setting<Optional<String>> PROTOCOL_SETTING = createString("protocol");
static final Setting<Optional<Integer>> CACHE_SIZE_SETTING = createInt("session.cache_size");
static final Setting<Optional<TimeValue>> CACHE_TIMEOUT_SETTING = createTimeValue("session.cache_timeout");
static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting("ciphers", Collections.emptyList(), s -> s);
static final Setting<List<String>> SUPPORTED_PROTOCOLS_SETTING =
Setting.listSetting("supported_protocols", Collections.emptyList(), s -> s);
static final Setting<Optional<String>> KEYSTORE_PATH_SETTING = createString("keystore.path");
static final Setting<Optional<String>> KEYSTORE_PASSWORD_SETTING = createString("keystore.password");
static final Setting<String> KEYSTORE_ALGORITHM_SETTING = new Setting<>("keystore.algorithm",
s -> System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()), Function.identity());
static final Setting<Optional<String>> KEYSTORE_KEY_PASSWORD_FALLBACK = createString("keystore.password");
static final Setting<Optional<String>> KEYSTORE_KEY_PASSWORD_SETTING =
createString("keystore.key_password", KEYSTORE_KEY_PASSWORD_FALLBACK);
static final Setting<Optional<String>> TRUSTSTORE_PATH_SETTING = createString("truststore.path");
static final Setting<Optional<String>> TRUSTSTORE_PASSWORD_SETTING = createString("truststore.password");
static final Setting<String> TRUSTSTORE_ALGORITHM_SETTING = new Setting<>("truststore.algorithm",
s -> System.getProperty("ssl.TrustManagerFactory.algorithm",
TrustManagerFactory.getDefaultAlgorithm()), Function.identity());
static final Setting<Optional<String>> KEY_PATH_SETTING = createString("key.path");
static final Setting<Optional<String>> KEY_PASSWORD_SETTING = createString("key.password");
static final Setting<List<String>> CERT_SETTING = Setting.listSetting("cert", Collections.emptyList(), s -> s);
static final Setting<List<String>> CA_PATHS_SETTING = Setting.listSetting("ca", Collections.emptyList(), s -> s);
static final Setting<Boolean> INCLUDE_JDK_CERTS_SETTING = Setting.boolSetting("trust_cacerts", true);
static final Setting<Boolean> RELOAD_ENABLED_SETTING = Setting.boolSetting("reload.enabled", true);
private final KeyConfig keyConfig;
private final TrustConfig trustConfig;
private final String sslProtocol;
private final int sessionCacheSize;
private final TimeValue sessionCacheTimeout;
private final List<String> ciphers;
private final List<String> supportedProtocols;
/**
* The settings passed in should be the group settings under ssl, like xpack.security.ssl
*
* @param settings the profile settings to get the SSL configuration for
* @param defaultConfig the default SSL configuration
*/
public Custom(Settings settings, SSLConfiguration defaultConfig) {
Objects.requireNonNull(settings);
this.keyConfig = createKeyConfig(settings, defaultConfig);
this.trustConfig = createTrustConfig(settings, keyConfig, defaultConfig);
this.sslProtocol = PROTOCOL_SETTING.get(settings).orElse(defaultConfig.protocol());
this.sessionCacheSize = CACHE_SIZE_SETTING.get(settings).orElse(defaultConfig.sessionCacheSize());
this.sessionCacheTimeout = CACHE_TIMEOUT_SETTING.get(settings).orElse(defaultConfig.sessionCacheTimeout());
this.ciphers = getListOrDefault(CIPHERS_SETTING, settings, defaultConfig.ciphers());
this.supportedProtocols = getListOrDefault(SUPPORTED_PROTOCOLS_SETTING, settings, defaultConfig.supportedProtocols());
}
@Override
public KeyConfig keyConfig() {
return keyConfig;
}
@Override
public TrustConfig trustConfig() {
return trustConfig;
}
@Override
public String protocol() {
return sslProtocol;
}
@Override
public int sessionCacheSize() {
return sessionCacheSize;
}
@Override
public TimeValue sessionCacheTimeout() {
return sessionCacheTimeout;
}
@Override
public List<String> ciphers() {
return ciphers;
}
@Override
public List<String> supportedProtocols() {
return supportedProtocols;
}
@Override
public String toString() {
return "SSLConfiguration{" +
", keyConfig=[" + keyConfig +
"], trustConfig=" + trustConfig +
"], sslProtocol=['" + sslProtocol + '\'' +
"], sessionCacheSize=[" + sessionCacheSize +
"], sessionCacheTimeout=[" + sessionCacheTimeout +
"], ciphers=[" + ciphers +
"], supportedProtocols=[" + supportedProtocols +
'}';
}
static KeyConfig createKeyConfig(Settings settings, SSLConfiguration global) {
String keyStorePath = KEYSTORE_PATH_SETTING.get(settings).orElse(null);
String keyPath = KEY_PATH_SETTING.get(settings).orElse(null);
if (keyPath != null && keyStorePath != null) {
throw new IllegalArgumentException("you cannot specify a keystore and key file");
} else if (keyStorePath == null && keyPath == null) {
return global.keyConfig();
}
boolean includeSystem = INCLUDE_JDK_CERTS_SETTING.get(settings);
boolean reloadEnabled = RELOAD_ENABLED_SETTING.get(settings);
if (keyPath != null) {
String keyPassword = KEY_PASSWORD_SETTING.get(settings).orElse(null);
List<String> certPaths = getListOrNull(CERT_SETTING, settings);
if (certPaths == null) {
throw new IllegalArgumentException("you must specify the certificates to use with the key");
}
return new PEMKeyConfig(includeSystem, reloadEnabled, keyPath, keyPassword, certPaths);
} else {
assert keyStorePath != null;
String keyStorePassword = KEYSTORE_PASSWORD_SETTING.get(settings).orElse(null);
String keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings);
String keyStoreKeyPassword = KEYSTORE_KEY_PASSWORD_SETTING.get(settings).orElse(keyStorePassword);
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
return new StoreKeyConfig(includeSystem, reloadEnabled, keyStorePath, keyStorePassword, keyStoreKeyPassword,
keyStoreAlgorithm, trustStoreAlgorithm);
}
}
static TrustConfig createTrustConfig(Settings settings, KeyConfig keyConfig, SSLConfiguration global) {
String trustStorePath = TRUSTSTORE_PATH_SETTING.get(settings).orElse(null);
List<String> caPaths = getListOrNull(CA_PATHS_SETTING, settings);
if (trustStorePath != null && caPaths != null) {
throw new IllegalArgumentException("you cannot specify a truststore and ca files");
} else if (caPaths != null) {
return new PEMTrustConfig(INCLUDE_JDK_CERTS_SETTING.get(settings), RELOAD_ENABLED_SETTING.get(settings), caPaths);
} else if (trustStorePath != null) {
String trustStorePassword = TRUSTSTORE_PASSWORD_SETTING.get(settings).orElse(null);
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
return new StoreTrustConfig(INCLUDE_JDK_CERTS_SETTING.get(settings), RELOAD_ENABLED_SETTING.get(settings),
trustStorePath, trustStorePassword, trustStoreAlgorithm);
} else if (keyConfig == global.keyConfig()) {
return global.trustConfig();
} else {
return keyConfig;
}
}
}
static List<String> getListOrNull(Setting<List<String>> listSetting, Settings settings) {
return getListOrDefault(listSetting, settings, null);
}
static List<String> getListOrDefault(Setting<List<String>> listSetting, Settings settings, List<String> defaultValue) {
if (listSetting.exists(settings)) {
return listSetting.get(settings);
}
return defaultValue;
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.support.AbstractShieldModule;
/**
@ -20,6 +21,7 @@ public class SSLModule extends AbstractShieldModule {
@Override
protected void configure(boolean clientMode) {
bind(Global.class).asEagerSingleton();
bind(ClientSSLService.class).asEagerSingleton();
if (clientMode) {
bind(ServerSSLService.class).toProvider(Providers.<ServerSSLService>of(null));

View File

@ -1,180 +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.shield.ssl;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.common.unit.TimeValue;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import static org.elasticsearch.shield.Security.setting;
import static org.elasticsearch.shield.support.OptionalStringSetting.create;
/**
* Class that contains all settings related to SSL
*/
public class SSLSettings {
public interface Globals {
List<String> DEFAULT_SUPPORTED_PROTOCOLS = Arrays.asList("TLSv1", "TLSv1.1", "TLSv1.2");
List<String> DEFAULT_CIPHERS =
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
TimeValue DEFAULT_SESSION_CACHE_TIMEOUT = TimeValue.timeValueHours(24);
int DEFAULT_SESSION_CACHE_SIZE = 1000;
String DEFAULT_PROTOCOL = "TLSv1.2";
Setting<List<String>> CIPHERS_SETTING =
Setting.listSetting(setting("ssl.ciphers"), DEFAULT_CIPHERS, Function.identity(), Property.NodeScope, Property.Filtered);
Setting<List<String>> SUPPORTED_PROTOCOLS_SETTING =
Setting.listSetting(setting("ssl.supported_protocols"), DEFAULT_SUPPORTED_PROTOCOLS,
Function.identity(), Property.NodeScope, Property.Filtered);
Setting<Optional<String>> KEYSTORE_PATH_SETTING = create(setting("ssl.keystore.path"),
s -> System.getProperty("javax.net.ssl.keyStore"), Property.NodeScope, Property.Filtered);
Setting<Optional<String>> KEYSTORE_PASSWORD_SETTING = create(setting("ssl.keystore.password"),
s -> System.getProperty("javax.net.ssl.keyStorePassword"), Property.NodeScope, Property.Filtered);
Setting<String> KEYSTORE_ALGORITHM_SETTING =
new Setting<>(setting("ssl.keystore.algorithm"),
s -> System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()),
Function.identity(), Property.NodeScope, Property.Filtered);
Setting<Optional<String>> KEYSTORE_KEY_PASSWORD_SETTING = create(setting("ssl.keystore.key_password"), KEYSTORE_PASSWORD_SETTING,
Property.NodeScope, Property.Filtered);
Setting<Optional<String>> TRUSTSTORE_PATH_SETTING = create(setting("ssl.truststore.path"),
s -> System.getProperty("javax.net.ssl.trustStore"), Property.NodeScope, Property.Filtered);
Setting<Optional<String>> TRUSTSTORE_PASSWORD_SETTING = create(setting("ssl.truststore.password"),
s -> System.getProperty("javax.net.ssl.trustStorePassword"), Property.NodeScope, Property.Filtered);
Setting<String> TRUSTSTORE_ALGORITHM_SETTING =
new Setting<>(setting("ssl.truststore.algorithm"),
s -> System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()),
Function.identity(), Property.NodeScope, Property.Filtered);
Setting<String> PROTOCOL_SETTING =
new Setting<>(setting("ssl.protocol"), DEFAULT_PROTOCOL, Function.identity(), Property.NodeScope, Property.Filtered);
Setting<Integer> SESSION_CACHE_SIZE_SETTING =
Setting.intSetting(setting("ssl.session.cache_size"), DEFAULT_SESSION_CACHE_SIZE, Property.NodeScope, Property.Filtered);
Setting<TimeValue> SESSION_CACHE_TIMEOUT_SETTING =
Setting.timeSetting(setting("ssl.session.cache_timeout"), DEFAULT_SESSION_CACHE_TIMEOUT,
Property.NodeScope, Property.Filtered);
}
private static final ESLogger logger = Loggers.getLogger(SSLSettings.class);
static Setting<Optional<String>> KEYSTORE_PATH_SETTING = create("keystore.path", Globals.KEYSTORE_PATH_SETTING);
static Setting<Optional<String>> KEYSTORE_PASSWORD_SETTING = create("keystore.password", Globals.KEYSTORE_PASSWORD_SETTING);
static Setting<String> KEYSTORE_ALGORITHM_SETTING =
new Setting<>("keystore.algorithm", Globals.KEYSTORE_ALGORITHM_SETTING, s -> s);
//key password fallback should be keystore.key_password -> keystore.password -> global keystore.key_pasword -> global keystore.password
static Setting<Optional<String>> KEY_PASSWORD_FALLBACK = create("keystore.password", Globals.KEYSTORE_KEY_PASSWORD_SETTING);
static Setting<Optional<String>> KEY_PASSWORD_SETTING = create("keystore.key_password", KEY_PASSWORD_FALLBACK);
static Setting<Optional<String>> TRUSTSTORE_PATH_SETTING = create("truststore.path", Globals.TRUSTSTORE_PATH_SETTING);
static Setting<Optional<String>> TRUSTSTORE_PASSWORD_SETTING = create("truststore.password", Globals.TRUSTSTORE_PASSWORD_SETTING);
static Setting<String> TRUSTSTORE_ALGORITHM_SETTING =
new Setting<>("truststore.algorithm", Globals.TRUSTSTORE_ALGORITHM_SETTING, s -> s);
static Setting<String> PROTOCOL_SETTING =
new Setting<>("protocol", Globals.PROTOCOL_SETTING, s -> s);
static Setting<Integer> CACHE_SIZE_SETTING =
new Setting<>("session.cache_size", Globals.SESSION_CACHE_SIZE_SETTING, Integer::parseInt);
static Setting<TimeValue> CACHE_TIMEOUT_SETTING =
Setting.timeSetting("session.cache_timeout", Globals.SESSION_CACHE_TIMEOUT_SETTING);
String keyStorePath;
String keyStorePassword;
String keyStoreAlgorithm;
String keyPassword;
String trustStorePath;
String trustStorePassword;
String trustStoreAlgorithm;
String sslProtocol;
int sessionCacheSize;
TimeValue sessionCacheTimeout;
SSLSettings(Settings settings, Settings sslServiceSettings) {
keyStorePath = getStringOrNull(KEYSTORE_PATH_SETTING, settings, sslServiceSettings);
keyStorePassword = getStringOrNull(KEYSTORE_PASSWORD_SETTING, settings, sslServiceSettings);
keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings, sslServiceSettings);
keyPassword = getStringOrNull(KEY_PASSWORD_SETTING, settings, sslServiceSettings);
// Truststore settings
trustStorePath = getStringOrNull(TRUSTSTORE_PATH_SETTING, settings, sslServiceSettings);
trustStorePassword = getStringOrNull(TRUSTSTORE_PASSWORD_SETTING, settings, sslServiceSettings);
trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings, sslServiceSettings);
sslProtocol = PROTOCOL_SETTING.get(settings, sslServiceSettings);
sessionCacheSize = CACHE_SIZE_SETTING.get(settings, sslServiceSettings);
sessionCacheTimeout = CACHE_TIMEOUT_SETTING.get(settings, sslServiceSettings);
if (trustStorePath == null) {
if (logger.isDebugEnabled()) {
logger.debug("no truststore defined. using keystore [{}] as truststore", keyStorePath);
}
trustStorePath = keyStorePath;
trustStorePassword = keyStorePassword;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SSLSettings that = (SSLSettings) o;
if (keyStorePath != null ? !keyStorePath.equals(that.keyStorePath) : that.keyStorePath != null) {
return false;
}
if (sslProtocol != null ? !sslProtocol.equals(that.sslProtocol) : that.sslProtocol != null) {
return false;
}
if (trustStorePath != null ? !trustStorePath.equals(that.trustStorePath) : that.trustStorePath != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = keyStorePath != null ? keyStorePath.hashCode() : 0;
result = 31 * result + (trustStorePath != null ? trustStorePath.hashCode() : 0);
result = 31 * result + (sslProtocol != null ? sslProtocol.hashCode() : 0);
return result;
}
public static void registerSettings(SettingsModule settingsModule) {
settingsModule.registerSetting(Globals.CIPHERS_SETTING);
settingsModule.registerSetting(Globals.SUPPORTED_PROTOCOLS_SETTING);
settingsModule.registerSetting(Globals.KEYSTORE_PATH_SETTING);
settingsModule.registerSetting(Globals.KEYSTORE_PASSWORD_SETTING);
settingsModule.registerSetting(Globals.KEYSTORE_ALGORITHM_SETTING);
settingsModule.registerSetting(Globals.KEYSTORE_KEY_PASSWORD_SETTING);
settingsModule.registerSetting(Globals.TRUSTSTORE_PATH_SETTING);
settingsModule.registerSetting(Globals.TRUSTSTORE_PASSWORD_SETTING);
settingsModule.registerSetting(Globals.TRUSTSTORE_ALGORITHM_SETTING);
settingsModule.registerSetting(Globals.PROTOCOL_SETTING);
settingsModule.registerSetting(Globals.SESSION_CACHE_SIZE_SETTING);
settingsModule.registerSetting(Globals.SESSION_CACHE_TIMEOUT_SETTING);
}
private static String getStringOrNull(Setting<Optional<String>> setting, Settings settings, Settings fallbackSettings) {
// for settings with complicated fallback we need to try to get it first, if not then try the fallback settings
Optional<String> optional = setting.get(settings);
return optional.orElse(setting.get(fallbackSettings).orElse(null));
}
}

View File

@ -8,30 +8,23 @@ package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.watcher.ResourceWatcherService;
public class ServerSSLService extends AbstractSSLService {
@Inject
public ServerSSLService(Settings settings, Environment environment) {
super(settings, environment);
public ServerSSLService(Settings settings, Environment environment, Global globalSSLConfiguration,
ResourceWatcherService resourceWatcherService) {
super(settings, environment, globalSSLConfiguration, resourceWatcherService);
}
@Override
protected SSLSettings sslSettings(Settings customSettings) {
SSLSettings sslSettings = new SSLSettings(customSettings, settings);
if (sslSettings.keyStorePath == null) {
throw new IllegalArgumentException("no keystore configured");
protected void validateSSLConfiguration(SSLConfiguration sslConfiguration) {
if (sslConfiguration.keyConfig() == KeyConfig.NONE) {
throw new IllegalArgumentException("a key must be configured to act as a server");
}
if (sslSettings.keyStorePassword == null) {
throw new IllegalArgumentException("no keystore password configured");
}
assert sslSettings.keyPassword != null;
assert sslSettings.trustStorePath != null;
if (sslSettings.trustStorePassword == null) {
throw new IllegalArgumentException("no truststore password configured");
}
return sslSettings;
sslConfiguration.keyConfig().validate();
sslConfiguration.trustConfig().validate();
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Collections;
import java.util.List;
class StoreKeyConfig extends KeyConfig {
final String keyStorePath;
final String keyStorePassword;
final String keyStoreAlgorithm;
final String keyPassword;
final String trustStoreAlgorithm;
StoreKeyConfig(boolean includeSystem, boolean reloadEnabled, String keyStorePath, String keyStorePassword, String keyPassword,
String keyStoreAlgorithm, String trustStoreAlgorithm) {
super(includeSystem, reloadEnabled);
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
this.keyPassword = keyPassword;
this.keyStoreAlgorithm = keyStoreAlgorithm;
this.trustStoreAlgorithm = trustStoreAlgorithm;
}
@Override
X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) {
try (InputStream in = Files.newInputStream(CertUtils.resolvePath(keyStorePath, environment))) {
// TODO remove reliance on JKS since we can PKCS12 stores...
KeyStore ks = KeyStore.getInstance("jks");
assert keyStorePassword != null;
ks.load(in, keyStorePassword.toCharArray());
return CertUtils.keyManagers(ks, keyPassword.toCharArray(), keyStoreAlgorithm);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
}
}
@Override
X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) {
try (InputStream in = Files.newInputStream(CertUtils.resolvePath(keyStorePath, environment))) {
// TODO remove reliance on JKS since we can PKCS12 stores...
KeyStore ks = KeyStore.getInstance("jks");
assert keyStorePassword != null;
ks.load(in, keyStorePassword.toCharArray());
return CertUtils.trustManagers(ks, trustStoreAlgorithm);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
}
}
@Override
void validate() {
if (keyStorePath == null) {
throw new IllegalArgumentException("keystore path must be specified");
} else if (keyStorePassword == null) {
throw new IllegalArgumentException("no keystore password configured");
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.singletonList(CertUtils.resolvePath(keyStorePath, environment));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StoreKeyConfig that = (StoreKeyConfig) o;
if (keyStorePath != null ? !keyStorePath.equals(that.keyStorePath) : that.keyStorePath != null) return false;
if (keyStorePassword != null ? !keyStorePassword.equals(that.keyStorePassword) : that.keyStorePassword != null)
return false;
if (keyStoreAlgorithm != null ? !keyStoreAlgorithm.equals(that.keyStoreAlgorithm) : that.keyStoreAlgorithm != null)
return false;
if (keyPassword != null ? !keyPassword.equals(that.keyPassword) : that.keyPassword != null) return false;
return trustStoreAlgorithm != null ? trustStoreAlgorithm.equals(that.trustStoreAlgorithm) : that.trustStoreAlgorithm == null;
}
@Override
public int hashCode() {
int result = keyStorePath != null ? keyStorePath.hashCode() : 0;
result = 31 * result + (keyStorePassword != null ? keyStorePassword.hashCode() : 0);
result = 31 * result + (keyStoreAlgorithm != null ? keyStoreAlgorithm.hashCode() : 0);
result = 31 * result + (keyPassword != null ? keyPassword.hashCode() : 0);
result = 31 * result + (trustStoreAlgorithm != null ? trustStoreAlgorithm.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "keyStorePath=[" + keyStorePath +
"], keyStoreAlgorithm=[" + keyStoreAlgorithm +
"], trustStoreAlgorithm=[" + trustStoreAlgorithm +
"]";
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Collections;
import java.util.List;
class StoreTrustConfig extends TrustConfig {
final String trustStorePath;
final String trustStorePassword;
final String trustStoreAlgorithm;
StoreTrustConfig(boolean includeSystem, boolean reloadEnabled, String trustStorePath, String trustStorePassword,
String trustStoreAlgorithm) {
super(includeSystem, reloadEnabled);
this.trustStorePath = trustStorePath;
this.trustStorePassword = trustStorePassword;
this.trustStoreAlgorithm = trustStoreAlgorithm;
}
@Override
X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) {
if (trustStorePath == null) {
return null;
}
try (InputStream in = Files.newInputStream(CertUtils.resolvePath(trustStorePath, environment))) {
// TODO remove reliance on JKS since we can PKCS12 stores...
KeyStore trustStore = KeyStore.getInstance("jks");
assert trustStorePassword != null;
trustStore.load(in, trustStorePassword.toCharArray());
return CertUtils.trustManagers(trustStore, trustStoreAlgorithm);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
}
}
@Override
void validate() {
if (trustStorePath != null) {
if (trustStorePassword == null) {
throw new IllegalArgumentException("no truststore password configured");
}
}
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.singletonList(CertUtils.resolvePath(trustStorePath, environment));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StoreTrustConfig that = (StoreTrustConfig) o;
if (trustStorePath != null ? !trustStorePath.equals(that.trustStorePath) : that.trustStorePath != null) return false;
if (trustStorePassword != null ? !trustStorePassword.equals(that.trustStorePassword) : that.trustStorePassword != null)
return false;
return trustStoreAlgorithm != null ? trustStoreAlgorithm.equals(that.trustStoreAlgorithm) : that.trustStoreAlgorithm ==
null;
}
@Override
public int hashCode() {
int result = trustStorePath != null ? trustStorePath.hashCode() : 0;
result = 31 * result + (trustStorePassword != null ? trustStorePassword.hashCode() : 0);
result = 31 * result + (trustStoreAlgorithm != null ? trustStoreAlgorithm.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "trustStorePath=[" + trustStorePath +
"], trustStoreAlgorithm=[" + trustStoreAlgorithm +
"]";
}
}

View File

@ -0,0 +1,299 @@
/*
* 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.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.TrustConfig.Reloadable.Listener;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.watcher.ResourceWatcherService.Frequency;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
abstract class TrustConfig {
protected final boolean includeSystem;
protected final boolean reloadEnabled;
TrustConfig(boolean includeSystem, boolean reloadEnabled) {
this.includeSystem = includeSystem;
this.reloadEnabled = reloadEnabled;
}
final TrustManager[] trustManagers(@Nullable Environment environment, @Nullable ResourceWatcherService resourceWatcherService,
@Nullable Listener listener) {
X509ExtendedTrustManager[] trustManagers = loadAndMergeIfNecessary(environment);
if (reloadEnabled && resourceWatcherService != null && listener != null) {
ReloadableTrustManager reloadableTrustManager = new ReloadableTrustManager(trustManagers[0], environment);
try {
List<Path> filesToMonitor = filesToMonitor(environment);
ChangeListener changeListener = new ChangeListener(filesToMonitor, reloadableTrustManager, listener);
for (Path path : directoriesToMonitor(filesToMonitor)) {
FileWatcher fileWatcher = new FileWatcher(path);
fileWatcher.addListener(changeListener);
resourceWatcherService.add(fileWatcher, Frequency.HIGH);
}
return new X509ExtendedTrustManager[] { reloadableTrustManager };
} catch (IOException e) {
throw new ElasticsearchException("failed to add file watcher", e);
}
}
return trustManagers;
}
abstract X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment);
abstract void validate();
abstract List<Path> filesToMonitor(@Nullable Environment environment);
public abstract String toString();
private X509ExtendedTrustManager[] loadAndMergeIfNecessary(@Nullable Environment environment) {
X509ExtendedTrustManager[] nonSystemTrustManagers = nonSystemTrustManagers(environment);
if (includeSystem) {
return mergeWithSystem(nonSystemTrustManagers);
} else if (nonSystemTrustManagers == null || nonSystemTrustManagers.length == 0) {
return new X509ExtendedTrustManager[0];
}
return nonSystemTrustManagers;
}
private X509ExtendedTrustManager[] mergeWithSystem(X509ExtendedTrustManager[] nonSystemTrustManagers) {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
TrustManager[] systemTrustManagers = tmf.getTrustManagers();
X509ExtendedTrustManager system = findFirstX509TrustManager(systemTrustManagers);
if (nonSystemTrustManagers == null || nonSystemTrustManagers.length == 0) {
return new X509ExtendedTrustManager[] { system };
}
return new X509ExtendedTrustManager[] { new CombiningX509TrustManager(nonSystemTrustManagers[0], system) };
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a trust managers", e);
}
}
private static X509ExtendedTrustManager findFirstX509TrustManager(TrustManager[] trustManagers) {
X509ExtendedTrustManager x509TrustManager = null;
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
// first one wins like in the JDK
x509TrustManager = (X509ExtendedTrustManager) trustManager;
break;
}
}
if (x509TrustManager == null) {
throw new IllegalArgumentException("did not find a X509TrustManager");
}
return x509TrustManager;
}
static Set<Path> directoriesToMonitor(List<Path> filePaths) {
Set<Path> paths = new HashSet<>();
for (Path path : filePaths) {
assert Files.isDirectory(path) == false;
paths.add(path.getParent());
}
return paths;
}
private static class CombiningX509TrustManager extends X509ExtendedTrustManager {
private final X509ExtendedTrustManager first;
private final X509ExtendedTrustManager second;
private final X509Certificate[] acceptedIssuers;
CombiningX509TrustManager(X509ExtendedTrustManager first, X509ExtendedTrustManager second) {
this.first = first;
this.second = second;
X509Certificate[] firstIssuers = first.getAcceptedIssuers();
X509Certificate[] secondIssuers = second.getAcceptedIssuers();
this.acceptedIssuers = new X509Certificate[firstIssuers.length + secondIssuers.length];
System.arraycopy(firstIssuers, 0, acceptedIssuers, 0, firstIssuers.length);
System.arraycopy(secondIssuers, 0, acceptedIssuers, firstIssuers.length, secondIssuers.length);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
try {
first.checkClientTrusted(x509Certificates, s, socket);
} catch (CertificateException e) {
second.checkClientTrusted(x509Certificates, s, socket);
}
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
try {
first.checkServerTrusted(x509Certificates, s, socket);
} catch (CertificateException e) {
second.checkServerTrusted(x509Certificates, s, socket);
}
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
try {
first.checkClientTrusted(x509Certificates, s, sslEngine);
} catch (CertificateException e) {
second.checkClientTrusted(x509Certificates, s, sslEngine);
}
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
try {
first.checkServerTrusted(x509Certificates, s, sslEngine);
} catch (CertificateException e) {
second.checkServerTrusted(x509Certificates, s, sslEngine);
}
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
try {
first.checkClientTrusted(x509Certificates, s);
} catch (CertificateException e) {
second.checkClientTrusted(x509Certificates, s);
}
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
try {
first.checkServerTrusted(x509Certificates, s);
} catch (CertificateException e) {
second.checkServerTrusted(x509Certificates, s);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return acceptedIssuers;
}
}
final class ReloadableTrustManager extends X509ExtendedTrustManager implements Reloadable {
private final Environment environment;
private volatile X509ExtendedTrustManager trustManager;
ReloadableTrustManager(X509ExtendedTrustManager trustManager, @Nullable Environment environment) {
this.trustManager = trustManager;
this.environment = environment;
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s, socket);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s, sslEngine);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s, sslEngine);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
public synchronized void reload() {
X509ExtendedTrustManager[] array = loadAndMergeIfNecessary(environment);
this.trustManager = array[0];
}
synchronized void setTrustManager(X509ExtendedTrustManager trustManager) {
this.trustManager = trustManager;
}
}
interface Reloadable {
void reload();
interface Listener {
void onReload();
void onFailure(Exception e);
}
}
protected static class ChangeListener extends FileChangesListener {
private final List<Path> paths;
private final Reloadable reloadable;
private final Listener listener;
protected ChangeListener(List<Path> paths, Reloadable reloadable, Listener listener) {
this.paths = paths;
this.reloadable = reloadable;
this.listener = listener;
}
@Override
public void onFileDeleted(Path file) {
onFileChanged(file);
}
@Override
public void onFileChanged(Path file) {
for (Path path : paths) {
if (file.equals(path)) {
try {
reloadable.reload();
listener.onReload();
} catch (Exception e) {
listener.onFailure(e);
}
break;
}
}
}
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.shield.support;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import java.util.Optional;
import java.util.function.Function;
public class OptionalSettings {
private OptionalSettings() {}
public static Setting<Optional<Integer>> createInt(String key, Property... properties) {
return new Setting<>(key, s -> null, s -> {
if (s != null) {
return Optional.of(Integer.parseInt(s));
} else {
return Optional.<Integer>ofNullable(null);
}
}, properties);
}
public static Setting<Optional<String>> createString(String key, Property... properties) {
return createString(key, s -> null, properties);
}
public static Setting<Optional<String>> createString(String key, Function<Settings, String> defaultValue, Property... properties) {
return new Setting<>(key, defaultValue, Optional::ofNullable, properties);
}
public static Setting<Optional<String>> createString(String key, Setting<Optional<String>> fallback, Property... properties) {
return new Setting<>(key, fallback, Optional::ofNullable, properties);
}
public static Setting<Optional<TimeValue>> createTimeValue(String key, Property... properties) {
return new Setting<>(key, s-> null, s -> {
if (s != null) {
return Optional.of(TimeValue.parseTimeValue(s, key));
} else {
return Optional.<TimeValue>ofNullable(null);
}
}, properties);
}
}

View File

@ -1,30 +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.shield.support;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import java.util.Optional;
import java.util.function.Function;
public class OptionalStringSetting {
private OptionalStringSetting() {}
public static Setting<Optional<String>> create(String key, Property... properties) {
return create(key, s -> null, properties);
}
public static Setting<Optional<String>> create(String key, Function<Settings, String> defaultValue, Property... properties) {
return new Setting<>(key, defaultValue, Optional::ofNullable, properties);
}
public static Setting<Optional<String>> create(String key, Setting<Optional<String>> fallback, Property... properties) {
return new Setting<>(key, fallback, Optional::ofNullable, properties);
}
}

View File

@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.http.netty.NettyHttpServerTransport;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.ServerSSLService;
import org.elasticsearch.shield.transport.SSLClientAuth;
import org.elasticsearch.shield.transport.filter.IPFilter;
@ -25,6 +26,8 @@ import org.jboss.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
import java.util.Collections;
import static org.elasticsearch.shield.Security.setting;
import static org.elasticsearch.shield.transport.SSLExceptionHelper.isCloseDuringHandshakeException;
import static org.elasticsearch.shield.transport.SSLExceptionHelper.isNotSslRecordException;
@ -44,18 +47,28 @@ public class ShieldNettyHttpServerTransport extends NettyHttpServerTransport {
public static final Setting<SSLClientAuth> CLIENT_AUTH_SETTING =
new Setting<>(setting("http.ssl.client.auth"), CLIENT_AUTH_DEFAULT, SSLClientAuth::parse, Property.NodeScope);
private final IPFilter ipFilter;
private final ServerSSLService sslService;
private final boolean ssl;
private final Settings sslSettings;
private final Global globalSSLConfiguration;
@Inject
public ShieldNettyHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, IPFilter ipFilter,
ServerSSLService sslService, ThreadPool threadPool) {
ServerSSLService sslService, ThreadPool threadPool, Global sslConfig) {
super(settings, networkService, bigArrays, threadPool);
this.ipFilter = ipFilter;
this.ssl = SSL_SETTING.get(settings);
this.sslService = sslService;
this.globalSSLConfiguration = sslConfig;
if (ssl) {
Settings.Builder builder = Settings.builder().put(settings.getByPrefix(setting("http.ssl.")));
builder.remove("client.auth");
builder.remove("enabled");
sslSettings = builder.build();
} else {
sslSettings = Settings.EMPTY;
}
}
@Override
@ -87,6 +100,7 @@ public class ShieldNettyHttpServerTransport extends NettyHttpServerTransport {
@Override
protected void doStart() {
super.doStart();
globalSSLConfiguration.onTransportStart(this.boundAddress(), Collections.emptyMap());
ipFilter.setBoundHttpTransportAddress(this.boundAddress());
}
@ -108,7 +122,7 @@ public class ShieldNettyHttpServerTransport extends NettyHttpServerTransport {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = super.getPipeline();
if (ssl) {
SSLEngine engine = sslService.createSSLEngine();
SSLEngine engine = sslService.createSSLEngine(sslSettings);
engine.setUseClientMode(false);
clientAuth.configure(engine);

View File

@ -18,6 +18,7 @@ import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.ServerSSLService;
import org.elasticsearch.shield.transport.SSLClientAuth;
import org.elasticsearch.shield.transport.filter.IPFilter;
@ -35,6 +36,7 @@ import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.net.InetSocketAddress;
import static org.elasticsearch.shield.Security.featureEnabledSetting;
import static org.elasticsearch.shield.Security.setting;
import static org.elasticsearch.shield.Security.settingPrefix;
import static org.elasticsearch.shield.transport.SSLExceptionHelper.isCloseDuringHandshakeException;
@ -46,10 +48,13 @@ import static org.elasticsearch.shield.transport.SSLExceptionHelper.isNotSslReco
public class ShieldNettyTransport extends NettyTransport {
public static final String CLIENT_AUTH_DEFAULT = SSLClientAuth.REQUIRED.name();
public static final boolean SSL_DEFAULT = false;
public static final boolean SSL_DEFAULT = true;
public static final Setting<Boolean> DEPRECATED_HOSTNAME_VERIFICATION_SETTING =
Setting.boolSetting(setting("ssl.hostname_verification"), true, Property.NodeScope, Property.Filtered, Property.Deprecated);
public static final Setting<Boolean> HOSTNAME_VERIFICATION_SETTING =
Setting.boolSetting(setting("ssl.hostname_verification"), true, Property.NodeScope, Property.Filtered);
Setting.boolSetting(featureEnabledSetting("ssl.hostname_verification"), DEPRECATED_HOSTNAME_VERIFICATION_SETTING,
Property.NodeScope, Property.Filtered);
public static final Setting<Boolean> HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING =
Setting.boolSetting(setting("ssl.hostname_verification.resolve_name"), true, Property.NodeScope, Property.Filtered);
@ -74,6 +79,7 @@ public class ShieldNettyTransport extends NettyTransport {
private final ServerSSLService serverSslService;
private final ClientSSLService clientSSLService;
private final Global globalSSLConfiguration;
private final @Nullable IPFilter authenticator;
private final boolean ssl;
@ -81,17 +87,19 @@ public class ShieldNettyTransport extends NettyTransport {
public ShieldNettyTransport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays,
Version version, @Nullable IPFilter authenticator, @Nullable ServerSSLService serverSSLService,
ClientSSLService clientSSLService, NamedWriteableRegistry namedWriteableRegistry,
CircuitBreakerService circuitBreakerService) {
CircuitBreakerService circuitBreakerService, Global globalSSLConfiguration) {
super(settings, threadPool, networkService, bigArrays, version, namedWriteableRegistry, circuitBreakerService);
this.authenticator = authenticator;
this.ssl = SSL_SETTING.get(settings);
this.serverSslService = serverSSLService;
this.clientSSLService = clientSSLService;
this.globalSSLConfiguration = globalSSLConfiguration;
}
@Override
protected void doStart() {
super.doStart();
globalSSLConfiguration.onTransportStart(boundAddress, profileBoundAddresses);
if (authenticator != null) {
authenticator.setBoundTransportAddress(this.boundAddress(), profileBoundAddresses());
}
@ -139,7 +147,7 @@ public class ShieldNettyTransport extends NettyTransport {
// we can't use the fallback mechanism here since it may not exist in the profile settings and we get the wrong value
// for the profile if they use the old setting
if (PROFILE_SSL_SETTING.exists(profileSettings)) {
return SSL_SETTING.get(profileSettings);
return PROFILE_SSL_SETTING.get(profileSettings);
} else if (DEPRECATED_PROFILE_SSL_SETTING.exists(profileSettings)) {
return DEPRECATED_PROFILE_SSL_SETTING.get(profileSettings);
} else {
@ -256,5 +264,6 @@ public class ShieldNettyTransport extends NettyTransport {
// deprecated transport settings
settingsModule.registerSetting(DEPRECATED_SSL_SETTING);
settingsModule.registerSetting(DEPRECATED_PROFILE_SSL_SETTING);
settingsModule.registerSetting(DEPRECATED_HOSTNAME_VERIFICATION_SETTING);
}
}

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDsjCCApqgAwIBAgIJAKGDffMf8r51MA0GCSqGSIb3DQEBCwUAMEQxGTAXBgNV
BAoTEEVsYXN0aWNzZWFyY2ggQlYxCzAJBgNVBAYTAk5MMRowGAYDVQQDExFYLVBh
Y2sgRGVmYXVsdCBDQTAeFw0xNjA0MTkxNDQ1NDdaFw0yMTA0MTgxNDQ1NDdaMEQx
GTAXBgNVBAoTEEVsYXN0aWNzZWFyY2ggQlYxCzAJBgNVBAYTAk5MMRowGAYDVQQD
ExFYLVBhY2sgRGVmYXVsdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMEtxhGU9TNgfaYFYFTehSkI1tCxPoe9WdvLd6Ag1DggN2Sp65xhDuWLNX4R
mAo7PyXvic+shqfLDjca6R7OgisubldFN3qiw8IwXzjMey/MC0pThsPJS9TQFc6i
a4oNmd4QpvoctqAW8qCOqB+PEUNMx6B54sjqX1/CBGuCB4HZJud//RRbANAf8WKN
nf4z9vM4nk4m6UF0hShCuKdlU+zlDp0/LdkuYn+fn6JmWVNmwMR4Ym9M1i0RKaBI
OE0pgPVIYTRhuU+VyCRGg4ue7tdPlWpps25EihgwEWoieS0lLSGPqEVPC7Msk+HA
kOs4DwJahs0RAep+PJNNrcdSB/0CAwEAAaOBpjCBozAMBgNVHRMEBTADAQH/MB0G
A1UdDgQWBBR5tSUNfOVoB2DrCDjFIauzzxsP+DB0BgNVHSMEbTBrgBR5tSUNfOVo
B2DrCDjFIauzzxsP+KFIpEYwRDEZMBcGA1UEChMQRWxhc3RpY3NlYXJjaCBCVjEL
MAkGA1UEBhMCTkwxGjAYBgNVBAMTEVgtUGFjayBEZWZhdWx0IENBggkAoYN98x/y
vnUwDQYJKoZIhvcNAQELBQADggEBAIv6mt9qW93Y8ZKD7neiYvS6lzN6/yTCRDZ0
sPMYX1BV4dgwIfa3Nc5Iz7fl9MJRQMUaQg1yDmQzoavtRtw8K2Rd1GHtF1pRBwz6
y2th20gVGKXtIyaS5l8oxJ5nlV+GX439In8ovM8Q0CqRN/3X36v77OTTt/FCt24w
8cA6igo9DMfLylfhIU6R3rcImMMPPE/cgXSE6lPCBX0zK2m1h3Bsp9cNAgndUsI6
1xSfnDoO+DuOrze6NX9gzFvy6IwgcTF9vlQ55eXAgy263+k7IhBQydFscNrIFpM5
enqQIqgzcEs5FZcq/V1RWlI5phxsOYaImoN9MJMSO9D3avWOtcE=
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,BF10B2B7AF64C239
xWhoEPwuO8I2qXRG5nSVSPXLltxmzmTxK3VvZPxaf++PTMt+r3F414px763NWigr
Wka3f6p2EJI3huafk47mth02OS268A6hCx3wrp9pq0SrzZVTHqML0OqyNyLJNK8j
azu6IdCFNaFsh6Z5y2PSJ8SWAX11QcYF6g8IRBwGcGr4pmMdJmKEoliuxJ0oGJIy
0hEdeeqiiwEMP2I97CH3g+CtD8eh3+XyNapzSWe7j2Y3wU9lrVUdicUPDOibLVbU
xiLz+HJ0BmNbHF3ND8JiDAZUAYQVjvENHP3VIqm68kzRiHcxsHzVhW6iNz1QTwmR
vugODGkOoYCV0Yfap1Fjawt7bT0emGU7Lbv/E80X43t4kgoeIOGxX96XYIfwJT9C
VVYwtmn85Fr8FmmPRIUYJlMlqm+mvwcRdBxYXKyaamvN7cj8G5pj5A7dgXlSAz7z
JaywteP3GzXdzXAS+ehYrZCkBkdIaMbtNIBFV9ZChDvKWJidGkpajDQtSTNixEnS
42+qMmg4b0AWEcPtlJJJuYZmgl3XXLkfgiyiQ6dnATyq5tE5PxSzHorUCBIv4Bp3
sJCcgsP7TTy3dhr+dyQdsWCVyM9HxUeUzxVj4ULfB6wTAd6duMNHxiIkh3bsxdni
xRm2vNQraW64K/xDTsccbp2emhKpJT3KAU2+ZFAEQUJDMejoQv0b1klOcGZGFKh3
IMmwaSdZfRfh3pIDyRJARHkQ5fPv19SKwtUmwL1ygqR0iRcK7kN9D+lvhzL7RYcP
bKkndgr5jwHdqdGZvrFz2NshcqOwKzKr1tTkXRL8BN6s1s4Az1NQBWyK+bjotF+D
zf3HDUGa94xYF5KFbZCKMUenL4OV+K4qqUvbwqBE/s4rsUkB6p9SSySFc9M8Obmm
U6ZP4hr7/WZpO0AZr6OUX6HMXCeIex0tanINVijDR8Qq7zwa0Nt1t4bnfGbEqPo+
ew5MWjxqEWOb7cJogIjTKokcLqlp4OyVYkP5XdXNqKrnNE23xQNeTeD+W1zZxuJo
KdYQA82NC8vW1uHeEfCiJx4Giziqg6zEhNtF6t0wHebGfLORDICvNsb0Df2tYunj
rtduSaQRVGTT8YRheJFl0B+n8xrLTOqtbFL6PPb/eU4O3C0La8h8VZNSmXBTY/0A
UfeHsxmO7SoAdz8cSiXjvlwVwkt0gbPCbFBTSwALTS0vMas71xI62Dl53EsEup0K
oV9agBfEZyVlse8VC/Js8sZi7cICmCrHcvhKWnju3QB47vPti00/aj6MgE0oLHC4
8CicYeFCyREaxzF+HnOriuG3f82wUfJhtejwefE4xytvJTBTz+5f2j+FI2CL4OGp
R6ix+r/JoygSSIssmxnpcUZQpCxbthfvfBQEvQKq6YDgwgSDfhWAFvLTx45HFDz5
if/wgMDTlL9BP+PcNY4xY73qU9Za1AYdDdS9nWSD3pRz9SZc4gRrLLdLmRzUsCU+
t2Lrvy0AAFVBzf1RZYN0kVArUdgFGiUQyvsyGyFu8FYww23VZuxglIniHVD5JBa+
gymaHiWlKadGciVT552SuYFz89c8nkRFiSEwz/1aLNHP/fFqfVCD2A==
-----END RSA PRIVATE KEY-----

View File

@ -19,8 +19,7 @@ import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.ssl.AbstractSSLService;
import org.elasticsearch.shield.ssl.SSLSettings;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.test.ShieldSettingsSource;
@ -112,7 +111,7 @@ public class SettingsFilterTests extends ShieldIntegTestCase {
.put("xpack.security.ssl.keystore.path", "/path/to/keystore")
.put("xpack.security.ssl.ciphers", "_ciphers")
.put("xpack.security.ssl.supported_protocols", randomFrom(SSLSettings.Globals.DEFAULT_SUPPORTED_PROTOCOLS))
.put("xpack.security.ssl.supported_protocols", randomFrom(Global.DEFAULT_SUPPORTED_PROTOCOLS))
.put("xpack.security.ssl.keystore.password", randomAsciiOfLength(5))
.put("xpack.security.ssl.keystore.algorithm", "_algorithm")
.put("xpack.security.ssl.keystore.key_password", randomAsciiOfLength(5))
@ -124,7 +123,7 @@ public class SettingsFilterTests extends ShieldIntegTestCase {
.put("transport.profiles.client.xpack.security.keystore.path", "/path/to/keystore")
.put("transport.profiles.client.xpack.security.ciphers", "_ciphers")
.put("transport.profiles.client.xpack.security.supported_protocols",
randomFrom(SSLSettings.Globals.DEFAULT_SUPPORTED_PROTOCOLS))
randomFrom(Global.DEFAULT_SUPPORTED_PROTOCOLS))
.put("transport.profiles.client.xpack.security.keystore.password", randomAsciiOfLength(5))
.put("transport.profiles.client.xpack.security.keystore.algorithm", "_algorithm")
.put("transport.profiles.client.xpack.security.keystore.key_password", randomAsciiOfLength(5))

View File

@ -52,14 +52,12 @@ public class ShieldClearScrollTests extends ShieldIntegTestCase {
@Override
protected String configRoles() {
return super.configRoles() +
// note the new line here.. we need to fix this in another PR
// as this throws another exception in the constructor and then we fuck up
"\nallowed_role:\n" +
" cluster:\n" +
" - cluster:admin/indices/scroll/clear_all \n" +
"denied_role:\n" +
" indices:\n" +
" - names: '*'" +
" - names: '*'\n" +
" privileges: [ALL]\n";
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.integration.ldap;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
@ -22,7 +21,6 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
@ -58,10 +56,14 @@ abstract public class AbstractAdLdapRealmTestCase extends ShieldIntegTestCase {
"Philanthropists: [ \"cn=Philanthropists,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com\" ] \n";
static protected RealmConfig realmConfig;
static protected boolean useGlobalSSL;
static protected boolean sslEnabled;
@BeforeClass
public static void setupRealm() {
realmConfig = randomFrom(RealmConfig.values());
useGlobalSSL = randomBoolean();
sslEnabled = randomBoolean();
ESLoggerFactory.getLogger("test").info("running test with realm configuration [{}], with direct group to role mapping [{}]",
realmConfig, realmConfig.mapGroupsAsRoles);
}
@ -74,13 +76,25 @@ abstract public class AbstractAdLdapRealmTestCase extends ShieldIntegTestCase {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Path nodeFiles = createTempDir();
return Settings.builder()
Path store = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks");
Settings.Builder builder = Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(realmConfig.buildSettings())
//we need ssl to the LDAP server
.put(sslSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode"))
.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".files.role_mapping", writeFile(nodeFiles, "role_mapping.yml", configRoleMappings()))
.build();
.put(realmConfig.buildSettings(store, "testnode"))
.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".files.role_mapping", writeFile(nodeFiles, "role_mapping.yml", configRoleMappings()));
if (sslEnabled == false && useGlobalSSL) {
builder.put(sslSettingsForStore(store, "testnode"));
}
return builder.build();
}
@Override
protected boolean sslTransportEnabled() {
return sslEnabled;
}
@Override
protected boolean autoSSLEnabled() {
return sslEnabled && (useGlobalSSL == false);
}
protected String configRoleMappings() {
@ -152,13 +166,7 @@ abstract public class AbstractAdLdapRealmTestCase extends ShieldIntegTestCase {
return UsernamePasswordToken.basicAuthHeaderValue(username, new SecuredString(password.toCharArray()));
}
private Settings sslSettingsForStore(String resourcePathToStore, String password) {
Path store = getDataPath(resourcePathToStore);
if (Files.notExists(store)) {
throw new ElasticsearchException("store path [" + store + "] doesn't exist");
}
private Settings sslSettingsForStore(Path store, String password) {
return Settings.builder()
.put("xpack.security.ssl.keystore.path", store)
.put("xpack.security.ssl.keystore.password", password)
@ -230,13 +238,18 @@ abstract public class AbstractAdLdapRealmTestCase extends ShieldIntegTestCase {
this.mapGroupsAsRoles = randomBoolean();
}
public Settings buildSettings() {
return Settings.builder()
public Settings buildSettings(Path store, String password) {
Settings.Builder builder = Settings.builder()
.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".order", 1)
.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".hostname_verification", false)
.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".unmapped_groups_as_roles", mapGroupsAsRoles)
.put(this.settings)
.build();
.put(this.settings);
if (useGlobalSSL == false) {
builder.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".ssl.truststore.path", store)
.put(SHIELD_AUTHC_REALMS_EXTERNAL + ".ssl.truststore.password", password);
}
return builder.build();
}
//if mapGroupsAsRoles is turned on we don't write anything to the rolemapping file

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.shield.Security;
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
import org.elasticsearch.shield.user.SystemUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.authc.AuthenticationToken;
@ -130,7 +131,7 @@ public class IndexAuditTrailTests extends ShieldIntegTestCase {
final boolean useSSL = useShield && randomBoolean();
logger.info("--> remote indexing enabled. shield enabled: [{}], SSL enabled: [{}], nodes: [{}]", useShield, useSSL, numNodes);
ShieldSettingsSource cluster2SettingsSource =
new ShieldSettingsSource(numNodes, useSSL, systemKey(), createTempDir(), Scope.SUITE) {
new ShieldSettingsSource(numNodes, useSSL, randomBoolean(), systemKey(), createTempDir(), Scope.SUITE) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()
@ -158,6 +159,8 @@ public class IndexAuditTrailTests extends ShieldIntegTestCase {
for (Map.Entry<String, String> entry : cluster2SettingsSource.getClientSSLSettings().getAsMap().entrySet()) {
builder.put("xpack.security.audit.index.client." + entry.getKey(), entry.getValue());
}
} else {
builder.put("xpack.security.audit.index.client." + ShieldNettyTransport.SSL_SETTING.getKey(), false);
}
remoteSettings = builder.build();
}

View File

@ -42,6 +42,7 @@ public class RemoteIndexAuditTrailStartingTests extends ShieldIntegTestCase {
private InternalTestCluster remoteCluster;
private final boolean useSSL = randomBoolean();
private final boolean autoSSL = randomBoolean();
private final boolean localAudit = randomBoolean();
private final String outputs = randomFrom("index", "logfile", "index,logfile");
@ -50,6 +51,11 @@ public class RemoteIndexAuditTrailStartingTests extends ShieldIntegTestCase {
return useSSL;
}
@Override
public boolean autoSSLEnabled() {
return autoSSL;
}
@Override
public Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()
@ -87,7 +93,8 @@ public class RemoteIndexAuditTrailStartingTests extends ShieldIntegTestCase {
// Setup a second test cluster with randomization for number of nodes, shield enabled, and SSL
final int numNodes = randomIntBetween(2, 3);
ShieldSettingsSource cluster2SettingsSource = new ShieldSettingsSource(numNodes, useSSL, systemKey(), createTempDir(), Scope.TEST) {
ShieldSettingsSource cluster2SettingsSource = new ShieldSettingsSource(numNodes, useSSL, autoSSL, systemKey(), createTempDir(),
Scope.TEST) {
@Override
public Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder()
@ -98,10 +105,8 @@ public class RemoteIndexAuditTrailStartingTests extends ShieldIntegTestCase {
.put("xpack.security.audit.index.client.cluster.name", clusterName)
.put("xpack.security.audit.index.client.xpack.security.user", DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD);
if (useSSL) {
for (Map.Entry<String, String> entry : getClientSSLSettings().getAsMap().entrySet()) {
builder.put("xpack.security.audit.index.client." + entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : getClientSSLSettings().getAsMap().entrySet()) {
builder.put("xpack.security.audit.index.client." + entry.getKey(), entry.getValue());
}
return builder.build();
}

View File

@ -17,6 +17,7 @@ import org.elasticsearch.shield.Security;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.test.ShieldSettingsSource;
import org.elasticsearch.test.rest.client.http.HttpResponse;
@ -214,6 +215,7 @@ public class RunAsIntegTests extends ShieldIntegTestCase {
Settings settings = Settings.builder()
.put(extraSettings)
.put("cluster.name", clusterName)
.put(ShieldNettyTransport.SSL_SETTING.getKey(), false)
.build();
return TransportClient.builder()

View File

@ -6,22 +6,13 @@
package org.elasticsearch.shield.authc.activedirectory;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPURL;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.authc.ldap.GroupsResolverTestCase;
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.After;
import org.junit.Before;
import java.nio.file.Path;
import java.util.List;
import java.util.regex.Pattern;
@ -31,39 +22,9 @@ import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
@Network
public class ActiveDirectoryGroupsResolverTests extends ESTestCase {
public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
private LDAPConnection ldapConnection;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
ClientSSLService clientSSLService = new ClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build());
clientSSLService.setEnvironment(env);
LDAPURL ldapurl = new LDAPURL(ActiveDirectorySessionFactoryTests.AD_LDAP_URL);
LDAPConnectionOptions options = new LDAPConnectionOptions();
options.setFollowReferrals(true);
options.setAutoReconnect(true);
options.setAllowConcurrentSocketFactoryUse(true);
options.setConnectTimeoutMillis(Math.toIntExact(SessionFactory.TIMEOUT_DEFAULT.millis()));
options.setResponseTimeoutMillis(SessionFactory.TIMEOUT_DEFAULT.millis());
ldapConnection = new LDAPConnection(clientSSLService.sslSocketFactory(), options, ldapurl.getHost(), ldapurl.getPort(),
BRUCE_BANNER_DN, ActiveDirectorySessionFactoryTests.PASSWORD);
}
@Override
@After
public void tearDown() throws Exception {
super.tearDown();
ldapConnection.close();
}
public void testResolveSubTree() throws Exception {
Settings settings = Settings.builder()
@ -151,4 +112,18 @@ public class ActiveDirectoryGroupsResolverTests extends ESTestCase {
}
}
@Override
protected String ldapUrl() {
return ActiveDirectorySessionFactoryTests.AD_LDAP_URL;
}
@Override
protected String bindDN() {
return BRUCE_BANNER_DN;
}
@Override
protected String bindPassword() {
return ActiveDirectorySessionFactoryTests.PASSWORD;
}
}

View File

@ -16,6 +16,7 @@ import org.elasticsearch.shield.authc.ldap.support.LdapTestCase;
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.Before;
@ -38,23 +39,28 @@ public class ActiveDirectorySessionFactoryTests extends ESTestCase {
private ClientSSLService clientSSLService;
private Settings globalSettings;
private boolean useGlobalSSL;
@Before
public void initializeSslSocketFactory() throws Exception {
useGlobalSSL = randomBoolean();
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
* verification tests since a re-established connection does not perform hostname verification.
*/
clientSSLService = new ClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build());
clientSSLService.setEnvironment(env);
globalSettings = Settings.builder().put("path.home", createTempDir()).build();
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
if (useGlobalSSL) {
builder.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit");
} else {
builder.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false);
}
globalSettings = builder.build();
Environment environment = new Environment(globalSettings);
clientSSLService = new ClientSSLService(globalSettings, new Global(globalSettings));
clientSSLService.setEnvironment(environment);
}
@SuppressWarnings("unchecked")
@ -238,6 +244,13 @@ public class ActiveDirectorySessionFactoryTests extends ESTestCase {
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
Settings settings = LdapTestCase.buildLdapSettings(AD_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE);
if (useGlobalSSL == false) {
settings = Settings.builder()
.put(settings)
.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
.put("ssl.truststore.password", "changeit")
.build();
}
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
@ -257,6 +270,13 @@ public class ActiveDirectorySessionFactoryTests extends ESTestCase {
public void testStandardLdapWithAttributeGroups() throws Exception {
String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
Settings settings = LdapTestCase.buildLdapSettings(new String[] { AD_LDAP_URL }, userTemplate, false);
if (useGlobalSSL == false) {
settings = Settings.builder()
.put(settings)
.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
.put("ssl.truststore.password", "changeit")
.build();
}
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
@ -302,26 +322,30 @@ public class ActiveDirectorySessionFactoryTests extends ESTestCase {
}
}
public static Settings buildAdSettings(String ldapUrl, String adDomainName, boolean hostnameVerification) {
return Settings.builder()
Settings buildAdSettings(String ldapUrl, String adDomainName, boolean hostnameVerification) {
Settings.Builder builder = Settings.builder()
.put(ActiveDirectorySessionFactory.URLS_SETTING, ldapUrl)
.put(ActiveDirectorySessionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(ActiveDirectorySessionFactory.HOSTNAME_VERIFICATION_SETTING, hostnameVerification)
.build();
.put(ActiveDirectorySessionFactory.HOSTNAME_VERIFICATION_SETTING, hostnameVerification);
if (useGlobalSSL == false) {
builder.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
.put("ssl.truststore.password", "changeit");
}
return builder.build();
}
public static Settings buildAdSettings(String ldapUrl, String adDomainName, LdapSearchScope scope, String userSearchDN) {
return buildAdSettings(ldapUrl, adDomainName, userSearchDN, scope, true);
}
public static Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN, LdapSearchScope scope,
Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN, LdapSearchScope scope,
boolean hostnameVerification) {
return Settings.builder()
Settings.Builder builder = Settings.builder()
.putArray(ActiveDirectorySessionFactory.URLS_SETTING, ldapUrl)
.put(ActiveDirectorySessionFactory.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
.put(ActiveDirectorySessionFactory.AD_USER_SEARCH_SCOPE_SETTING, scope)
.put(ActiveDirectorySessionFactory.HOSTNAME_VERIFICATION_SETTING, hostnameVerification)
.build();
.put(ActiveDirectorySessionFactory.HOSTNAME_VERIFICATION_SETTING, hostnameVerification);
if (useGlobalSSL == false) {
builder.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
.put("ssl.truststore.password", "changeit");
}
return builder.build();
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.shield.authc.ldap;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPURL;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.junit.After;
import org.junit.Before;
import java.nio.file.Path;
public abstract class GroupsResolverTestCase extends ESTestCase {
protected LDAPConnection ldapConnection;
protected abstract String ldapUrl();
protected abstract String bindDN();
protected abstract String bindPassword();
@Before
public void setUpLdapConnection() throws Exception {
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
boolean useGlobalSSL = randomBoolean();
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
if (useGlobalSSL) {
builder.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit");
} else {
builder.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false);
}
Settings settings = builder.build();
Environment env = new Environment(settings);
ClientSSLService clientSSLService = new ClientSSLService(settings, new Global(settings));
clientSSLService.setEnvironment(env);
LDAPURL ldapurl = new LDAPURL(ldapUrl());
LDAPConnectionOptions options = new LDAPConnectionOptions();
options.setFollowReferrals(true);
options.setAutoReconnect(true);
options.setAllowConcurrentSocketFactoryUse(true);
options.setConnectTimeoutMillis(Math.toIntExact(SessionFactory.TIMEOUT_DEFAULT.millis()));
options.setResponseTimeoutMillis(SessionFactory.TIMEOUT_DEFAULT.millis());
Settings connectionSettings;
if (useGlobalSSL) {
connectionSettings = Settings.EMPTY;
} else {
connectionSettings = Settings.builder().put("keystore.path", keystore)
.put("keystore.password", "changeit").build();
}
ldapConnection = new LDAPConnection(clientSSLService.sslSocketFactory(connectionSettings), options, ldapurl.getHost(),
ldapurl.getPort(), bindDN(), bindPassword());
}
@After
public void tearDownLdapConnection() throws Exception {
ldapConnection.close();
}
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.shield.authc.ldap.support.LdapTestCase;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.watcher.Watcher;
@ -70,10 +71,11 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
* verification tests since a re-established connection does not perform hostname verification.
*/
clientSSLService = new ClientSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build());
.build();
clientSSLService = new ClientSSLService(settings, new Global(settings));
clientSSLService.setEnvironment(env);
globalSettings = Settings.builder().put("path.home", createTempDir()).build();

View File

@ -15,6 +15,7 @@ import org.elasticsearch.shield.authc.ldap.support.LdapTestCase;
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
import org.elasticsearch.shield.authc.support.SecuredStringTests;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.Before;
@ -31,32 +32,37 @@ public class OpenLdapTests extends ESTestCase {
public static final String OPEN_LDAP_URL = "ldaps://54.200.235.244:636";
public static final String PASSWORD = "NickFuryHeartsES";
private boolean useGlobalSSL;
private ClientSSLService clientSSLService;
private Settings globalSettings;
@Before
public void initializeSslSocketFactory() throws Exception {
useGlobalSSL = randomBoolean();
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
/*
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
* verification tests since a re-established connection does not perform hostname verification.
*/
clientSSLService = new ClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build());
clientSSLService.setEnvironment(env);
globalSettings = Settings.builder().put("path.home", createTempDir()).build();
Settings.Builder builder = Settings.builder().put("path.home", createTempDir());
if (useGlobalSSL) {
builder.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit");
} else {
builder.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false);
}
globalSettings = builder.build();
Environment environment = new Environment(globalSettings);
clientSSLService = new ClientSSLService(globalSettings, new Global(globalSettings));
clientSSLService.setEnvironment(environment);
}
public void testConnect() throws Exception {
//openldap does not use cn as naming attributes by default
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
RealmConfig config = new RealmConfig("oldap-test", LdapTestCase.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
LdapSearchScope.ONE_LEVEL), globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
@ -73,7 +79,7 @@ public class OpenLdapTests extends ESTestCase {
String groupSearchBase = "cn=Avengers,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
RealmConfig config = new RealmConfig("oldap-test", LdapTestCase.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
LdapSearchScope.BASE), globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
@ -89,7 +95,7 @@ public class OpenLdapTests extends ESTestCase {
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
Settings settings = Settings.builder()
.put(LdapTestCase.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
.put(buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
.put("group_search.filter", "(&(objectclass=posixGroup)(memberUID={0}))")
.put("group_search.user_attribute", "uid")
.build();
@ -106,7 +112,7 @@ public class OpenLdapTests extends ESTestCase {
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
Settings settings = Settings.builder()
.put(LdapTestCase.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
.put(buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
.put(SessionFactory.HOSTNAME_VERIFICATION_SETTING, false)
.put(SessionFactory.TIMEOUT_TCP_READ_SETTING, "1ms") //1 millisecond
.build();
@ -127,7 +133,7 @@ public class OpenLdapTests extends ESTestCase {
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
Settings settings = Settings.builder()
.put(LdapTestCase.buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
.put(buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
.put(LdapSessionFactory.HOSTNAME_VERIFICATION_SETTING, true)
.build();
@ -141,4 +147,16 @@ public class OpenLdapTests extends ESTestCase {
assertThat(e.getMessage(), containsString("failed to connect to any LDAP servers"));
}
}
Settings buildLdapSettings(String ldapUrl, String userTemplate, String groupSearchBase, LdapSearchScope scope) {
Settings baseSettings = LdapTestCase.buildLdapSettings(ldapUrl, userTemplate, groupSearchBase, scope);
if (useGlobalSSL) {
return baseSettings;
}
return Settings.builder()
.put(baseSettings)
.put("ssl.truststore.path", getDataPath("../ldap/support/ldaptrust.jks"))
.put("ssl.truststore.password", "changeit")
.build();
}
}

View File

@ -5,23 +5,13 @@
*/
package org.elasticsearch.shield.authc.ldap;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPURL;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.After;
import org.junit.Before;
import java.nio.file.Path;
import java.util.List;
import static org.hamcrest.Matchers.containsInAnyOrder;
@ -31,41 +21,10 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@Network
public class SearchGroupsResolverTests extends ESTestCase {
public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public static final String BRUCE_BANNER_DN = "uid=hulk,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
private LDAPConnection ldapConnection;
@Before
public void setup() throws Exception {
super.setUp();
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
ClientSSLService clientSSLService = new ClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build());
clientSSLService.setEnvironment(env);
LDAPURL ldapurl = new LDAPURL(OpenLdapTests.OPEN_LDAP_URL);
LDAPConnectionOptions options = new LDAPConnectionOptions();
options.setFollowReferrals(true);
options.setAutoReconnect(true);
options.setAllowConcurrentSocketFactoryUse(true);
options.setConnectTimeoutMillis(Math.toIntExact(SessionFactory.TIMEOUT_DEFAULT.millis()));
options.setResponseTimeoutMillis(SessionFactory.TIMEOUT_DEFAULT.millis());
ldapConnection = new LDAPConnection(clientSSLService.sslSocketFactory(), options, ldapurl.getHost(), ldapurl.getPort(),
BRUCE_BANNER_DN, OpenLdapTests.PASSWORD);
}
@Override
@After
public void tearDown() throws Exception {
super.tearDown();
ldapConnection.close();
}
public void testResolveSubTree() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
@ -174,4 +133,19 @@ public class SearchGroupsResolverTests extends ESTestCase {
String attribute = resolver.readUserAttribute(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE);
assertThat(attribute, is(notNullValue()));
}
@Override
protected String ldapUrl() {
return OpenLdapTests.OPEN_LDAP_URL;
}
@Override
protected String bindDN() {
return BRUCE_BANNER_DN;
}
@Override
protected String bindPassword() {
return OpenLdapTests.PASSWORD;
}
}

View File

@ -5,22 +5,12 @@
*/
package org.elasticsearch.shield.authc.ldap;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPURL;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.authc.activedirectory.ActiveDirectorySessionFactoryTests;
import org.elasticsearch.shield.authc.ldap.support.SessionFactory;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.After;
import org.junit.Before;
import java.nio.file.Path;
import java.util.List;
import static org.hamcrest.Matchers.containsInAnyOrder;
@ -29,40 +19,9 @@ import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItems;
@Network
public class UserAttributeGroupsResolverTests extends ESTestCase {
public class UserAttributeGroupsResolverTests extends GroupsResolverTestCase {
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
private LDAPConnection ldapConnection;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
Path keystore = getDataPath("../ldap/support/ldaptrust.jks");
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
ClientSSLService clientSSLService = new ClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build());
clientSSLService.setEnvironment(env);
LDAPURL ldapurl = new LDAPURL(ActiveDirectorySessionFactoryTests.AD_LDAP_URL);
LDAPConnectionOptions options = new LDAPConnectionOptions();
options.setFollowReferrals(true);
options.setAutoReconnect(true);
options.setAllowConcurrentSocketFactoryUse(true);
options.setConnectTimeoutMillis(Math.toIntExact(SessionFactory.TIMEOUT_DEFAULT.millis()));
options.setResponseTimeoutMillis(SessionFactory.TIMEOUT_DEFAULT.millis());
ldapConnection = new LDAPConnection(clientSSLService.sslSocketFactory(), options, ldapurl.getHost(), ldapurl.getPort(),
BRUCE_BANNER_DN, ActiveDirectorySessionFactoryTests.PASSWORD);
}
@Override
@After
public void tearDown() throws Exception {
super.tearDown();
ldapConnection.close();
}
public void testResolve() throws Exception {
//falling back on the 'memberOf' attribute
@ -92,4 +51,19 @@ public class UserAttributeGroupsResolverTests extends ESTestCase {
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
assertThat(groups, empty());
}
@Override
protected String ldapUrl() {
return ActiveDirectorySessionFactoryTests.AD_LDAP_URL;
}
@Override
protected String bindDN() {
return BRUCE_BANNER_DN;
}
@Override
protected String bindPassword() {
return ActiveDirectorySessionFactoryTests.PASSWORD;
}
}

View File

@ -36,6 +36,7 @@ import java.nio.file.Path;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Locale;
import java.util.Map.Entry;
import static org.elasticsearch.test.ShieldSettingsSource.getSSLSettingsForStore;
import static org.hamcrest.Matchers.containsString;
@ -73,6 +74,11 @@ public class PkiAuthenticationTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
public void testTransportClientCanAuthenticateViaPki() {
Settings settings = getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode");
try (TransportClient client = createTransportClient(settings)) {
@ -138,8 +144,18 @@ public class PkiAuthenticationTests extends ShieldIntegTestCase {
}
private TransportClient createTransportClient(Settings additionalSettings) {
Settings.Builder builder = Settings.builder()
.put(transportClientSettings())
Settings clientSettings = transportClientSettings();
if (additionalSettings.getByPrefix("xpack.security.ssl.").isEmpty() == false) {
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : clientSettings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.security.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
clientSettings = builder.build();
}
Settings.Builder builder = Settings.builder().put(clientSettings)
.put(additionalSettings)
.put("cluster.name", internalCluster().getClusterName());
builder.remove(Security.USER_SETTING.getKey());

View File

@ -77,6 +77,11 @@ public class PkiOptionalClientAuthTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
public void testRestClientWithoutClientCertificate() throws Exception {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);

View File

@ -52,7 +52,7 @@ public class PkiRealmTests extends ESTestCase {
}
public void testExtractToken() throws Exception {
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.cert"));
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"));
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putTransient(PkiRealm.PKI_CERT_HEADER_NAME, new X509Certificate[] { certificate });
PkiRealm realm = new PkiRealm(new RealmConfig("", Settings.EMPTY, globalSettings), mock(DnRoleMapper.class));
@ -64,7 +64,7 @@ public class PkiRealmTests extends ESTestCase {
}
public void testAuthenticateBasedOnCertToken() throws Exception {
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.cert"));
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"));
X509AuthenticationToken token = new X509AuthenticationToken(new X509Certificate[] { certificate }, "Elasticsearch Test Node",
"CN=Elasticsearch Test Node,");
DnRoleMapper roleMapper = mock(DnRoleMapper.class);
@ -79,7 +79,7 @@ public class PkiRealmTests extends ESTestCase {
}
public void testCustomUsernamePattern() throws Exception {
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.cert"));
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"));
DnRoleMapper roleMapper = mock(DnRoleMapper.class);
PkiRealm realm = new PkiRealm(new RealmConfig("", Settings.builder().put("username_pattern", "OU=(.*?),").build(), globalSettings),
roleMapper);
@ -96,7 +96,7 @@ public class PkiRealmTests extends ESTestCase {
}
public void testVerificationUsingATruststore() throws Exception {
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.cert"));
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"));
DnRoleMapper roleMapper = mock(DnRoleMapper.class);
Settings settings = Settings.builder()
.put("truststore.path", getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks"))
@ -117,7 +117,7 @@ public class PkiRealmTests extends ESTestCase {
}
public void testVerificationFailsUsingADifferentTruststore() throws Exception {
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.cert"));
X509Certificate certificate = readCert(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"));
DnRoleMapper roleMapper = mock(DnRoleMapper.class);
Settings settings = Settings.builder()
.put("truststore.path", getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-client-profile.jks"))

View File

@ -57,6 +57,11 @@ public class PkiWithoutClientAuthenticationTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
@Override
public Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()

View File

@ -0,0 +1,60 @@
/*
* 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.shield.ssl;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.shield.Security;
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.XPackPlugin;
import static org.elasticsearch.test.ShieldSettingsSource.DEFAULT_PASSWORD;
import static org.elasticsearch.test.ShieldSettingsSource.DEFAULT_USER_NAME;
import static org.hamcrest.Matchers.containsString;
public class AutoSSLIntegTests extends ShieldIntegTestCase {
@Override
public boolean sslTransportEnabled() {
return true;
}
@Override
public boolean autoSSLEnabled() {
return true;
}
public void testTransportClient() {
String clusterName = internalCluster().getClusterName();
TransportAddress transportAddress = randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses());
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class)
.settings(Settings.builder()
.put("cluster.name", clusterName)
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD))
.build()) {
transportClient.addTransportAddress(transportAddress);
assertGreenClusterState(transportClient);
}
// now try with SSL disabled and it should fail
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class)
.settings(Settings.builder()
.put("cluster.name", clusterName)
.put(ShieldNettyTransport.SSL_SETTING.getKey(), false)
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD))
.build()) {
transportClient.addTransportAddress(transportAddress);
assertGreenClusterState(transportClient);
fail("should not have been able to connect");
} catch (NoNodeAvailableException e) {
assertThat(e.getMessage(), containsString("None of the configured nodes are available"));
}
}
}

View File

@ -0,0 +1,144 @@
/*
* 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.shield.ssl;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.test.ESTestCase;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
/**
* Unit tests for cert utils
*/
public class CertUtilsTests extends ESTestCase {
public void testSerialNotRepeated() {
int iterations = scaledRandomIntBetween(10, 100);
List<BigInteger> list = new ArrayList<>(iterations);
for (int i = 0; i < iterations; i++) {
BigInteger serial = CertUtils.getSerial();
assertThat(list.contains(serial), is(false));
list.add(serial);
}
}
public void testGenerateKeyPair() throws Exception {
KeyPair keyPair = CertUtils.generateKeyPair();
assertThat(keyPair.getPrivate().getAlgorithm(), is("RSA"));
assertThat(keyPair.getPublic().getAlgorithm(), is("RSA"));
}
public void testReadKeysCorrectly() throws Exception {
// read in keystore version
Path keystorePath = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks");
Key key;
try (InputStream in = Files.newInputStream(keystorePath)) {
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(in, "testnode".toCharArray());
key = keyStore.getKey("testnode", "testnode".toCharArray());
}
assertThat(key, notNullValue());
assertThat(key, instanceOf(PrivateKey.class));
PrivateKey privateKey;
try (Reader reader = Files.newBufferedReader(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.pem"),
StandardCharsets.UTF_8)) {
privateKey = CertUtils.readPrivateKey(reader, "testnode".toCharArray());
}
assertThat(privateKey, notNullValue());
assertThat(privateKey, equalTo(key));
}
public void testReadCertsCorrectly() throws Exception {
// read in keystore version
Path keystorePath = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks");
Certificate certificate;
try (InputStream in = Files.newInputStream(keystorePath)) {
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(in, "testnode".toCharArray());
certificate = keyStore.getCertificate("testnode");
}
assertThat(certificate, notNullValue());
assertThat(certificate, instanceOf(X509Certificate.class));
Certificate pemCert;
try (Reader reader = Files.newBufferedReader(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"),
StandardCharsets.UTF_8)) {
List<Certificate> certificateList = new ArrayList<>(1);
CertUtils.readCertificates(reader, certificateList, CertificateFactory.getInstance("X.509"));
assertThat(certificateList.size(), is(1));
pemCert = certificateList.get(0);
}
assertThat(pemCert, notNullValue());
assertThat(pemCert, equalTo(certificate));
}
public void testSubjectAlternativeNames() throws Exception {
final boolean resolveName = randomBoolean();
InetAddress address = InetAddresses.forString("127.0.0.1");
GeneralNames generalNames = CertUtils.getSubjectAlternativeNames(resolveName, Collections.singleton(address));
assertThat(generalNames, notNullValue());
GeneralName[] generalNameArray = generalNames.getNames();
assertThat(generalNameArray, notNullValue());
if (resolveName) {
assertThat(generalNameArray.length, is(2));
int firstType = generalNameArray[0].getTagNo();
if (firstType == GeneralName.iPAddress) {
assertThat(generalNameArray[1].getTagNo(), is(GeneralName.dNSName));
} else if (firstType == GeneralName.dNSName) {
assertThat(generalNameArray[1].getTagNo(), is(GeneralName.iPAddress));
} else {
fail("unknown tag value: " + firstType);
}
} else {
assertThat(generalNameArray.length, is(1));
assertThat(generalNameArray[0].getTagNo(), is(GeneralName.iPAddress));
}
}
public void testIsAnyLocalAddress() throws Exception {
InetAddress address = mock(InetAddress.class);
when(address.isAnyLocalAddress()).thenReturn(true);
GeneralNames generalNames = CertUtils.getSubjectAlternativeNames(randomBoolean(), Collections.singleton(address));
assertThat(generalNames, notNullValue());
GeneralName[] generalNameArray = generalNames.getNames();
assertThat(generalNameArray, notNullValue());
verify(address).isAnyLocalAddress();
verifyNoMoreInteractions(address);
}
}

View File

@ -12,6 +12,7 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.Before;
@ -39,6 +40,7 @@ import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class ClientSSLServiceTests extends ESTestCase {
Environment env;
Path testclientStore;
@ -50,13 +52,15 @@ public class ClientSSLServiceTests extends ESTestCase {
public void testThatInvalidProtocolThrowsException() throws Exception {
try {
new ClientSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.protocol", "non-existing")
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.put("xpack.security.ssl.truststore.path", testclientStore)
.put("xpack.security.ssl.truststore.password", "testclient")
.build()).createSSLEngine();
.build();
ClientSSLService clientSSLService = new ClientSSLService(settings, new Global(settings));
clientSSLService.createSSLEngine();
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize the SSLContext"));
@ -148,7 +152,9 @@ public class ClientSSLServiceTests extends ESTestCase {
}
public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.EMPTY);
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false)
.build());
SSLEngine sslEngine = sslService.createSSLEngine();
assertThat(sslEngine, notNullValue());
}
@ -173,7 +179,8 @@ public class ClientSSLServiceTests extends ESTestCase {
@Network
public void testThatSSLContextWithoutSettingsWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.EMPTY);
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false).build());
SSLContext sslContext = sslService.sslContext();
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
@ -184,12 +191,24 @@ public class ClientSSLServiceTests extends ESTestCase {
}
@Network
public void testThatSSLContextWithKeystoreDoesNotTrustAllPublicCAs() throws Exception {
public void testThatSSLContextTrustsJDKTrustedCAs() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
SSLContext sslContext = sslService.sslContext();
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA which will succeed because the JDK
// certs are trusted by default
client.execute(new HttpGet("https://www.elastic.co/")).close();
}
sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), "false")
.build());
sslContext = sslService.sslContext();
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
// This will result in a SSLHandshakeException because the truststore is the testnodestore, which doesn't
@ -226,7 +245,7 @@ public class ClientSSLServiceTests extends ESTestCase {
}
public void testValidCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(SSLSettings.Globals.DEFAULT_CIPHERS);
List<String> ciphers = new ArrayList<>(Global.DEFAULT_CIPHERS);
ciphers.add("foo");
ciphers.add("bar");
ClientSSLService sslService = createClientSSLService(Settings.builder()
@ -246,7 +265,7 @@ public class ClientSSLServiceTests extends ESTestCase {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("failed loading cipher suites [[foo, bar]]"));
assertThat(e.getMessage(), is("none of the ciphers [foo, bar] are supported by this JVM"));
}
}
@ -255,8 +274,8 @@ public class ClientSSLServiceTests extends ESTestCase {
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
SSLSocketFactory factory = sslService.sslSocketFactory();
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), sslService.ciphers());
SSLSocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY);
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), sslService.ciphers(), false);
assertThat(factory.getDefaultCipherSuites(), is(ciphers));
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
@ -266,7 +285,7 @@ public class ClientSSLServiceTests extends ESTestCase {
}
ClientSSLService createClientSSLService(Settings settings) {
ClientSSLService clientSSLService = new ClientSSLService(settings);
ClientSSLService clientSSLService = new ClientSSLService(settings, new Global(settings));
clientSSLService.setEnvironment(env);
return clientSSLService;
}

View File

@ -0,0 +1,751 @@
/*
* 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.shield.ssl;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Custom;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.TrustConfig.Reloadable.Listener;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class SSLConfigurationTests extends ESTestCase {
private static final Settings NO_AUTO_GEN = Settings.builder()
.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false)
.build();
public void testThatSSLConfigurationHasCorrectDefaults() {
SSLConfiguration globalConfig = new Global(Settings.EMPTY);
assertThat(globalConfig.keyConfig(), instanceOf(AutoGeneratedKeyConfig.class));
assertThat(globalConfig.trustConfig(), sameInstance(globalConfig.keyConfig()));
assertThat(globalConfig.sessionCacheSize(), is(equalTo(Global.DEFAULT_SESSION_CACHE_SIZE)));
assertThat(globalConfig.sessionCacheTimeout(), is(equalTo(Global.DEFAULT_SESSION_CACHE_TIMEOUT)));
assertThat(globalConfig.protocol(), is(equalTo(Global.DEFAULT_PROTOCOL)));
SSLConfiguration scopedConfig = new Custom(Settings.EMPTY, globalConfig);
assertThat(scopedConfig.keyConfig(), sameInstance(globalConfig.keyConfig()));
assertThat(scopedConfig.trustConfig(), sameInstance(globalConfig.trustConfig()));
assertThat(globalConfig.sessionCacheSize(), is(equalTo(Global.DEFAULT_SESSION_CACHE_SIZE)));
assertThat(globalConfig.sessionCacheTimeout(), is(equalTo(Global.DEFAULT_SESSION_CACHE_TIMEOUT)));
assertThat(globalConfig.protocol(), is(equalTo(Global.DEFAULT_PROTOCOL)));
}
public void testThatSSLConfigurationWithoutAutoGenHasCorrectDefaults() {
SSLConfiguration globalSettings = new Global(NO_AUTO_GEN);
SSLConfiguration scopedSettings = new Custom(Settings.EMPTY, globalSettings);
for (SSLConfiguration sslConfiguration : Arrays.asList(globalSettings, scopedSettings)) {
assertThat(sslConfiguration.keyConfig(), sameInstance(KeyConfig.NONE));
assertThat(sslConfiguration.sessionCacheSize(), is(equalTo(Global.DEFAULT_SESSION_CACHE_SIZE)));
assertThat(sslConfiguration.sessionCacheTimeout(), is(equalTo(Global.DEFAULT_SESSION_CACHE_TIMEOUT)));
assertThat(sslConfiguration.protocol(), is(equalTo(Global.DEFAULT_PROTOCOL)));
assertThat(sslConfiguration.trustConfig(), notNullValue());
assertThat(sslConfiguration.trustConfig(), is(instanceOf(StoreTrustConfig.class)));
StoreTrustConfig ksTrustInfo = (StoreTrustConfig) sslConfiguration.trustConfig();
assertThat(ksTrustInfo.trustStorePath, is(nullValue()));
assertThat(ksTrustInfo.trustStorePassword, is(nullValue()));
assertThat(ksTrustInfo.trustStoreAlgorithm, is(nullValue()));
}
}
public void testThatOnlyKeystoreInSettingsSetsTruststoreSettings() {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", "path")
.put("xpack.security.ssl.keystore.password", "password")
.build();
Settings profileSettings = settings.getByPrefix("xpack.security.ssl.");
// Pass settings in as component settings
SSLConfiguration globalSettings = new Global(settings);
SSLConfiguration scopedSettings = new Custom(profileSettings, globalSettings);
SSLConfiguration scopedEmptyGlobalSettings =
new Custom(profileSettings, new Global(NO_AUTO_GEN));
for (SSLConfiguration sslConfiguration : Arrays.asList(globalSettings, scopedSettings, scopedEmptyGlobalSettings)) {
assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class));
StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig();
assertThat(ksKeyInfo.keyStorePath, is(equalTo("path")));
assertThat(ksKeyInfo.keyStorePassword, is(equalTo("password")));
assertThat(ksKeyInfo.keyPassword, is(equalTo(ksKeyInfo.keyStorePassword)));
assertThat(ksKeyInfo.keyStoreAlgorithm, is(KeyManagerFactory.getDefaultAlgorithm()));
assertThat(sslConfiguration.trustConfig(), is(sameInstance(ksKeyInfo)));
}
}
public void testThatKeyPasswordCanBeSet() {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", "path")
.put("xpack.security.ssl.keystore.password", "password")
.put("xpack.security.ssl.keystore.key_password", "key")
.build();
SSLConfiguration sslConfiguration = new Global(settings);
assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class));
StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig();
assertThat(ksKeyInfo.keyStorePassword, is(equalTo("password")));
assertThat(ksKeyInfo.keyPassword, is(equalTo("key")));
// Pass settings in as profile settings
Settings profileSettings = settings.getByPrefix("xpack.security.ssl.");
SSLConfiguration sslConfiguration1 = new Custom(profileSettings,
randomBoolean() ? sslConfiguration : new Global(NO_AUTO_GEN));
assertThat(sslConfiguration1.keyConfig(), instanceOf(StoreKeyConfig.class));
ksKeyInfo = (StoreKeyConfig) sslConfiguration1.keyConfig();
assertThat(ksKeyInfo.keyStorePassword, is(equalTo("password")));
assertThat(ksKeyInfo.keyPassword, is(equalTo("key")));
}
public void testThatProfileSettingsOverrideServiceSettings() {
Settings profileSettings = Settings.builder()
.put("keystore.path", "path")
.put("keystore.password", "password")
.put("keystore.key_password", "key")
.put("keystore.algorithm", "algo")
.put("truststore.path", "trust path")
.put("truststore.password", "password for trust")
.put("truststore.algorithm", "trusted")
.put("protocol", "ssl")
.put("session.cache_size", "3")
.put("session.cache_timeout", "10m")
.build();
Settings serviceSettings = Settings.builder()
.put("xpack.security.ssl.keystore.path", "comp path")
.put("xpack.security.ssl.keystore.password", "comp password")
.put("xpack.security.ssl.keystore.key_password", "comp key")
.put("xpack.security.ssl.keystore.algorithm", "comp algo")
.put("xpack.security.ssl.truststore.path", "comp trust path")
.put("xpack.security.ssl.truststore.password", "comp password for trust")
.put("xpack.security.ssl.truststore.algorithm", "comp trusted")
.put("xpack.security.ssl.protocol", "tls")
.put("xpack.security.ssl.session.cache_size", "7")
.put("xpack.security.ssl.session.cache_timeout", "20m")
.build();
SSLConfiguration globalSettings = new Global(serviceSettings);
SSLConfiguration sslConfiguration = new Custom(profileSettings, globalSettings);
assertThat(sslConfiguration.keyConfig(), instanceOf(StoreKeyConfig.class));
StoreKeyConfig ksKeyInfo = (StoreKeyConfig) sslConfiguration.keyConfig();
assertThat(ksKeyInfo.keyStorePath, is(equalTo("path")));
assertThat(ksKeyInfo.keyStorePassword, is(equalTo("password")));
assertThat(ksKeyInfo.keyPassword, is(equalTo("key")));
assertThat(ksKeyInfo.keyStoreAlgorithm, is(equalTo("algo")));
assertThat(sslConfiguration.trustConfig(), instanceOf(StoreTrustConfig.class));
StoreTrustConfig ksTrustInfo = (StoreTrustConfig) sslConfiguration.trustConfig();
assertThat(ksTrustInfo.trustStorePath, is(equalTo("trust path")));
assertThat(ksTrustInfo.trustStorePassword, is(equalTo("password for trust")));
assertThat(ksTrustInfo.trustStoreAlgorithm, is(equalTo("trusted")));
assertThat(sslConfiguration.protocol(), is(equalTo("ssl")));
assertThat(sslConfiguration.sessionCacheSize(), is(equalTo(3)));
assertThat(sslConfiguration.sessionCacheTimeout(), is(equalTo(TimeValue.timeValueMinutes(10L))));
}
public void testThatEmptySettingsAreEqual() {
SSLConfiguration sslConfiguration = new Global(NO_AUTO_GEN);
SSLConfiguration sslConfiguration1 = new Global(NO_AUTO_GEN);
assertThat(sslConfiguration.equals(sslConfiguration1), is(equalTo(true)));
assertThat(sslConfiguration1.equals(sslConfiguration), is(equalTo(true)));
assertThat(sslConfiguration.equals(sslConfiguration), is(equalTo(true)));
assertThat(sslConfiguration1.equals(sslConfiguration1), is(equalTo(true)));
SSLConfiguration profileSSLConfiguration = new Custom(Settings.EMPTY, sslConfiguration);
assertThat(sslConfiguration.equals(profileSSLConfiguration), is(equalTo(true)));
assertThat(profileSSLConfiguration.equals(sslConfiguration), is(equalTo(true)));
assertThat(profileSSLConfiguration.equals(profileSSLConfiguration), is(equalTo(true)));
}
public void testThatSettingsWithDifferentKeystoresAreNotEqual() {
SSLConfiguration sslConfiguration = new Global(Settings.builder()
.put("xpack.security.ssl.keystore.path", "path").build());
SSLConfiguration sslConfiguration1 = new Global(Settings.builder()
.put("xpack.security.ssl.keystore.path", "path1").build());
assertThat(sslConfiguration.equals(sslConfiguration1), is(equalTo(false)));
assertThat(sslConfiguration1.equals(sslConfiguration), is(equalTo(false)));
assertThat(sslConfiguration.equals(sslConfiguration), is(equalTo(true)));
assertThat(sslConfiguration1.equals(sslConfiguration1), is(equalTo(true)));
}
public void testThatSettingsWithDifferentProtocolsAreNotEqual() {
SSLConfiguration sslConfiguration = new Global(Settings.builder()
.put("xpack.security.ssl.protocol", "ssl").build());
SSLConfiguration sslConfiguration1 = new Global(Settings.builder()
.put("xpack.security.ssl.protocol", "tls").build());
assertThat(sslConfiguration.equals(sslConfiguration1), is(equalTo(false)));
assertThat(sslConfiguration1.equals(sslConfiguration), is(equalTo(false)));
assertThat(sslConfiguration.equals(sslConfiguration), is(equalTo(true)));
assertThat(sslConfiguration1.equals(sslConfiguration1), is(equalTo(true)));
}
public void testThatSettingsWithDifferentTruststoresAreNotEqual() {
SSLConfiguration sslConfiguration = new Global(Settings.builder()
.put("xpack.security.ssl.truststore.path", "/trust").build());
SSLConfiguration sslConfiguration1 = new Global(Settings.builder()
.put("xpack.security.ssl.truststore.path", "/truststore").build());
assertThat(sslConfiguration.equals(sslConfiguration1), is(equalTo(false)));
assertThat(sslConfiguration1.equals(sslConfiguration), is(equalTo(false)));
assertThat(sslConfiguration.equals(sslConfiguration), is(equalTo(true)));
assertThat(sslConfiguration1.equals(sslConfiguration1), is(equalTo(true)));
}
public void testThatEmptySettingsHaveSameHashCode() {
SSLConfiguration sslConfiguration = new Global(NO_AUTO_GEN);
SSLConfiguration sslConfiguration1 = new Global(NO_AUTO_GEN);
assertThat(sslConfiguration.hashCode(), is(equalTo(sslConfiguration1.hashCode())));
SSLConfiguration profileSettings = new Custom(Settings.EMPTY, sslConfiguration);
assertThat(profileSettings.hashCode(), is(equalTo(sslConfiguration.hashCode())));
}
public void testThatSettingsWithDifferentKeystoresHaveDifferentHashCode() {
SSLConfiguration sslConfiguration = new Global(Settings.builder()
.put("xpack.security.ssl.keystore.path", "path").build());
SSLConfiguration sslConfiguration1 = new Global(Settings.builder()
.put("xpack.security.ssl.keystore.path", "path1").build());
assertThat(sslConfiguration.hashCode(), is(not(equalTo(sslConfiguration1.hashCode()))));
}
public void testThatSettingsWithDifferentProtocolsHaveDifferentHashCode() {
SSLConfiguration sslConfiguration = new Global(Settings.builder()
.put("xpack.security.ssl.protocol", "ssl").build());
SSLConfiguration sslConfiguration1 = new Global(Settings.builder()
.put("xpack.security.ssl.protocol", "tls").build());
assertThat(sslConfiguration.hashCode(), is(not(equalTo(sslConfiguration1.hashCode()))));
}
public void testThatSettingsWithDifferentTruststoresHaveDifferentHashCode() {
SSLConfiguration sslConfiguration = new Global(Settings.builder()
.put("xpack.security.ssl.truststore.path", "/trust").build());
SSLConfiguration sslConfiguration1 = new Global(Settings.builder()
.put("xpack.security.ssl.truststore.path", "/truststore").build());
assertThat(sslConfiguration.hashCode(), is(not(equalTo(sslConfiguration1.hashCode()))));
}
public void testConfigurationUsingPEMKeyFiles() {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Settings settings = Settings.builder()
.put("xpack.security.ssl.key.path", getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.pem"))
.put("xpack.security.ssl.key.password", "testnode")
.put("xpack.security.ssl.cert", getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"))
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class));
PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig();
KeyManager[] keyManagers = keyConfig.keyManagers(env, null, null);
assertThat(keyManagers.length, is(1));
assertThat(config.trustConfig(), sameInstance(keyConfig));
TrustManager[] trustManagers = keyConfig.trustManagers(env, null, null);
assertThat(trustManagers.length, is(1));
}
public void testConfigurationUsingPEMKeyAndTrustFiles() {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Settings settings = Settings.builder()
.put("xpack.security.ssl.key.path", getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.pem"))
.put("xpack.security.ssl.key.password", "testnode")
.put("xpack.security.ssl.cert", getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"))
.putArray("xpack.security.ssl.ca",
getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt").toString(),
getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt").toString())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class));
PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig();
KeyManager[] keyManagers = keyConfig.keyManagers(env, null, null);
assertThat(keyManagers.length, is(1));
assertThat(config.trustConfig(), not(sameInstance(keyConfig)));
assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class));
TrustManager[] trustManagers = keyConfig.trustManagers(env, null, null);
assertThat(trustManagers.length, is(1));
}
public void testReloadingKeyStore() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path keystorePath = tempDir.resolve("testnode.jks");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks"), keystorePath);
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", keystorePath)
.put("xpack.security.ssl.keystore.password", "testnode")
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(StoreKeyConfig.class));
StoreKeyConfig keyConfig = (StoreKeyConfig) config.keyConfig();
CountDownLatch latch = new CountDownLatch(2);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener);
assertThat(keyManagers.length, is(1));
assertThat(keyManagers[0], instanceOf(X509ExtendedKeyManager.class));
X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0];
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("testnode"));
TrustManager[] trustManagers = keyConfig.trustManagers(env, resourceWatcherService, listener);
assertThat(trustManagers.length, is(1));
assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class));
X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0];
Certificate[] certificates = trustManager.getAcceptedIssuers();
final int trustedCount = certificates.length;
assertThat(latch.getCount(), is(2L));
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
Path updated = tempDir.resolve("updated.jks");
try (OutputStream out = Files.newOutputStream(updated)) {
keyStore.store(out, "testnode".toCharArray());
}
atomicMoveIfPossible(updated, keystorePath);
latch.await();
assertThat(exceptionRef.get(), is(nullValue()));
aliases = keyManager.getServerAliases("RSA", null);
assertThat(aliases, is(nullValue()));
certificates = trustManager.getAcceptedIssuers();
assertThat(trustedCount - certificates.length, is(5));
} finally {
threadPool.shutdown();
}
}
public void testReloadingPEMKeyConfig() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path keyPath = tempDir.resolve("testnode.pem");
Path certPath = tempDir.resolve("testnode.crt");
Path clientCertPath = tempDir.resolve("testclient.crt");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.pem"), keyPath);
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"), certPath);
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt"), clientCertPath);
Settings settings = Settings.builder()
.put("xpack.security.ssl.key.path", keyPath)
.put("xpack.security.ssl.key.password", "testnode")
.put("xpack.security.ssl.cert", certPath)
.putArray("xpack.security.ssl.ca", certPath.toString(), clientCertPath.toString())
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class));
PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig();
CountDownLatch latch = new CountDownLatch(2);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload pem");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener);
assertThat(keyManagers.length, is(1));
assertThat(keyManagers[0], instanceOf(X509ExtendedKeyManager.class));
X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0];
String[] aliases = keyManager.getServerAliases("RSA", null);
assertThat(aliases, is(notNullValue()));
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
PrivateKey privateKey = keyManager.getPrivateKey(aliases[0]);
TrustManager[] trustManagers = keyConfig.trustManagers(env, resourceWatcherService, listener);
assertThat(trustManagers.length, is(1));
assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class));
X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0];
Certificate[] certificates = trustManager.getAcceptedIssuers();
final int trustedCount = certificates.length;
assertThat(latch.getCount(), is(2L));
// make sure we wait enough to see a change. if time is within a second the file may not be seen as modified since the size is
// the same!
awaitBusy(() -> {
try {
BasicFileAttributes attributes = Files.readAttributes(keyPath, BasicFileAttributes.class);
return System.currentTimeMillis() - attributes.lastModifiedTime().toMillis() >= 1000L;
} catch (IOException e) {
throw new ElasticsearchException("io exception while checking time", e);
}
});
Path updatedKeyPath = tempDir.resolve("updated.pem");
KeyPair keyPair = CertUtils.generateKeyPair();
try (OutputStream os = Files.newOutputStream(updatedKeyPath);
OutputStreamWriter osWriter = new OutputStreamWriter(os, StandardCharsets.UTF_8);
JcaPEMWriter writer = new JcaPEMWriter(osWriter)) {
writer.writeObject(keyPair,
new JcePEMEncryptorBuilder("DES-EDE3-CBC").setProvider(CertUtils.BC_PROV).build("testnode".toCharArray()));
}
atomicMoveIfPossible(updatedKeyPath, keyPath);
latch.await();
assertThat(exceptionRef.get(), is(nullValue()));
aliases = keyManager.getServerAliases("RSA", null);
assertThat(aliases, is(notNullValue()));
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
assertThat(keyManager.getPrivateKey(aliases[0]), not(equalTo(privateKey)));
assertThat(keyManager.getPrivateKey(aliases[0]), is(equalTo(keyPair.getPrivate())));
certificates = trustManager.getAcceptedIssuers();
assertThat(trustedCount - certificates.length, is(0));
} finally {
threadPool.shutdown();
}
}
public void testReloadingTrustStore() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path trustStorePath = tempDir.resolve("testnode.jks");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks"), trustStorePath);
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path", trustStorePath)
.put("xpack.security.ssl.truststore.password", "testnode")
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.trustConfig(), instanceOf(StoreTrustConfig.class));
StoreTrustConfig trustConfig = (StoreTrustConfig) config.trustConfig();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener);
assertThat(trustManagers.length, is(1));
assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class));
X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0];
Certificate[] certificates = trustManager.getAcceptedIssuers();
final int trustedCount = certificates.length;
assertThat(latch.getCount(), is(1L));
Path updatedTruststore = tempDir.resolve("updated.jks");
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
try (OutputStream out = Files.newOutputStream(updatedTruststore)) {
keyStore.store(out, "testnode".toCharArray());
}
atomicMoveIfPossible(updatedTruststore, trustStorePath);
latch.await();
assertThat(exceptionRef.get(), is(nullValue()));
certificates = trustManager.getAcceptedIssuers();
assertThat(trustedCount - certificates.length, is(5));
} finally {
threadPool.shutdown();
}
}
public void testReloadingPEMTrustConfig() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path clientCertPath = tempDir.resolve("testclient.crt");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt"), clientCertPath);
Settings settings = Settings.builder()
.putArray("xpack.security.ssl.ca", clientCertPath.toString())
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), false)
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class));
PEMTrustConfig trustConfig = (PEMTrustConfig) config.trustConfig();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener);
assertThat(trustManagers.length, is(1));
assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class));
X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0];
Certificate[] certificates = trustManager.getAcceptedIssuers();
assertThat(certificates.length, is(1));
assertThat(((X509Certificate)certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client"));
assertThat(latch.getCount(), is(1L));
Path updatedCert = tempDir.resolve("updated.crt");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"), updatedCert,
StandardCopyOption.REPLACE_EXISTING);
atomicMoveIfPossible(updatedCert, clientCertPath);
latch.await();
assertThat(exceptionRef.get(), is(nullValue()));
Certificate[] updatedCerts = trustManager.getAcceptedIssuers();
assertThat(updatedCerts.length, is(1));
assertThat(((X509Certificate)updatedCerts[0]).getSubjectX500Principal().getName(), containsString("Test Node"));
assertThat(updatedCerts[0], not(equalTo(certificates[0])));
} finally {
threadPool.shutdown();
}
}
public void testReloadingKeyStoreException() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path keystorePath = tempDir.resolve("testnode.jks");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks"), keystorePath);
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", keystorePath)
.put("xpack.security.ssl.keystore.password", "testnode")
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(StoreKeyConfig.class));
StoreKeyConfig keyConfig = (StoreKeyConfig) config.keyConfig();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener);
X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0];
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("testnode"));
assertThat(latch.getCount(), is(1L));
// truncate the keystore
try (OutputStream out = Files.newOutputStream(keystorePath)) {
}
latch.await();
assertThat(exceptionRef.get(), notNullValue());
assertThat(exceptionRef.get(), instanceOf(ElasticsearchException.class));
assertThat(keyManager.getServerAliases("RSA", null), equalTo(aliases));
} finally {
threadPool.shutdown();
}
}
public void testReloadingPEMKeyConfigException() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path keyPath = tempDir.resolve("testnode.pem");
Path certPath = tempDir.resolve("testnode.crt");
Path clientCertPath = tempDir.resolve("testclient.crt");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.pem"), keyPath);
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"), certPath);
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt"), clientCertPath);
Settings settings = Settings.builder()
.put("xpack.security.ssl.key.path", keyPath)
.put("xpack.security.ssl.key.password", "testnode")
.put("xpack.security.ssl.cert", certPath)
.putArray("xpack.security.ssl.ca", certPath.toString(), clientCertPath.toString())
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class));
PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload pem");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener);
assertThat(keyManagers.length, is(1));
assertThat(keyManagers[0], instanceOf(X509ExtendedKeyManager.class));
X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0];
String[] aliases = keyManager.getServerAliases("RSA", null);
assertThat(aliases, is(notNullValue()));
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
PrivateKey privateKey = keyManager.getPrivateKey(aliases[0]);
assertThat(latch.getCount(), is(1L));
// pick a random file to truncate
Path toTruncate = randomFrom(keyPath, certPath);
// truncate the file
try (OutputStream os = Files.newOutputStream(toTruncate)) {
}
latch.await();
assertThat(exceptionRef.get(), is(instanceOf(ElasticsearchException.class)));
assertThat(keyManager.getServerAliases("RSA", null), equalTo(aliases));
assertThat(keyManager.getPrivateKey(aliases[0]), is(equalTo(privateKey)));
} finally {
threadPool.shutdown();
}
}
public void testTrustStoreReloadException() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path trustStorePath = tempDir.resolve("testnode.jks");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks"), trustStorePath);
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path", trustStorePath)
.put("xpack.security.ssl.truststore.password", "testnode")
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean())
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.trustConfig(), instanceOf(StoreTrustConfig.class));
StoreTrustConfig trustConfig = (StoreTrustConfig) config.trustConfig();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener);
assertThat(trustManagers.length, is(1));
assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class));
X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0];
Certificate[] certificates = trustManager.getAcceptedIssuers();
// truncate the truststore
try (OutputStream os = Files.newOutputStream(trustStorePath)) {
}
latch.await();
assertThat(exceptionRef.get(), instanceOf(ElasticsearchException.class));
assertThat(trustManager.getAcceptedIssuers(), equalTo(certificates));
} finally {
threadPool.shutdown();
}
}
public void testPEMTrustReloadException() throws Exception {
Environment env = randomBoolean() ? null :
new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path tempDir = createTempDir();
Path clientCertPath = tempDir.resolve("testclient.crt");
Files.copy(getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt"), clientCertPath);
Settings settings = Settings.builder()
.putArray("xpack.security.ssl.ca", clientCertPath.toString())
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), false)
.build();
SSLConfiguration config = new Global(settings);
assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class));
PEMTrustConfig trustConfig = (PEMTrustConfig) config.trustConfig();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Exception> exceptionRef = new AtomicReference<>();
Listener listener = createRefreshListener(latch, exceptionRef);
ThreadPool threadPool = new ThreadPool("reload");
try {
ResourceWatcherService resourceWatcherService =
new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool).start();
TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener);
assertThat(trustManagers.length, is(1));
assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class));
X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0];
Certificate[] certificates = trustManager.getAcceptedIssuers();
assertThat(certificates.length, is(1));
assertThat(((X509Certificate) certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client"));
assertThat(latch.getCount(), is(1L));
// write bad file
Path updatedCert = tempDir.resolve("updated.crt");
try (OutputStream os = Files.newOutputStream(updatedCert)) {
os.write(randomByte());
}
atomicMoveIfPossible(updatedCert, clientCertPath);
latch.await();
assertThat(exceptionRef.get(), instanceOf(ElasticsearchException.class));
assertThat(trustManager.getAcceptedIssuers(), equalTo(certificates));
} finally {
threadPool.shutdown();
}
}
private Listener createRefreshListener(CountDownLatch latch, AtomicReference<Exception> exceptionRef) {
return new Listener() {
@Override
public void onReload() {
logger.info("refresh called");
latch.countDown();
}
@Override
public void onFailure(Exception e) {
logger.error("exception " + e);
exceptionRef.set(e);
latch.countDown();
}
};
}
private void atomicMoveIfPossible(Path source, Path target) throws IOException {
try {
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException e) {
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
}
}
}

View File

@ -0,0 +1,180 @@
/*
* 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.shield.ssl;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.network.InetAddressHelper;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.shield.Security;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.test.ShieldSettingsSource;
import org.elasticsearch.transport.Transport;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import static org.hamcrest.Matchers.containsString;
/**
* Integration tests for SSL reloading support
*/
public class SSLReloadIntegTests extends ShieldIntegTestCase {
private Path nodeStorePath;
@Override
public Settings nodeSettings(int nodeOrdinal) {
if (nodeStorePath == null) {
Path origPath = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks");
Path tempDir = createTempDir();
nodeStorePath = tempDir.resolve("testnode.jks");
try {
Files.copy(origPath, nodeStorePath);
} catch (IOException e) {
throw new ElasticsearchException("failed to copy keystore");
}
}
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : settings.getAsMap().entrySet()) {
if (entry.getKey().startsWith(Security.setting("ssl.")) == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
builder.put("resource.reload.interval.high", "1s")
.put(ShieldSettingsSource.getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks",
"testnode"))
.put("xpack.security.ssl.keystore.path", nodeStorePath);
if (builder.get("xpack.security.ssl.truststore.path") != null) {
builder.put("xpack.security.ssl.truststore.path", nodeStorePath);
}
return builder.build();
}
@Override
protected boolean sslTransportEnabled() {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
public void testThatSSLConfigurationReloadsOnModification() throws Exception {
KeyPair keyPair = CertUtils.generateKeyPair();
X509Certificate certificate = getCertificate(keyPair);
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
keyStore.setKeyEntry("key", keyPair.getPrivate(), "changeme".toCharArray(), new Certificate[] { certificate });
Path keystorePath = createTempDir().resolve("newcert.jks");
try (OutputStream out = Files.newOutputStream(keystorePath)) {
keyStore.store(out, "changeme".toCharArray());
}
Settings settings = Settings.builder()
.put("keystore.path", keystorePath)
.put("keystore.password", "changeme")
.put("truststore.path", nodeStorePath)
.put("truststore.password", "testnode")
.build();
String node = randomFrom(internalCluster().getNodeNames());
ServerSSLService sslService = internalCluster().getInstance(ServerSSLService.class, node);
SSLSocketFactory sslSocketFactory = sslService.sslSocketFactory(settings);
InetSocketTransportAddress address = (InetSocketTransportAddress) internalCluster()
.getInstance(Transport.class, node).boundAddress().publishAddress();
try (SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(address.getAddress(), address.getPort())) {
socket.startHandshake();
fail("handshake should not have been successful!");
} catch (SSLHandshakeException expected) {
}
KeyStore nodeStore = KeyStore.getInstance("jks");
try (InputStream in = Files.newInputStream(nodeStorePath)) {
nodeStore.load(in, "testnode".toCharArray());
}
nodeStore.setCertificateEntry("newcert", certificate);
Path path = nodeStorePath.getParent().resolve("updated.jks");
try (OutputStream out = Files.newOutputStream(path)) {
nodeStore.store(out, "testnode".toCharArray());
}
try {
Files.move(path, nodeStorePath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException e) {
Files.move(path, nodeStorePath, StandardCopyOption.REPLACE_EXISTING);
}
CountDownLatch latch = new CountDownLatch(1);
assertBusy(() -> {
try (SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(address.getAddress(), address.getPort())) {
socket.addHandshakeCompletedListener(event -> {
try {
assertThat(event.getPeerPrincipal().getName(), containsString("Test Node"));
logger.info("ssl handshake completed on port [{}]", event.getSocket().getLocalPort());
latch.countDown();
} catch (Exception e) {
fail("caught exception in listener " + e.getMessage());
}
});
socket.startHandshake();
} catch (Exception e) {
fail("caught exception " + e.getMessage());
}
});
latch.await();
}
private X509Certificate getCertificate(KeyPair keyPair) throws Exception {
final DateTime notBefore = new DateTime(DateTimeZone.UTC);
final DateTime notAfter = notBefore.plusYears(1);
X500Name subject = new X500Name("CN=random cert");
JcaX509v3CertificateBuilder builder =
new JcaX509v3CertificateBuilder(subject, CertUtils.getSerial(),
new Time(notBefore.toDate(), Locale.ROOT), new Time(notAfter.toDate(), Locale.ROOT), subject, keyPair.getPublic());
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
builder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
builder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(keyPair.getPublic()));
builder.addExtension(Extension.subjectAlternativeName, false,
CertUtils.getSubjectAlternativeNames(true, Sets.newHashSet(InetAddressHelper.getAllAddresses())));
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
X509CertificateHolder certificateHolder = builder.build(signer);
return new JcaX509CertificateConverter().getCertificate(certificateHolder);
}
}

View File

@ -1,206 +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.shield.ssl;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.ESTestCase;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
public class SSLSettingsTests extends ESTestCase {
public void testThatSSLSettingsWithEmptySettingsHaveCorrectDefaults() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.EMPTY);
assertThat(sslSettings.keyStorePath, nullValue());
assertThat(sslSettings.keyStorePassword, nullValue());
assertThat(sslSettings.keyPassword, nullValue());
assertThat(sslSettings.keyStoreAlgorithm, is(equalTo(KeyManagerFactory.getDefaultAlgorithm())));
assertThat(sslSettings.sessionCacheSize, is(equalTo(SSLSettings.Globals.DEFAULT_SESSION_CACHE_SIZE)));
assertThat(sslSettings.sessionCacheTimeout, is(equalTo(SSLSettings.Globals.DEFAULT_SESSION_CACHE_TIMEOUT)));
assertThat(sslSettings.sslProtocol, is(equalTo(SSLSettings.Globals.DEFAULT_PROTOCOL)));
assertThat(sslSettings.trustStoreAlgorithm, is(equalTo(TrustManagerFactory.getDefaultAlgorithm())));
assertThat(sslSettings.trustStorePassword, nullValue());
assertThat(sslSettings.trustStorePath, nullValue());
}
public void testThatOnlyKeystoreInSettingsSetsTruststoreSettings() {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", "path")
.put("xpack.security.ssl.keystore.password", "password")
.build();
// Pass settings in as component settings
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, settings);
assertThat(sslSettings.keyStorePath, is(equalTo("path")));
assertThat(sslSettings.keyStorePassword, is(equalTo("password")));
assertThat(sslSettings.trustStorePath, is(equalTo(sslSettings.keyStorePath)));
assertThat(sslSettings.trustStorePassword, is(equalTo(sslSettings.keyStorePassword)));
// Pass settings in as profile settings
settings = Settings.builder()
.put("keystore.path", "path")
.put("keystore.password", "password")
.build();
SSLSettings sslSettings1 = new SSLSettings(settings, Settings.EMPTY);
assertThat(sslSettings1.keyStorePath, is(equalTo("path")));
assertThat(sslSettings1.keyStorePassword, is(equalTo("password")));
assertThat(sslSettings1.trustStorePath, is(equalTo(sslSettings1.keyStorePath)));
assertThat(sslSettings1.trustStorePassword, is(equalTo(sslSettings1.keyStorePassword)));
}
public void testThatKeystorePasswordIsDefaultKeyPassword() {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.password", "password")
.build();
// Pass settings in as component settings
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, settings);
assertThat(sslSettings.keyPassword, is(equalTo(sslSettings.keyStorePassword)));
settings = Settings.builder()
.put("keystore.password", "password")
.build();
// Pass settings in as profile settings
SSLSettings sslSettings1 = new SSLSettings(settings, Settings.EMPTY);
assertThat(sslSettings1.keyPassword, is(equalTo(sslSettings1.keyStorePassword)));
}
public void testThatKeyPasswordCanBeSet() {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.password", "password")
.put("xpack.security.ssl.keystore.key_password", "key")
.build();
// Pass settings in as component settings
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, settings);
assertThat(sslSettings.keyStorePassword, is(equalTo("password")));
assertThat(sslSettings.keyPassword, is(equalTo("key")));
// Pass settings in as profile settings
settings = Settings.builder()
.put("keystore.password", "password")
.put("keystore.key_password", "key")
.build();
SSLSettings sslSettings1 = new SSLSettings(settings, Settings.EMPTY);
assertThat(sslSettings1.keyStorePassword, is(equalTo("password")));
assertThat(sslSettings1.keyPassword, is(equalTo("key")));
}
public void testThatProfileSettingsOverrideServiceSettings() {
Settings profileSettings = Settings.builder()
.put("keystore.path", "path")
.put("keystore.password", "password")
.put("keystore.key_password", "key")
.put("keystore.algorithm", "algo")
.put("truststore.path", "trust path")
.put("truststore.password", "password for trust")
.put("truststore.algorithm", "trusted")
.put("protocol", "ssl")
.put("session.cache_size", "3")
.put("session.cache_timeout", "10m")
.build();
Settings serviceSettings = Settings.builder()
.put("xpack.security.ssl.keystore.path", "comp path")
.put("xpack.security.ssl.keystore.password", "comp password")
.put("xpack.security.ssl.keystore.key_password", "comp key")
.put("xpack.security.ssl.keystore.algorithm", "comp algo")
.put("xpack.security.ssl.truststore.path", "comp trust path")
.put("xpack.security.ssl.truststore.password", "comp password for trust")
.put("xpack.security.ssl.truststore.algorithm", "comp trusted")
.put("xpack.security.ssl.protocol", "tls")
.put("xpack.security.ssl.session.cache_size", "7")
.put("xpack.security.ssl.session.cache_timeout", "20m")
.build();
SSLSettings sslSettings = new SSLSettings(profileSettings, serviceSettings);
assertThat(sslSettings.keyStorePath, is(equalTo("path")));
assertThat(sslSettings.keyStorePassword, is(equalTo("password")));
assertThat(sslSettings.keyPassword, is(equalTo("key")));
assertThat(sslSettings.keyStoreAlgorithm, is(equalTo("algo")));
assertThat(sslSettings.trustStorePath, is(equalTo("trust path")));
assertThat(sslSettings.trustStorePassword, is(equalTo("password for trust")));
assertThat(sslSettings.trustStoreAlgorithm, is(equalTo("trusted")));
assertThat(sslSettings.sslProtocol, is(equalTo("ssl")));
assertThat(sslSettings.sessionCacheSize, is(equalTo(3)));
assertThat(sslSettings.sessionCacheTimeout, is(equalTo(TimeValue.timeValueMinutes(10L))));
}
public void testThatEmptySettingsAreEqual() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.EMPTY);
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.EMPTY);
assertThat(sslSettings.equals(sslSettings1), is(equalTo(true)));
assertThat(sslSettings1.equals(sslSettings), is(equalTo(true)));
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
}
public void testThatSettingsWithDifferentKeystoresAreNotEqual() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.keystore.path", "path").build());
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.keystore.path", "path1").build());
assertThat(sslSettings.equals(sslSettings1), is(equalTo(false)));
assertThat(sslSettings1.equals(sslSettings), is(equalTo(false)));
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
}
public void testThatSettingsWithDifferentProtocolsAreNotEqual() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.protocol", "ssl").build());
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.protocol", "tls").build());
assertThat(sslSettings.equals(sslSettings1), is(equalTo(false)));
assertThat(sslSettings1.equals(sslSettings), is(equalTo(false)));
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
}
public void testThatSettingsWithDifferentTruststoresAreNotEqual() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.truststore.path", "/trust").build());
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.truststore.path", "/truststore").build());
assertThat(sslSettings.equals(sslSettings1), is(equalTo(false)));
assertThat(sslSettings1.equals(sslSettings), is(equalTo(false)));
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
}
public void testThatEmptySettingsHaveSameHashCode() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.EMPTY);
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.EMPTY);
assertThat(sslSettings.hashCode(), is(equalTo(sslSettings1.hashCode())));
}
public void testThatSettingsWithDifferentKeystoresHaveDifferentHashCode() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.keystore.path", "path").build());
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.keystore.path", "path1").build());
assertThat(sslSettings.hashCode(), is(not(equalTo(sslSettings1.hashCode()))));
}
public void testThatSettingsWithDifferentProtocolsHaveDifferentHashCode() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.protocol", "ssl").build());
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.protocol", "tls").build());
assertThat(sslSettings.hashCode(), is(not(equalTo(sslSettings1.hashCode()))));
}
public void testThatSettingsWithDifferentTruststoresHaveDifferentHashCode() {
SSLSettings sslSettings = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.truststore.path", "/trust").build());
SSLSettings sslSettings1 = new SSLSettings(Settings.EMPTY, Settings.builder()
.put("xpack.security.ssl.truststore.path", "/truststore").build());
assertThat(sslSettings.hashCode(), is(not(equalTo(sslSettings1.hashCode()))));
}
}

View File

@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
@ -33,6 +34,7 @@ import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class ServerSSLServiceTests extends ESTestCase {
Path testnodeStore;
Environment env;
@ -51,7 +53,7 @@ public class ServerSSLServiceTests extends ESTestCase {
.put("xpack.security.ssl.truststore.password", "testnode")
.build();
try {
new ServerSSLService(settings, env).createSSLEngine();
new ServerSSLService(settings, env, new Global(settings), null).createSSLEngine();
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize the SSLContext"));
@ -65,7 +67,7 @@ public class ServerSSLServiceTests extends ESTestCase {
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env);
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
Settings.Builder settingsBuilder = Settings.builder()
.put("truststore.path", testClientStore)
@ -79,10 +81,11 @@ public class ServerSSLServiceTests extends ESTestCase {
}
public void testThatSslContextCachingWorks() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build(), env);
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
SSLContext sslContext = sslService.sslContext();
SSLContext cachedSslContext = sslService.sslContext();
@ -92,20 +95,22 @@ public class ServerSSLServiceTests extends ESTestCase {
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
Path differentPasswordsStore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks");
new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", differentPasswordsStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.keystore.key_password", "testnode1")
.build(), env).createSSLEngine();
.build();
new ServerSSLService(settings, env, new Global(settings), null).createSSLEngine();
}
public void testIncorrectKeyPasswordThrowsException() throws Exception {
Path differentPasswordsStore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks");
try {
new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", differentPasswordsStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build(), env).createSSLEngine();
.build();
new ServerSSLService(settings, env, new Global(settings), null).createSSLEngine();
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory"));
@ -113,65 +118,71 @@ public class ServerSSLServiceTests extends ESTestCase {
}
public void testThatSSLv3IsNotEnabled() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
SSLEngine engine = sslService.createSSLEngine();
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
}
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(1000));
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
}
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.session.cache_size", "300")
.put("xpack.security.ssl.session.cache_timeout", "600s")
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(300));
assertThat(context.getSessionTimeout(), equalTo(600));
}
public void testThatCreateSSLEngineWithoutAnySettingsDoesNotWork() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.EMPTY, env);
ServerSSLService sslService = new ServerSSLService(Settings.EMPTY, env, new Global(Settings.builder()
.put(Global.AUTO_GENERATE_SSL_SETTING.getKey(), false).build()), null);
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("no keystore configured"));
assertThat(e.getMessage(), is("a key must be configured to act as a server"));
}
}
public void testThatCreateSSLEngineWithOnlyTruststoreDoesNotWork() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path", testnodeStore)
.put("xpack.security.ssl.truststore.password", "testnode")
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("no keystore configured"));
assertThat(e.getMessage(), is("a key must be configured to act as a server"));
}
}
public void testThatTruststorePasswordIsRequired() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.truststore.path", testnodeStore)
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
try {
sslService.sslContext();
fail("Expected IllegalArgumentException");
@ -181,9 +192,10 @@ public class ServerSSLServiceTests extends ESTestCase {
}
public void testThatKeystorePasswordIsRequired() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
try {
sslService.sslContext();
fail("Expected IllegalArgumentException");
@ -193,14 +205,15 @@ public class ServerSSLServiceTests extends ESTestCase {
}
public void testCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(SSLSettings.Globals.DEFAULT_CIPHERS);
List<String> ciphers = new ArrayList<>(Global.DEFAULT_CIPHERS);
ciphers.add("foo");
ciphers.add("bar");
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.putArray("xpack.security.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
SSLEngine engine = sslService.createSSLEngine();
assertThat(engine, is(notNullValue()));
String[] enabledCiphers = engine.getEnabledCipherSuites();
@ -208,26 +221,28 @@ public class ServerSSLServiceTests extends ESTestCase {
}
public void testInvalidCiphersOnlyThrowsException() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.putArray("xpack.security.ssl.ciphers", new String[] { "foo", "bar" })
.build(), env);
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("failed loading cipher suites [[foo, bar]]"));
assertThat(e.getMessage(), is("none of the ciphers [foo, bar] are supported by this JVM"));
}
}
public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build(), env);
SSLSocketFactory factory = sslService.sslSocketFactory();
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), sslService.ciphers());
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
SSLSocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY);
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), sslService.ciphers(), false);
assertThat(factory.getDefaultCipherSuites(), is(ciphers));
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {

View File

@ -51,6 +51,11 @@ public class ServerTransportFilterIntegrationTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings.Builder settingsBuilder = Settings.builder();

View File

@ -8,6 +8,7 @@ package org.elasticsearch.shield.transport.netty;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.ServerSSLService;
import org.elasticsearch.test.ESTestCase;
import org.jboss.netty.bootstrap.ClientBootstrap;
@ -74,7 +75,7 @@ public class HandshakeWaitingHandlerTests extends ESTestCase {
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
ServerSSLService sslService = new ServerSSLService(settings, env);
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null);
sslContext = sslService.sslContext();

View File

@ -12,6 +12,7 @@ import org.elasticsearch.transport.TransportSettings;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map.Entry;
import static org.hamcrest.CoreMatchers.is;
@ -23,9 +24,22 @@ public class IPHostnameVerificationTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : settings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.security.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
settings = builder.build();
// The default Unicast test behavior is to use 'localhost' with the port number. For this test we need to use IP
String[] unicastAddresses = settings.getAsArray("discovery.zen.ping.unicast.hosts");
for (int i = 0; i < unicastAddresses.length; i++) {
@ -59,7 +73,16 @@ public class IPHostnameVerificationTests extends ShieldIntegTestCase {
@Override
protected Settings transportClientSettings() {
return Settings.builder().put(super.transportClientSettings())
Settings clientSettings = super.transportClientSettings();
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : clientSettings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.security.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
clientSettings = builder.build();
return Settings.builder().put(clientSettings)
.put(ShieldNettyTransport.HOSTNAME_VERIFICATION_SETTING.getKey(), true)
.put(ShieldNettyTransport.HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING.getKey(), false)
.put("xpack.security.ssl.keystore.path", keystore.toAbsolutePath())

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.env.Environment;
import org.elasticsearch.http.netty.NettyHttpMockUtil;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.ServerSSLService;
import org.elasticsearch.shield.transport.SSLClientAuth;
import org.elasticsearch.shield.transport.filter.IPFilter;
@ -19,10 +20,14 @@ import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.ssl.SslHandler;
import org.junit.Before;
import javax.net.ssl.SSLEngine;
import java.nio.file.Path;
import java.util.Locale;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.mockito.Mockito.mock;
public class ShieldNettyHttpServerTransportTests extends ESTestCase {
@ -37,13 +42,13 @@ public class ShieldNettyHttpServerTransportTests extends ESTestCase {
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
serverSSLService = new ServerSSLService(settings, env);
serverSSLService = new ServerSSLService(settings, env, new Global(settings), null);
}
public void testDefaultClientAuth() throws Exception {
Settings settings = Settings.builder().put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyHttpServerTransport transport = new ShieldNettyHttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class), mock(Global.class));
NettyHttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -56,7 +61,7 @@ public class ShieldNettyHttpServerTransportTests extends ESTestCase {
.put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), true)
.put(ShieldNettyHttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
ShieldNettyHttpServerTransport transport = new ShieldNettyHttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class), mock(Global.class));
NettyHttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -69,7 +74,7 @@ public class ShieldNettyHttpServerTransportTests extends ESTestCase {
.put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), true)
.put(ShieldNettyHttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
ShieldNettyHttpServerTransport transport = new ShieldNettyHttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class), mock(Global.class));
NettyHttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(true));
@ -82,10 +87,32 @@ public class ShieldNettyHttpServerTransportTests extends ESTestCase {
.put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), true)
.put(ShieldNettyHttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
ShieldNettyHttpServerTransport transport = new ShieldNettyHttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class), mock(Global.class));
NettyHttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getWantClientAuth(), is(false));
}
public void testCustomSSLConfiguration() throws Exception {
Settings settings = Settings.builder()
.put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyHttpServerTransport transport = new ShieldNettyHttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class), mock(Global.class));
NettyHttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
SSLEngine defaultEngine = factory.getPipeline().get(SslHandler.class).getEngine();
settings = Settings.builder()
.put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), true)
.put("xpack.security.http.ssl.supported_protocols", "TLSv1.2")
.build();
transport = new ShieldNettyHttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class), mock(Global.class));
NettyHttpMockUtil.setOpenChannelsHandlerToMock(transport);
factory = transport.configureServerChannelPipelineFactory();
SSLEngine customEngine = factory.getPipeline().get(SslHandler.class).getEngine();
assertThat(customEngine.getEnabledProtocols(), arrayContaining("TLSv1.2"));
assertThat(customEngine.getEnabledProtocols(), not(equalTo(defaultEngine.getEnabledProtocols())));
}
}

View File

@ -13,6 +13,7 @@ import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.env.Environment;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.ssl.ServerSSLService;
import org.elasticsearch.shield.transport.SSLClientAuth;
import org.elasticsearch.test.ESTestCase;
@ -42,8 +43,9 @@ public class ShieldNettyTransportTests extends ESTestCase {
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
serverSSLService = new ServerSSLService(settings, env);
clientSSLService = new ClientSSLService(settings);
Global globalSSLConfiguration = new Global(settings);
serverSSLService = new ServerSSLService(settings, env, globalSSLConfiguration, null);
clientSSLService = new ClientSSLService(settings, globalSSLConfiguration);
clientSSLService.setEnvironment(env);
}
@ -51,7 +53,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
Settings.builder().put("xpack.security.ssl", false).build());
@ -62,7 +64,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), false).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
Settings.builder().put("xpack.security.ssl", true).build());
@ -73,7 +75,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
assertThat(factory.getPipeline().get(SslHandler.class).getEngine(), notNullValue());
@ -83,7 +85,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(true));
@ -97,7 +99,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
.put(ShieldNettyTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(true));
@ -111,7 +113,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
.put(ShieldNettyTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -125,7 +127,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
.put(ShieldNettyTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -137,7 +139,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
Settings.builder().put(ShieldNettyTransport.PROFILE_CLIENT_AUTH_SETTING, value).build());
@ -150,7 +152,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
Settings.builder().put(ShieldNettyTransport.PROFILE_CLIENT_AUTH_SETTING.getKey(), value).build());
@ -163,8 +165,7 @@ public class ShieldNettyTransportTests extends ESTestCase {
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class),
mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService,
mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class), mock(Global.class));
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
Settings.builder().put(ShieldNettyTransport.PROFILE_CLIENT_AUTH_SETTING.getKey(), value).build());

View File

@ -11,6 +11,7 @@ import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.shield.Security;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.XPackPlugin;
@ -18,20 +19,33 @@ import org.elasticsearch.xpack.XPackPlugin;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map.Entry;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsString;
public class SslHostnameVerificationTests extends ShieldIntegTestCase {
@Override
protected boolean sslTransportEnabled() {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings.Builder settingsBuilder = Settings.builder().put(super.nodeSettings(nodeOrdinal));
Settings settings = super.nodeSettings(nodeOrdinal);
Settings.Builder settingsBuilder = Settings.builder();
for (Entry<String, String> entry : settings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.security.ssl.") == false) {
settingsBuilder.put(entry.getKey(), entry.getValue());
}
}
Path keystore;
try {
/*
@ -45,9 +59,9 @@ public class SslHostnameVerificationTests extends ShieldIntegTestCase {
throw new RuntimeException(e);
}
return settingsBuilder.put("xpack.security.ssl.keystore.path", keystore.toAbsolutePath()) // settings for client truststore
return settingsBuilder.put("xpack.security.ssl.keystore.path", keystore.toAbsolutePath())
.put("xpack.security.ssl.keystore.password", "testnode-no-subjaltname")
.put("xpack.security.ssl.truststore.path", keystore.toAbsolutePath()) // settings for client truststore
.put("xpack.security.ssl.truststore.path", keystore.toAbsolutePath())
.put("xpack.security.ssl.truststore.password", "testnode-no-subjaltname")
// disable hostname verification as this test uses non-localhost addresses
.put(ShieldNettyTransport.HOSTNAME_VERIFICATION_SETTING.getKey(), false)
@ -58,11 +72,26 @@ public class SslHostnameVerificationTests extends ShieldIntegTestCase {
protected Settings transportClientSettings() {
Path keystore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-no-subjaltname.jks");
assert keystore != null;
return Settings.builder().put(super.transportClientSettings())
.put(ShieldNettyTransport.HOSTNAME_VERIFICATION_SETTING.getKey(), false)
.put("xpack.security.ssl.truststore.path", keystore.toAbsolutePath()) // settings for client truststore
.put("xpack.security.ssl.truststore.password", "testnode-no-subjaltname")
.build();
Settings settings = super.transportClientSettings();
// remove all ssl settings
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : settings.getAsMap().entrySet()) {
String key = entry.getKey();
if (key.startsWith(Security.setting("ssl.")) == false) {
builder.put(key, entry.getValue());
}
}
builder.put(ShieldNettyTransport.HOSTNAME_VERIFICATION_SETTING.getKey(), false)
.put("xpack.security.ssl.keystore.path", keystore.toAbsolutePath()) // settings for client keystore
.put("xpack.security.ssl.keystore.password", "testnode-no-subjaltname");
if (randomBoolean()) {
// randomly set the truststore, if not set the keystore should be used
builder.put("xpack.security.ssl.truststore.path", keystore.toAbsolutePath())
.put("xpack.security.ssl.truststore.password", "testnode-no-subjaltname");
}
return builder.build();
}
public void testThatHostnameMismatchDeniesTransportClientConnection() throws Exception {

View File

@ -17,6 +17,7 @@ import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.shield.Security;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
import org.elasticsearch.test.ShieldIntegTestCase;
@ -53,6 +54,11 @@ public class SslClientAuthTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
public void testThatHttpFailsWithoutSslClientAuth() throws IOException {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
SSLContexts.createDefault(),
@ -76,7 +82,7 @@ public class SslClientAuthTests extends ShieldIntegTestCase {
Settings settings = Settings.builder()
.put(getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient"))
.build();
ClientSSLService sslService = new ClientSSLService(settings);
ClientSSLService sslService = new ClientSSLService(settings, new Global(settings));
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
sslService.sslContext(),

View File

@ -23,6 +23,7 @@ import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.shield.ssl.ClientSSLService;
import org.elasticsearch.shield.ssl.SSLConfiguration.Global;
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.transport.Transport;
@ -55,6 +56,11 @@ public class SslIntegrationTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
// no SSL exception as this is the exception is returned when connecting
public void testThatUnconfiguredCiphersAreRejected() {
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(Settings.builder()
@ -94,8 +100,10 @@ public class SslIntegrationTests extends ShieldIntegTestCase {
}
public void testThatConnectionToHTTPWorks() throws Exception {
Settings settings = getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient");
ClientSSLService service = new ClientSSLService(settings);
Settings settings = Settings.builder()
.put(getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient"))
.build();
ClientSSLService service = new ClientSSLService(settings, new Global(settings));
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nodeClientUsername(),

View File

@ -20,6 +20,7 @@ import org.junit.BeforeClass;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map.Entry;
import static org.elasticsearch.test.ShieldSettingsSource.DEFAULT_PASSWORD;
import static org.elasticsearch.test.ShieldSettingsSource.DEFAULT_USER_NAME;
@ -86,8 +87,24 @@ public class SslMultiPortTests extends ShieldIntegTestCase {
return true;
}
@Override
protected boolean autoSSLEnabled() {
return false;
}
private TransportClient createTransportClient(Settings additionalSettings) {
Settings settings = Settings.builder().put(transportClientSettings())
Settings clientSettings = transportClientSettings();
if (additionalSettings.getByPrefix("xpack.security.ssl.").isEmpty() == false) {
Settings.Builder builder = Settings.builder();
for (Entry<String, String> entry : clientSettings.getAsMap().entrySet()) {
if (entry.getKey().startsWith("xpack.security.ssl.") == false) {
builder.put(entry.getKey(), entry.getValue());
}
}
clientSettings = builder.build();
}
Settings settings = Settings.builder().put(clientSettings)
.put("node.name", "programmatic_transport_client")
.put("cluster.name", internalCluster().getClusterName())
.put(additionalSettings)
@ -224,6 +241,7 @@ public class SslMultiPortTests extends ShieldIntegTestCase {
public void testThatTransportClientCanConnectToNoSslProfile() throws Exception {
Settings settings = Settings.builder()
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
.put(ShieldNettyTransport.SSL_SETTING.getKey(), false)
.put("cluster.name", internalCluster().getClusterName())
.build();
try (TransportClient transportClient = TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build()) {

View File

@ -100,7 +100,8 @@ public abstract class ShieldIntegTestCase extends ESIntegTestCase {
@BeforeClass
public static void initDefaultSettings() {
if (SHIELD_DEFAULT_SETTINGS == null) {
SHIELD_DEFAULT_SETTINGS = new ShieldSettingsSource(maxNumberOfNodes(), randomBoolean(), createTempDir(), Scope.SUITE);
SHIELD_DEFAULT_SETTINGS = new ShieldSettingsSource(maxNumberOfNodes(), randomBoolean(), randomBoolean(), createTempDir(),
Scope.SUITE);
}
}
@ -123,12 +124,12 @@ public abstract class ShieldIntegTestCase extends ESIntegTestCase {
switch (currentClusterScope) {
case SUITE:
if (customShieldSettingsSource == null) {
customShieldSettingsSource = new CustomShieldSettingsSource(sslTransportEnabled(), createTempDir(),
currentClusterScope);
customShieldSettingsSource = new CustomShieldSettingsSource(sslTransportEnabled(), autoSSLEnabled(),
createTempDir(), currentClusterScope);
}
break;
case TEST:
customShieldSettingsSource = new CustomShieldSettingsSource(sslTransportEnabled(), createTempDir(),
customShieldSettingsSource = new CustomShieldSettingsSource(sslTransportEnabled(), autoSSLEnabled(), createTempDir(),
currentClusterScope);
break;
}
@ -265,14 +266,18 @@ public abstract class ShieldIntegTestCase extends ESIntegTestCase {
return randomBoolean();
}
protected boolean autoSSLEnabled() {
return randomBoolean();
}
protected Class<? extends XPackPlugin> xpackPluginClass() {
return SHIELD_DEFAULT_SETTINGS.xpackPluginClass();
}
private class CustomShieldSettingsSource extends ShieldSettingsSource {
private CustomShieldSettingsSource(boolean sslTransportEnabled, Path configDir, Scope scope) {
super(maxNumberOfNodes(), sslTransportEnabled, configDir, scope);
private CustomShieldSettingsSource(boolean sslTransportEnabled, boolean autoSSLEnabled, Path configDir, Scope scope) {
super(maxNumberOfNodes(), sslTransportEnabled, autoSSLEnabled, configDir, scope);
}
@Override

View File

@ -6,6 +6,7 @@
package org.elasticsearch.test;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -30,9 +31,11 @@ import org.elasticsearch.xpack.XPackPlugin;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean;
import static org.elasticsearch.test.ESTestCase.randomFrom;
@ -81,6 +84,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
private final String subfolderPrefix;
private final byte[] systemKey;
private final boolean sslTransportEnabled;
private final boolean autoSSLEnabled;
private final boolean hostnameVerificationEnabled;
private final boolean hostnameVerificationResolveNameEnabled;
@ -92,8 +96,9 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
* @param parentFolder the parent folder that will contain all of the configuration files that need to be created
* @param scope the scope of the test that is requiring an instance of ShieldSettingsSource
*/
public ShieldSettingsSource(int numOfNodes, boolean sslTransportEnabled, Path parentFolder, ESIntegTestCase.Scope scope) {
this(numOfNodes, sslTransportEnabled, generateKey(), parentFolder, scope);
public ShieldSettingsSource(int numOfNodes, boolean sslTransportEnabled, boolean autoSSLEnabled, Path parentFolder,
ESIntegTestCase.Scope scope) {
this(numOfNodes, sslTransportEnabled, autoSSLEnabled, generateKey(), parentFolder, scope);
}
/**
@ -105,13 +110,14 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
* @param parentFolder the parent folder that will contain all of the configuration files that need to be created
* @param scope the scope of the test that is requiring an instance of ShieldSettingsSource
*/
public ShieldSettingsSource(int numOfNodes, boolean sslTransportEnabled, byte[] systemKey, Path parentFolder,
public ShieldSettingsSource(int numOfNodes, boolean sslTransportEnabled, boolean autoSSLEnabled, byte[] systemKey, Path parentFolder,
ESIntegTestCase.Scope scope) {
super(numOfNodes, DEFAULT_SETTINGS);
this.systemKey = systemKey;
this.parentFolder = parentFolder;
this.subfolderPrefix = scope.name();
this.sslTransportEnabled = sslTransportEnabled;
this.autoSSLEnabled = autoSSLEnabled;
this.hostnameVerificationEnabled = randomBoolean();
this.hostnameVerificationResolveNameEnabled = randomBoolean();
}
@ -210,11 +216,37 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
}
public Settings getNodeSSLSettings() {
if (sslTransportEnabled && autoSSLEnabled) {
return Settings.EMPTY;
}
if (randomBoolean()) {
return getSSLSettingsForPEMFiles("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.pem", "testnode",
Collections.singletonList("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"),
Arrays.asList("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-client-profile.crt",
"/org/elasticsearch/shield/transport/ssl/certs/simple/activedir.crt",
"/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt",
"/org/elasticsearch/shield/transport/ssl/certs/simple/openldap.crt",
"/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt"),
sslTransportEnabled, hostnameVerificationEnabled, hostnameVerificationResolveNameEnabled, false);
}
return getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode",
sslTransportEnabled, hostnameVerificationEnabled, hostnameVerificationResolveNameEnabled, false);
}
public Settings getClientSSLSettings() {
if (sslTransportEnabled && autoSSLEnabled) {
return Settings.EMPTY;
}
if (randomBoolean()) {
return getSSLSettingsForPEMFiles("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.pem", "testclient",
Collections.singletonList("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt"),
Arrays.asList("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.crt",
"/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.crt"),
sslTransportEnabled, hostnameVerificationEnabled, hostnameVerificationResolveNameEnabled, true);
}
return getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient",
sslTransportEnabled, hostnameVerificationEnabled, hostnameVerificationResolveNameEnabled, true);
}
@ -233,16 +265,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
private static Settings getSSLSettingsForStore(String resourcePathToStore, String password, boolean sslTransportEnabled,
boolean hostnameVerificationEnabled, boolean hostnameVerificationResolveNameEnabled,
boolean transportClient) {
Path store;
try {
store = PathUtils.get(ShieldSettingsSource.class.getResource(resourcePathToStore).toURI());
} catch (URISyntaxException e) {
throw new ElasticsearchException("exception while reading the store", e);
}
if (Files.notExists(store)) {
throw new ElasticsearchException("store path doesn't exist");
}
Path store = resolveResourcePath(resourcePathToStore);
final String sslEnabledSetting =
randomFrom(ShieldNettyTransport.SSL_SETTING.getKey(), ShieldNettyTransport.DEPRECATED_SSL_SETTING.getKey());
@ -265,4 +288,52 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
}
return builder.build();
}
private static Settings getSSLSettingsForPEMFiles(String keyPath, String password, List<String> certificateFiles,
List<String> trustedCertificates, boolean sslTransportEnabled,
boolean hostnameVerificationEnabled, boolean hostnameVerificationResolveNameEnabled,
boolean transportClient) {
Settings.Builder builder = Settings.builder();
final String sslEnabledSetting =
randomFrom(ShieldNettyTransport.SSL_SETTING.getKey(), ShieldNettyTransport.DEPRECATED_SSL_SETTING.getKey());
builder.put(sslEnabledSetting, sslTransportEnabled);
if (transportClient == false) {
builder.put(ShieldNettyHttpServerTransport.SSL_SETTING.getKey(), false);
}
if (sslTransportEnabled) {
builder.put("xpack.security.ssl.key.path", resolveResourcePath(keyPath))
.put("xpack.security.ssl.key.password", password)
.put("xpack.security.ssl.cert", Strings.arrayToCommaDelimitedString(resolvePathsToString(certificateFiles)))
.put(randomFrom(ShieldNettyTransport.HOSTNAME_VERIFICATION_SETTING.getKey(),
ShieldNettyTransport.DEPRECATED_HOSTNAME_VERIFICATION_SETTING.getKey()), hostnameVerificationEnabled)
.put(ShieldNettyTransport.HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING.getKey(), hostnameVerificationResolveNameEnabled);
if (trustedCertificates.isEmpty() == false) {
builder.put("xpack.security.ssl.ca", Strings.arrayToCommaDelimitedString(resolvePathsToString(trustedCertificates)));
}
}
return builder.build();
}
static String[] resolvePathsToString(List<String> resourcePaths) {
List<String> resolvedPaths = new ArrayList<>(resourcePaths.size());
for (String resource : resourcePaths) {
resolvedPaths.add(resolveResourcePath(resource).toString());
}
return resolvedPaths.toArray(new String[resolvedPaths.size()]);
}
static Path resolveResourcePath(String resourcePathToStore) {
try {
Path path = PathUtils.get(ShieldSettingsSource.class.getResource(resourcePathToStore).toURI());
if (Files.notExists(path)) {
throw new ElasticsearchException("path does not exist: " + path);
}
return path;
} catch (URISyntaxException e) {
throw new ElasticsearchException("exception while reading the store", e);
}
}
}

View File

@ -12,4 +12,6 @@ grant {
permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.RuntimePermission "getClassLoader";
// bouncy castle
permission java.security.SecurityPermission "putProviderProperty.BC";
};

View File

@ -1,440 +0,0 @@
SOFTWARE END USER LICENSE AGREEMENT
READ THIS AGREEMENT CAREFULLY, WHICH CONSTITUTES A LEGALLY BINDING AGREEMENT AND GOVERNS YOUR USE OF
ELASTICSEARCHS PROPRIETARY SOFTWARE. BY INSTALLING AND/OR USING SUCH SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO THE
TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH SUCH TERMS AND CONDITIONS, YOU MAY NOT
INSTALL OR USE ANY OF THE SOFTWARE.
This END USER LICENSE AGREEMENT (this “Agreement") is entered into by and between the applicable Elasticsearch
entity referenced in Attachment 1 hereto (“Elasticsearch”) and the person or entity (“You”) that has downloaded any of
Elasticsearchs proprietary software to which this Agreement is attached or in connection with which this Agreement is
presented to You (collectively, the “Software”). This Agreement is effective upon the earlier of the date on the
commencement of any License granted pursuant to Section 1.1. below (as applicable, the “Effective Date”).
1. SOFTWARE LICENSE AND RESTRICTIONS
1.1 License Grants.
(a) Trial Version License. Subject to the terms and conditions of this Agreement, Elasticsearch agrees to
grant, and does hereby grant to You, for a period of thirty (30) days from the date on which You first install the
Software (the “Trial Term”), a License to the to use the Eligible Features and Functions of the Software that are
applicable to the Trial Version of the Software.   You understand and agree that upon the expiration of a Trial Term,
You will no longer be able to use the Software, unless you either (i) purchase a Subscription, in which case You will
receive a License under Section 1.1(b) below to use the Eligible Features and Functions of the Software that are
applicable to the Subscription that You purchase, (ii) undertake the Registration of Your use of the Software with
Elasticsearch, in which case You will receive a License under Section 1.1(c) below to the Basic Version of the Software
or (iii) obtain from Elasticsearch written consent (e-mail sufficient) to extend the Trial Term, which may be granted by
Elasticsearch in its sole and absolute discretion.
(b) Subscription License. Subject to the terms and conditions of this Agreement and complete payment of any and
all applicable Subscription fees, Elasticsearch agrees to grant, and does hereby grant to You during the Subscription
Term, and for the restricted scope of this Agreement, a License (i) to use the Eligible Features and Functions of the
Software that are applicable to the Subscription level that You have purchased, (ii) for the number of Nodes (as defined
in the Elasticsearch Support Services Policy) and (iii) for the specific project for which you have purchased a
Subscription. The level of Subscription, the number of Nodes and specific project for which you have purchased such
Subscription, are set forth on the applicable ordering document entered into by Elasticsearch and You for the purchase
of the applicable Subscription (“Order Form”).
(c) Basic Version License. Subject to the terms and conditions of this Agreement, and in consideration of the
Registration of Your use the Software, Elasticsearch agrees to grant, and does hereby grant to You, for a period of one
(1) year from the date of Registration (“Basic Term”), a License to use the Eligible Features and Functions of the
Software that are applicable to the Basic Version of the Software.
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You, Elasticsearch owns all right title and
interest in and to the Software and any derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Software is granted to You by implication, estoppel or otherwise. You agree not to: (i)
reverse engineer or decompile, decrypt, disassemble or otherwise reduce any Software or any portion thereof to
human-readable form, except and only to the extent any such restriction is prohibited by applicable law, (ii) deploy the
Software on more Nodes (as defined in Elasticsearchs Support Services Policy) than are permitted under the applicable
License grant in Section 1.1 above (iii) where You have purchased a Subscription, use the Software in connection with
any project other than the project for which you have purchased such Subscription, as identified on the applicable Order
Form, (iv) prepare derivative works from, modify, copy or use the Software in any manner except as expressly permitted
in this Agreement; (v) except as expressly permitted in Section 1.1 above, transfer, sell, rent, lease, distribute,
sublicense, loan or otherwise transfer the Software in whole or in part to any third party; (vi) except as may be
expressly permitted on an applicable Order Form, use the Software for providing time-sharing services, any
software-as-a-service offering (“SaaS”), service bureau services or as part of an application services provider or other
service offering; (vii) circumvent the limitations on use of the Software that are imposed or preserved by any License
Key, (viii) alter or remove any proprietary notices in the Software; or (ix) make available to any third party any
analysis of the results of operation of the Software, including benchmarking results, without the prior written consent
of Elasticsearch. The Software may contain or be provided with open source libraries, components, utilities and other
open source software (collectively, “Open Source Software”), which Open Source Software may have applicable license
terms as identified on a website designated by Elasticsearch or otherwise provided with the Software or Documentation.
Notwithstanding anything to the contrary herein, use of the Open Source Software shall be subject to the license terms
and conditions applicable to such Open Source Software, to the extent required by the applicable licensor (which terms
shall not restrict the license rights granted to You hereunder, but may contain additional rights).
1.3 Audit Rights. You agree that, unless such right is waived in writing by Elasticsearch, Elasticsearch shall have the
right, upon fifteen (15) days notice to You, to audit Your use of the Software for compliance with any quantitative
limitations on Your use of the Software that are set forth in the applicable Order Form. You agree to provide
Elasticsearch with the necessary access to the Software to conduct such an audit either (i) remotely, or (ii) if remote
performance is not possible, at Your facilities, during normal business hours and no more than one (1) time in any
twelve (12) month period. In the event any such audit reveals that You have used the Software in excess of the
applicable quantitative limitations, You agree to solely for Your internal business operations, a limited,
non-exclusive, non-transferable, fully paid up,  right and license (without the right to grant or authorize sublicenses)
promptly pay to Elasticsearch an amount equal to the difference between the fees actually paid and the fees that You
should have paid to remain in compliance with such quantitative limitations. This Section 1.3 shall survive for a
period of two (2) years from the termination or expiration of this Agreement.
1.4 Cluster Metadata. You understand and agree that once deployed, and on a daily basis, the Software may provide
metadata to Elasticsearch about Your cluster statistics and associates that metadata with Your IP address. However, no
other information is provided to Elasticsearch by the Software, including any information about the data You process or
store in connection with your use of the Software. Instructions for disabling this feature are contained in the
Software, however leaving this feature active enables Elasticsearch to gather cluster statistics and provide an improved
level of support to You.
2. TERM AND TERMINATION
2.1 Term. Unless earlier terminated under Section 2.2 below, this Agreement shall commence on the Effective Date, and
shall continue in force for the term of the last to expire applicable license set forth in Section 1.1 above.
2.2 Termination. Either party may, upon written notice to the other party, terminate this Agreement for material breach
by the other party automatically and without any other formality, if such party has failed to cure such material breach
within thirty (30) days of receiving written notice of such material breach from the non-breaching party.
Notwithstanding the foregoing, this Agreement shall automatically terminate in the event that You intentionally breach
the scope of the license granted in Section 1.1 of this Agreement, provided that Elasticsearch reserves the right to
retroactively waive such automatic termination upon written notice to You.
2.3 Post Termination or Expiration. Upon termination or expiration of this Agreement, for any reason, You shall
promptly cease the use of the Software and Documentation and destroy (and certify to Elasticsearch in writing the fact
of such destruction), or return to Elasticsearch, all copies of the Software and Documentation then in Your possession
or under Your control.
2.4 Survival. Sections 2.3, 2.4, 3, 4 and 5 (as any such Sections may be modified by Attachment 1, if applicable) shall
survive any termination or expiration of this Agreement.
3. LIMITED WARRANTY AND DISCLAIMER OF WARRANTIES
3.1 Limited Performance Warranty. Subject to You purchasing a Subscription, Elasticsearch warrants that during the
applicable Subscription Term, the Software will perform in all material respects in accordance with the Documentation.
In the event of a breach of the foregoing warranty, Elasticsearchs sole obligation, and Your exclusive remedy shall be
for Elasticsearch to (i) correct any failure(s) of the Software to perform in all material respects in accordance with
the Documentation or (ii) if Elasticsearch is unable to provide such a correction within thirty (30) days of receipt of
notice of the applicable non-conformity, promptly refund to Customer any pre-paid, unused fees paid by You to
Elasticsearch for the applicable Subscription. The warranty set forth in this Section 3.1 does not apply if the
applicable Software or any portion thereof: (a) has been altered, except by or on behalf Elasticsearch; (b) has not been
used, installed, operated, repaired, or maintained in accordance with this Agreement and/or the Documentation; (c) has
been subjected to abnormal physical or electrical stress, misuse, negligence, or accident; or (d) is used on equipment,
products, or systems not meeting specifications identified by Elasticsearch in the Documentation. Additionally, the
warranties set forth herein only apply when notice of a warranty claim is provided to Elasticsearch within the
applicable warranty period specified herein and do not apply to any bug, defect or error caused by or attributable to
software or hardware not supplied by Elasticsearch.
3.2 Malicious Code. Elasticsearch represents and warrants that prior to making it available for delivery to You,
Elasticsearch will use standard industry practices including, without limitation, the use of an updated commercial
anti-virus program, to test the Software for Malicious Code and remove any Malicious Code it discovers. In the event of
a breach of the foregoing warranty, Elasticsearchs sole obligation, and Your exclusive remedy shall be for
Elasticsearch to replace the Software with Software that does not contain any Malicious Code.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS” WITHOUT
WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR STATUTORY
REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF
THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE SOFTWARE WILL BE UNINTERRUPTED.
4. LIMITATION OF LIABILITY
The provisions of this Section 4 apply if You have not purchased a Subscription. If you have purchased a Subscription,
then the limitations of liability set forth in the applicable Subscription Agreement will apply in lieu of those set
forth in this Section 4.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO
USE THE SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT OR
TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH THROUGH GROSS NEGLIGENCE
OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT
CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
4.2 Damages Cap. IN NO EVENT SHALL ELASTICSEARCHS OR ITS LICENSORS AGGREGATE, CUMULATIVE LIABILITY UNDER THIS
AGREEMENT EXCEED ONE THOUSAND DOLLARS ($1,000).
4.3 YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A REASONABLE ALLOCATION OF THE RISK BETWEEN
THE PARTIES AND WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL
PURPOSE.
5. DEFINITIONS
The following terms have the meanings ascribed:
5.1 “License” means a limited, non-exclusive, non-transferable, fully paid up, right and license (without the right to
grant or authorize sublicenses) solely for Your internal business operations to (i) install and use, in object code
format, the applicable Eligible Features and Functions of the Software, (ii) use, and distribute internally a reasonable
number of copies of the Documentation, provided that You must include on such copies all Marks and Notices; (iii) permit
Contractors to use the Software and Documentation as set forth in (i) and (ii) above, provided that such use must be
solely for Your benefit, and You shall be responsible for all acts and omissions of such Contractors in connection with
their use of the Software that are contrary to the terms and conditions of this Agreement..
5.2 “License Key” means an alphanumeric code that enables the Eligible Features and Functions of the Software.
5.3 “Basic Version” means that version of the Software available for use without the purchase of a Qualifying
Subscription, but which does require Registration.
5.4 “Contractor” means third party contractors performing services on Your behalf.
5.5 “Documentation” means the published end user documentation provided by Elasticsearch with the Software.
5.6 “Eligible Features and Functions” means those features and functions of the Software that are eligible for use with
respect to a particular version of the Software or level of the Subscription. A list of the Eligible Features and
Functions that correspond to each version of the Software and Subscription levels may be found at
https://www.elastic.co/subscriptions.
5.7 “Malicious Code” means any code that is designed to harm, or otherwise disrupt in any unauthorized manner, the
operation of a recipients computer programs or computer systems or destroy or damage recipients data. For clarity,
Malicious Code shall not include any software bugs or errors handled through Support Services, or any standard features
of functions of the Software and/or any License Key that are intended to enforce the temporal and other limitations on
the scope of the use of the Software to the scope of the license purchased by You.
5.8 “Marks and Notices” means all Elasticsearch trademarks, trade names, logos and notices present on the Documentation
as originally provided by Elasticsearch.
5.9 “Registration” means Elasticsearchs then-current process under which You may register Your use of the Software with
Elasticsearch by providing certain information to Elasticsearch regarding your use of the Software.
5.10 “Subscription” means the right to receive Support Services and a License to the Software.
5.11 “Subscription Term” means the period of time for which You have purchased a Subscription.
5.12 “Trial Version” means that version of the Software available for use without the purchase of a Qualifying
Subscription and without Registration.
6. MISCELLANEOUS
This Agreement, including Attachment 1 hereto, which is hereby incorporated herein by this reference, completely and
exclusively states the entire agreement of the parties regarding the subject matter herein, and it supersedes, and its
terms govern, all prior proposals, agreements, or other communications between the parties, oral or written, regarding
such subject matter. For the avoidance of doubt, the parties hereby expressly acknowledge and agree that if You issue
any purchase order or similar document in connection with its purchase of a license to the Software, You will do so only
for Your internal, administrative purposes and not with the intent to provide any contractual terms. This Agreement may
not be modified except by a subsequently dated, written amendment that expressly amends this Agreement and which is
signed on behalf of Elasticsearch and You, by duly authorized representatives. If any provision hereof is held
unenforceable, this Agreement will continue without said provision and be interpreted to reflect the original intent of
the parties.
ATTACHMENT 1
ADDITIONAL TERMS AND CONDITIONS
A. The following additional terms and conditions apply to all Customers with principal offices in the United States
of America:
(1) Applicable Elasticsearch Entity. The entity providing the license is Elasticsearch, Inc., a Delaware corporation.
(2) Government Rights. The Software product is "Commercial Computer Software," as that term is defined in 48 C.F.R.
2.101, and as the term is used in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial computer
software" and "commercial computer software documentation". If acquired by or on behalf of a civilian agency, the U.S.
Government acquires this commercial computer software and/or commercial computer software documentation subject to the
terms of this Agreement, as specified in 48 C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal
Acquisition Regulation ("FAR") and its successors. If acquired by or on behalf of any agency within the Department of
Defense ("DOD"), the U.S. Government acquires this commercial computer software and/or commercial computer software
documentation subject to the terms of the Elasticsearch Software License Agreement as specified in 48 C.F.R. 227.7202-3
and 48 C.F.R. 227.7202-4 of the DOD FAR Supplement ("DFARS") and its successors, and consistent with 48 C.F.R. 227.7202.
This U.S. Government Rights clause, consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202 is in lieu of, and
supersedes, any other FAR, DFARS, or other clause or provision that addresses Government rights in computer software,
computer software documentation or technical data related to the Software under this Agreement and in any Subcontract
under which this commercial computer software and commercial computer software documentation is acquired or licensed.
(3) Export Control. You acknowledge that the goods, software and technology acquired from Elasticsearch are subject
to U.S. export control laws and regulations, including but not limited to the International Traffic In Arms Regulations
(“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export Administration Regulations ("EAR") (15 C.F.R. Parts 730-774
(2010)); the U.S. antiboycott regulations in the EAR and U.S. Department of the Treasury regulations; the economic
sanctions regulations and guidelines of the U.S. Department of the Treasury, Office of Foreign Assets Control, and the
USA Patriot Act (Title III of Pub. L. 107-56, signed into law October 26, 2001), as amended.  You are now and will
remain in the future compliant with all such export control laws and regulations, and will not export, re-export,
otherwise transfer any Elasticsearch goods, software or technology or disclose any Elasticsearch software or technology
to any person contrary to such laws or regulations.  You acknowledge that remote access to the Software may in certain
circumstances be considered a re-export of Software, and accordingly, may not be granted in contravention of U.S. export
control laws and regulations.
(4) Governing Law, Jurisdiction and Venue.
(a) Customers in California. If Customer is located in California (as determined by the Customer address on the
applicable Order Form, or for a trial license under 1.1(a), the location of person who installed the Software), this
Agreement will be governed by the laws of the State of California, without regard to its conflict of laws principles,
and all suits hereunder will be brought solely in Federal Court for the Northern District of California, or if that
court lacks subject matter jurisdiction, in any California State Court located in Santa Clara County.
(b) Customers Outside of California. If Customer is located anywhere other than California (as determined by the
Customer address on the applicable Order Form, or for a trial license under 1.1(a), the location of person who installed
the Software), this Agreement will be governed by the laws of the State of Delaware, without regard to its conflict of
laws principles, and all suits hereunder will be brought solely in Federal Court for the District of Delaware, or if
that court lacks subject matter jurisdiction, in any Delaware State Court located in Wilmington, Delaware.
(c) All Customers. This Agreement shall not be governed by the 1980 UN Convention on Contracts for the International
Sale of Goods. The parties hereby irrevocably waive any and all claims and defenses either might otherwise have in any
action or proceeding in any of the applicable courts set forth in (a) or (b) above, based upon any alleged lack of
personal jurisdiction, improper venue, forum non conveniens, or any similar claim or defense.
(d) Equitable Relief. A breach or threatened breach, by either party of Section 4 may cause irreparable harm for
which the non-breaching party shall be entitled to seek injunctive relief without being required to post a bond.
B. The following additional terms and conditions apply to all Customers with principal offices in Canada:
(1) Applicable Elasticsearch Entity. The entity providing the license is Elasticsearch B.C. Ltd., a corporation
incorporated under laws of the Province of British Columbia.
(2) Export Control. You acknowledge that the goods, software and technology acquired from Elasticsearch are subject
to the restrictions and controls set out in Section A(3) above as well as those imposed by the Export and Import Permits
Act (Canada) and the regulations thereunder and that you will comply with all applicable laws and regulations. Without
limitation, You acknowledge that the Marvel Software, or any portion thereof, will not be exported: (a) to any country
on Canada's Area Control List; (b) to any country subject to UN Security Council embargo or action; or (c) contrary to
Canada's Export Control List Item 5505. You are now and will remain in the future compliant with all such export control
laws and regulations, and will not export, re-export, otherwise transfer any Elasticsearch goods, software or technology
or disclose any Elasticsearch software or technology to any person contrary to such laws or regulations.  You will not
export or re-export the Marvel Software, or any portion thereof, directly or indirectly, in violation of the Canadian
export administration laws and regulations to any country or end user, or to any end user who you know or have reason to
know will utilize them in the design, development or production of nuclear, chemical or biological weapons. You further
acknowledge that the Marvel Software product may include technical data subject to such Canadian export regulations.
Elasticsearch does not represent that the Marvel Software is appropriate or available for use in all countries.
Elasticsearch prohibits accessing materials from countries or states where contents are illegal. You are using the
Marvel Software on your own initiative and you are responsible for compliance with all applicable laws. You hereby agree
to indemnify Elasticsearch and its affiliates from any claims, actions, liability or expenses (including reasonable
lawyers' fees) resulting from Your failure to act in accordance with the acknowledgements, agreements, and
representations in this Section B(2).
(3) Governing Law and Dispute Resolution. This Agreement shall be governed by the Province of Ontario and the
federal laws of Canada applicable therein without regard to conflict of laws provisions. The parties hereby irrevocably
waive any and all claims and defenses either might otherwise have in any such action or proceeding in any of such courts
based upon any alleged lack of personal jurisdiction, improper venue, forum non conveniens or any similar claim or
defense. Any dispute, claim or controversy arising out of or relating to this Agreement or the existence, breach,
termination, enforcement, interpretation or validity thereof, including the determination of the scope or applicability
of this agreement to arbitrate, (each, a “Dispute”), which the parties are unable to resolve after good faith
negotiations, shall be submitted first to the upper management level of the parties. The parties, through their upper
management level representatives shall meet within thirty (30) days of the Dispute being referred to them and if the
parties are unable to resolve such Dispute within thirty (30) days of meeting, the parties agree to seek to resolve the
Dispute through mediation with ADR Chambers in the City of Toronto, Ontario, Canada before pursuing any other
proceedings. The costs of the mediator shall be shared equally by the parties. If the Dispute has not been resolved
within thirty (30) days of the notice to desire to mediate, any party may terminate the mediation and proceed to
arbitration and the matter shall be referred to and finally resolved by arbitration at ADR Chambers pursuant to the
general ADR Chambers Rules for Arbitration in the City of Toronto, Ontario, Canada. The arbitration shall proceed in
accordance with the provisions of the Arbitration Act (Ontario). The arbitral panel shall consist of three (3)
arbitrators, selected as follows: each party shall appoint one (1) arbitrator; and those two (2) arbitrators shall
discuss and select a chairman. If the two (2) party-appointed arbitrators are unable to agree on the chairman, the
chairman shall be selected in accordance with the applicable rules of the arbitration body. Each arbitrator shall be
independent of each of the parties. The arbitrators shall have the authority to grant specific performance and to
allocate between the parties the costs of arbitration (including service fees, arbitrator fees and all other fees
related to the arbitration) in such equitable manner as the arbitrators may determine. The prevailing party in any
arbitration shall be entitled to receive reimbursement of its reasonable expenses incurred in connection therewith.
Judgment upon the award so rendered may be entered in a court having jurisdiction or application may be made to such
court for judicial acceptance of any award and an order of enforcement, as the case may be. Notwithstanding the
foregoing, Elasticsearch shall have the right to institute an action in a court of proper jurisdiction for preliminary
injunctive relief pending a final decision by the arbitrator, provided that a permanent injunction and damages shall
only be awarded by the arbitrator. The language to be used in the arbitral proceedings shall be English.
(4) Language. Any translation of this Agreement is done for local requirements and in the event of a dispute
between the English and any non-English version, the English version of this Agreement shall govern. At the request of
the parties, the official language of this Agreement and all communications and documents relating hereto is the English
language, and the English-language version shall govern all interpretation of the Agreement.  À la demande des parties,
la langue officielle de la présente convention ainsi que toutes communications et tous documents s'y rapportant est la
langue anglaise, et la version anglaise est celle qui régit toute interprétation de la présente convention.
(5) Warranty Disclaimer. For Customers with principal offices in the Province of Québec, the following new sentence
is to be added to the end of Section 3.3: “SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS OR EXCLUSIONS OF CERTAIN TYPES OF
DAMAGES AND/OR WARRANTIES AND CONDITIONS. THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SET FORTH IN THIS AGREEMENT SHALL
NOT APPLY IF AND ONLY IF AND TO THE EXTENT THAT THE LAWS OF A COMPETENT JURISDICTION REQUIRE LIABILITIES BEYOND AND
DESPITE THESE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS.”
(6) Limitation of Liability. For Customers with principal offices in the Province of Québec, the following new
sentence is to be added to the end of Section 4.1: “SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS OR EXCLUSIONS OF CERTAIN
TYPES OF DAMAGES AND/OR WARRANTIES AND CONDITIONS.  THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SET FORTH IN THIS
AGREEMENT SHALL NOT APPLY IF AND ONLY IF AND TO THE EXTENT THAT THE LAWS OF A COMPETENT JURISDICTION REQUIRE LIABILITIES
BEYOND AND DESPITE THESE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS.”
C. The following additional terms and conditions apply to all Customers with principal offices outside of the United
States of America and Canada:
(1) Applicable Elasticsearch Entity. The entity providing the license in Germany is Elasticsearch Gmbh; in France is
Elasticsearch SARL, in the United Kingdom is Elasticsearch Ltd, in Australia is Elasticsearch Pty Ltd., in Japan is
Elasticsearch KK, in Sweden is Elasticsearch AB, in Norway is Elasticsearch AS and in all other countries is
Elasticsearch BV.
(2) Choice of Law. This Agreement shall be governed by and construed in accordance with the laws of the State of New
York, without reference to or application of choice of law rules or principles. Notwithstanding any choice of law
provision or otherwise, the Uniform Computer Information Transactions Act (UCITA) and the United Nations Convention on
the International Sale of Goods shall not apply.
(3) Arbitration. Any dispute, claim or controversy arising out of or relating to this Agreement or the existence,
breach, termination, enforcement, interpretation or validity thereof, including the determination of the scope or
applicability of this agreement to arbitrate, (each, a “Dispute”) shall be referred to and finally resolved by
arbitration under the rules and at the location identified below. The arbitral panel shall consist of three (3)
arbitrators, selected as follows: each party shall appoint one (1) arbitrator; and those two (2) arbitrators shall
discuss and select a chairman. If the two party-appointed arbitrators are unable to agree on the chairman, the chairman
shall be selected in accordance with the applicable rules of the arbitration body. Each arbitrator shall be independent
of each of the parties. The arbitrators shall have the authority to grant specific performance and to allocate between
the parties the costs of arbitration (including service fees, arbitrator fees and all other fees related to the
arbitration) in such equitable manner as the arbitrators may determine. The prevailing party in any arbitration shall
be entitled to receive reimbursement of its reasonable expenses incurred in connection therewith. Judgment upon the
award so rendered may be entered in a court having jurisdiction or application may be made to such court for judicial
acceptance of any award and an order of enforcement, as the case may be. Notwithstanding the foregoing, Elasticsearch
shall have the right to institute an action in a court of proper jurisdiction for preliminary injunctive relief pending
a final decision by the arbitrator, provided that a permanent injunction and damages shall only be awarded by the
arbitrator. The language to be used in the arbitral proceedings shall be English.
In addition, the following terms only apply to Customers with principal offices within Europe, the Middle East or Africa
(EMEA):
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under the London
Court of International Arbitration (“LCIA”) Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York, USA. The seat, or legal place, of
arbitration shall be London, England.
(b) In addition, the following terms only apply to Customers with principal offices within Asia Pacific, Australia &
New Zealand:
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under the Rules of
Conciliation and Arbitration of the International Chamber of Commerce (“ICC”) in force on the date when the notice of
arbitration is submitted in accordance with such Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York, USA. The seat, or legal place, of
arbitration shall be Singapore.
(c) In addition, the following terms only apply to Customers with principal offices within the Americas (excluding
North America):
Arbitration Rules and Location. Any Dispute shall be referred to and finally resolved by arbitration under
International Dispute Resolution Procedures of the American Arbitration Association (“AAA”) in force on the date when
the notice of arbitration is submitted in accordance with such Procedures (which Procedures are deemed to be
incorporated by reference into this clause) on the basis that the governing law is the law of the State of New York,
USA. The seat, or legal place, of arbitration shall be New York, New York, USA.
(4) In addition, for Customers with principal offices within the UK, the following new sentence is added to the end
of Section 4.1:
Nothing in this Agreement shall have effect so as to limit or exclude a partys liability for death or personal injury
caused by negligence or for fraud including fraudulent misrepresentation and this Section 4.1 shall take effect subject
to this provision.
(5) In addition, for Customers with principal offices within France, Sections 1.2, 3 and 4.1 of the Agreement are
deleted and replaced with the following new Sections 1.2, 3.3 and 4.1:
1.2 Reservation of Rights; Restrictions. Elasticsearch owns all right title and interest in and to the Software and
any derivative works thereof, and except as expressly set forth in Section 1.1 above, no other license to the Software
is granted to You by implication, or otherwise. You agree not to prepare derivative works from, modify, copy or use the
Software in any manner except as expressly permitted in this Agreement; provided that You may copy the Software for
archival purposes, only where such software is provided on a non-durable medium; and You may decompile the Software,
where necessary for interoperability purposes and where necessary for the correction of errors making the software unfit
for its intended purpose, if such right is not reserved by Elasticsearch as editor of the Software. Pursuant to article
L122-6-1 of the French intellectual property code, Elasticsearch reserves the right to correct any bugs as necessary for
the Software to serve its intended purpose. You agree not to: (i) transfer, sell, rent, lease, distribute, sublicense,
loan or otherwise transfer the Software in whole or in part to any third party; (ii) use the Software for providing
time-sharing services, any software-as-a-service offering (“SaaS”), service bureau services or as part of an application
services provider or other service offering; (iii) alter or remove any proprietary notices in the Software; or (iv) make
available to any third party any analysis of the results of operation of the Software, including benchmarking results,
without the prior written consent of Elasticsearch.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS”
WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR
STATUTORY REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE WITH
RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER, ELASTICSEARCH DOES
NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE SOFTWARE WILL BE
UNINTERRUPTED.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT OR
UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE, OR THE
PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT,
INCLUDING NEGLIGENCE. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH, THROUGH
GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1, OR IN CASE OF
DEATH OR PERSONAL INJURY.
(6) In addition, for Customers with principal offices within Australia, Sections 4.1, 4.2 and 4.3 of the Agreement
are deleted and replaced with the following new Sections 4.1, 4.2 and 4.3:
4.1 Disclaimer of Certain Damages. Subject to clause 4.3, a party is not liable for Consequential Loss however
caused (including by the negligence of that party) suffered or incurred by the other party in connection with this
agreement. “Consequential Loss” means loss of revenues, loss of reputation, indirect loss, loss of profits,
consequential loss, loss of actual or anticipated savings, indirect loss, lost opportunities, including opportunities to
enter into arrangements with third parties, loss or damage in connection with claims against by third parties, or loss
or corruption or data.
4.2 Damages Cap. SUBJECT TO CLAUSES 4.1 AND 4.3, ANY LIABILITY OF ELASTICSEARCH FOR ANY LOSS OR DAMAGE, HOWEVER
CAUSED (INCLUDING BY THE NEGLIGENCE OF ELASTICSEARCH), SUFFERED BY YOU IN CONNECTION WITH THIS AGREEMENT IS LIMITED TO
THE AMOUNT YOU PAID, IN THE TWELVE (12) MONTHS IMMEDIATELY PRIOR TO THE EVENT GIVING RISE TO LIABILITY, UNDER THE
ELASTICSEARCH SUPPORT SERVICES AGREEMENT IN CONNECTION WITH WHICH YOU OBTAINED THE LICENSE TO USE THE SOFTWARE. THE
LIMITATION SET OUT IN THIS SECTION 4.2 IS AN AGGREGATE LIMIT FOR ALL CLAIMS, WHENEVER MADE.
4.3 Limitation and Disclaimer Exceptions. If the Competition and Consumer Act 2010 (Cth) or any other legislation or
any other legislation states that there is a guarantee in relation to any good or service supplied by Elasticsearch in
connection with this agreement, and Elasticsearchs liability for failing to comply with that guarantee cannot be
excluded but may be limited, Sections 4.1 and 4.2 do not apply to that liability and instead Elasticsearchs liability
for such failure is limited (at Elasticsearchs election) to, in the case of a supply of goods, the Elasticsearch
replacing the goods or supplying equivalent goods or repairing the goods, or in the case of a supply of services,
Elasticsearch supplying the services again or paying the cost of having the services supplied again.
(7) In addition, for Customers with principal offices within Japan, Sections 1.2, 3 and 4.1 of the Agreement are
deleted and replaced with the following new Sections 1.2, 3.3 and 4.1:
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You, Elasticsearch owns all right title and
interest in and to the Software and any derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Software is granted to You by implication or otherwise. You agree not to: (i) prepare
derivative works from, modify, copy or use the Software in any manner except as expressly permitted in this Agreement or
applicable law; (ii) transfer, sell, rent, lease, distribute, sublicense, loan or otherwise transfer the Software in
whole or in part to any third party; (iii) use the Software for providing time-sharing services, any
software-as-a-service offering (“SaaS”), service bureau services or as part of an application services provider or other
service offering; (iv) alter or remove any proprietary notices in the Software; or (v) make available to any third party
any analysis of the results of operation of the Software, including benchmarking results, without the prior written
consent of Elasticsearch.
3.3 Warranty Disclaimer. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE SOFTWARE IS PROVIDED “AS IS”
WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR
STATUTORY REGARDING OR RELATING TO THE SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW,
ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF
THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE SOFTWARE WILL BE UNINTERRUPTED.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY LOSS OF
PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
SPECIALINDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING OUT OF THE USE
OR INABILITY TO USE THE SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH
THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY
OTHER LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.

View File

@ -1,52 +0,0 @@
Elasticsearch Watcher
Copyright 2009-2015 Elastic
---
This product includes software developed by The Apache Software
Foundation (http://www.apache.org/).
---
This product contains software distributed under
Common Development and Distribution License 1.0 (CDDL)
JavaMail API 1.5.3
https://java.net/projects/javamail/pages/Home
https://java.net/projects/javamail/pages/License
JavaBeans Activation Framework 1.1.1
http://www.oracle.com/technetwork/articles/java/index-135046.html
---
This product contains software developed by Mike Samuel. The
following is the copyright and notice text for this software:
Copyright (c) 2011, Mike Samuel
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the OWASP nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.