[7.x] Fix ReloadSecureSettings API to consume password (#54771) (#55059)

The secure_settings_password was never taken into consideration in
the ReloadSecureSettings API. This commit fixes that and adds
necessary REST layer testing. Doing so, it also:

- Allows TestClusters to have a password protected keystore
so that it can be set for tests.
- Adds a parameter to the run task so that elastisearch can
be run with a password protected keystore from source.
This commit is contained in:
Ioannis Kakavas 2020-04-13 09:50:55 +03:00 committed by GitHub
parent 862799956c
commit 7a8a66d9ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 393 additions and 38 deletions

View File

@ -408,6 +408,14 @@ class Run extends DefaultTask {
public void setDataDir(String dataDirStr) {
project.project(':distribution').run.dataDir = dataDirStr
}
@Option(
option = "keystore-password",
description = "Set the elasticsearch keystore password"
)
public void setKeystorePassword(String password) {
project.project(':distribution').run.keystorePassword = password
}
}
task run(type: Run) {

View File

@ -174,6 +174,11 @@ public class ElasticsearchCluster implements TestClusterConfiguration, Named {
nodes.all(each -> each.keystore(key, valueSupplier));
}
@Override
public void keystorePassword(String password) {
nodes.all(each -> each.keystorePassword(password));
}
@Override
public void cliSetup(String binTool, CharSequence... args) {
nodes.all(each -> each.cliSetup(binTool, args));

View File

@ -143,6 +143,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
private final Path httpPortsFile;
private final Path esStdoutFile;
private final Path esStderrFile;
private final Path esStdinFile;
private final Path tmpDir;
private int currentDistro = 0;
@ -154,6 +155,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
private String httpPort = "0";
private String transportPort = "0";
private Path confPathData;
private String keystorePassword = "";
ElasticsearchNode(String name, Project project, ReaperService reaper, File workingDirBase, Jdk bwcJdk) {
this.path = project.getPath();
@ -170,6 +172,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
httpPortsFile = confPathLogs.resolve("http.ports");
esStdoutFile = confPathLogs.resolve("es.stdout.log");
esStderrFile = confPathLogs.resolve("es.stderr.log");
esStdinFile = workingDir.resolve("es.stdin");
tmpDir = workingDir.resolve("tmp");
waitConditions.put("ports files", this::checkPortsFilesExistWithDelay);
@ -305,6 +308,11 @@ public class ElasticsearchNode implements TestClusterConfiguration {
keystoreFiles.put(key, valueSupplier);
}
@Override
public void keystorePassword(String password) {
keystorePassword = password;
}
@Override
public void cliSetup(String binTool, CharSequence... args) {
cliSetup.add(new CliEntry(binTool, args));
@ -439,13 +447,17 @@ public class ElasticsearchNode implements TestClusterConfiguration {
}
}
logToProcessStdout("Creating elasticsearch keystore with password set to [" + keystorePassword + "]");
if (keystorePassword.length() > 0) {
runElasticsearchBinScriptWithInput(keystorePassword + "\n" + keystorePassword, "elasticsearch-keystore", "create", "-p");
} else {
runElasticsearchBinScript("elasticsearch-keystore", "create");
}
if (keystoreSettings.isEmpty() == false || keystoreFiles.isEmpty() == false) {
logToProcessStdout("Adding " + keystoreSettings.size() + " keystore settings and " + keystoreFiles.size() + " keystore files");
runElasticsearchBinScript("elasticsearch-keystore", "create");
keystoreSettings.forEach(
(key, value) -> runElasticsearchBinScriptWithInput(value.toString(), "elasticsearch-keystore", "add", "-x", key)
);
keystoreSettings.forEach((key, value) -> runKeystoreCommandWithPassword(keystorePassword, value.toString(), "add", "-x", key));
for (Map.Entry<String, File> entry : keystoreFiles.entrySet()) {
File file = entry.getValue();
@ -453,7 +465,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
if (file.exists() == false) {
throw new TestClustersException("supplied keystore file " + file + " does not exist, require for " + this);
}
runElasticsearchBinScript("elasticsearch-keystore", "add-file", entry.getKey(), file.getAbsolutePath());
runKeystoreCommandWithPassword(keystorePassword, "", "add-file", entry.getKey(), file.getAbsolutePath());
}
}
@ -657,6 +669,11 @@ public class ElasticsearchNode implements TestClusterConfiguration {
}
}
private void runKeystoreCommandWithPassword(String keystorePassword, String input, CharSequence... args) {
final String actualInput = keystorePassword.length() > 0 ? keystorePassword + "\n" + input : input;
runElasticsearchBinScriptWithInput(actualInput, "elasticsearch-keystore", args);
}
private void runElasticsearchBinScript(String tool, CharSequence... args) {
runElasticsearchBinScriptWithInput("", tool, args);
}
@ -746,6 +763,14 @@ public class ElasticsearchNode implements TestClusterConfiguration {
processBuilder.redirectError(ProcessBuilder.Redirect.appendTo(esStderrFile.toFile()));
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(esStdoutFile.toFile()));
if (keystorePassword != null && keystorePassword.length() > 0) {
try {
Files.write(esStdinFile, (keystorePassword + "\n").getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
processBuilder.redirectInput(esStdinFile.toFile());
} catch (IOException e) {
throw new TestClustersException("Failed to set the keystore password for " + this, e);
}
}
LOGGER.info("Running `{}` in `{}` for {} env: {}", command, workingDir, this, environment);
try {
esProcess = processBuilder.start();

View File

@ -28,6 +28,8 @@ public class RunTask extends DefaultTestClustersTask {
private Path dataDir = null;
private String keystorePassword = "";
@Option(option = "debug-jvm", description = "Enable debugging configuration, to allow attaching a debugger to elasticsearch.")
public void setDebug(boolean enabled) {
this.debug = enabled;
@ -43,6 +45,17 @@ public class RunTask extends DefaultTestClustersTask {
dataDir = Paths.get(dataDirStr).toAbsolutePath();
}
@Option(option = "keystore-password", description = "Set the elasticsearch keystore password")
public void setKeystorePassword(String password) {
keystorePassword = password;
}
@Input
@Optional
public String getKeystorePassword() {
return keystorePassword;
}
@Input
@Optional
public String getDataDir() {
@ -90,6 +103,9 @@ public class RunTask extends DefaultTestClustersTask {
node.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + debugPort);
debugPort += 1;
}
if (keystorePassword.length() > 0) {
node.keystorePassword(keystorePassword);
}
}
}
}

View File

@ -57,6 +57,8 @@ public interface TestClusterConfiguration {
void keystore(String key, FileSupplier valueSupplier);
void keystorePassword(String password);
void cliSetup(String binTool, CharSequence... args);
void setting(String key, String value);

View File

@ -58,6 +58,7 @@ testClusters.integTest {
}
setting 'xpack.autoscaling.enabled', 'true'
setting 'xpack.eql.enabled', 'true'
keystorePassword 's3cr3t'
}
// enable regexes in painless so our tests don't complain about example snippets that use them

View File

@ -44,16 +44,25 @@ standard. It is justifiable only when retrying failed reload operations.
[[cluster-nodes-reload-secure-settings-api-request-body]]
==== {api-request-body-title}
`reload_secure_settings`::
`secure_settings_password`::
(Optional, string) The password for the {es} keystore.
[[cluster-nodes-reload-secure-settings-api-example]]
==== {api-examples-title}
The following examples assume a common password for the {es} keystore on every
node of the cluster:
[source,console]
--------------------------------------------------
POST _nodes/reload_secure_settings
{
"secure_settings_password":"s3cr3t"
}
POST _nodes/nodeId1,nodeId2/reload_secure_settings
{
"secure_settings_password":"s3cr3t"
}
--------------------------------------------------
// TEST[setup:node]
// TEST[s/nodeId1,nodeId2/*/]
@ -81,27 +90,3 @@ that was thrown during the reload process, if any.
--------------------------------------------------
// TESTRESPONSE[s/"my_cluster"/$body.cluster_name/]
// TESTRESPONSE[s/"pQHNt5rXTTWNvUgOrdynKg"/\$node_name/]
The following example uses a common password for the {es} keystore on every
node of the cluster:
[source,js]
--------------------------------------------------
POST _nodes/reload_secure_settings
{
"reload_secure_settings": "s3cr3t"
}
--------------------------------------------------
// NOTCONSOLE
The following example uses a password for the {es} keystore on the local node:
[source,js]
--------------------------------------------------
POST _nodes/_local/reload_secure_settings
{
"reload_secure_settings": "s3cr3t"
}
--------------------------------------------------
// NOTCONSOLE

View File

@ -35,7 +35,7 @@ using the `bin/elasticsearch-keystore add` command, call:
----
POST _nodes/reload_secure_settings
{
"reload_secure_settings": "s3cr3t" <1>
"secure_settings_password": "s3cr3t" <1>
}
----
// NOTCONSOLE

View File

@ -32,6 +32,10 @@
"type": "time",
"description": "Explicit operation timeout"
}
},
"body": {
"description": "An object containing the password for the elasticsearch keystore",
"required": false
}
}
}

View File

@ -1,8 +1,31 @@
---
"node_reload_secure_settings test":
"node_reload_secure_settings test wrong password":
- skip:
version: " - 7.99.99"
reason: "support for reloading password protected keystores was introduced in 7.7.0"
- do:
nodes.reload_secure_settings:
node_id: _local
body:
secure_settings_password: awrongpasswordhere
- set:
nodes._arbitrary_key_: node_id
- is_true: nodes
- is_true: cluster_name
- match: { nodes.$node_id.reload_exception.type: "security_exception" }
- match: { nodes.$node_id.reload_exception.reason: "Provided keystore password was incorrect" }
---
"node_reload_secure_settings test correct(empty) password":
- do:
nodes.reload_secure_settings: {}
- set:
nodes._arbitrary_key_: node_id
- is_true: nodes
- is_true: cluster_name
- is_false: nodes.$node_id.reload_exception

View File

@ -75,7 +75,7 @@ public final class RestReloadSecureSettingsAction extends BaseRestHandler {
.setNodesIds(nodesIds);
request.withContentOrSourceParamParserOrNull(parser -> {
if (parser != null) {
final NodesReloadSecureSettingsRequest nodesRequest = nodesRequestBuilder.request();
final NodesReloadSecureSettingsRequest nodesRequest = PARSER.parse(parser, null);
nodesRequestBuilder.setSecureStorePassword(nodesRequest.getSecureSettingsPassword());
}
});

View File

@ -0,0 +1,52 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Tests that need to run against an Elasticsearch cluster that
* is using a password protected keystore in its nodes.
*/
apply plugin: 'elasticsearch.testclusters'
apply plugin: 'elasticsearch.standalone-rest-test'
apply plugin: 'elasticsearch.rest-test'
dependencies {
testCompile project(path: xpackModule('core'), configuration: 'default')
}
testClusters.integTest {
testDistribution = 'DEFAULT'
numberOfNodes = 2
keystorePassword 's3cr3t'
setting 'xpack.security.enabled', 'true'
setting 'xpack.security.authc.anonymous.roles', 'anonymous'
setting 'xpack.security.transport.ssl.enabled', 'true'
setting 'xpack.security.transport.ssl.certificate', 'transport.crt'
setting 'xpack.security.transport.ssl.key', 'transport.key'
setting 'xpack.security.transport.ssl.key_passphrase', 'transport-password'
setting 'xpack.security.transport.ssl.certificate_authorities', 'ca.crt'
extraConfigFile 'transport.key', file('src/test/resources/ssl/transport.key')
extraConfigFile 'transport.crt', file('src/test/resources/ssl/transport.crt')
extraConfigFile 'ca.crt', file('src/test/resources/ssl/ca.crt')
extraConfigFile 'roles.yml', file('src/test/resources/roles.yml')
user username: 'admin_user', password: 'admin-password'
user username:'test-user' ,password: 'test-password', role: 'user_role'
}

View File

@ -0,0 +1,97 @@
/*
* 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.password_protected_keystore;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.ObjectPath;
import org.elasticsearch.test.rest.ESRestTestCase;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import java.util.Map;
public class ReloadSecureSettingsWithPasswordProtectedKeystoreRestIT extends ESRestTestCase {
// From build.gradle
private final String KEYSTORE_PASSWORD = "s3cr3t";
private final int NUM_NODES = 2;
@SuppressWarnings("unchecked")
public void testReloadSecureSettingsWithCorrectPassword() throws Exception {
final Request request = new Request("POST", "_nodes/reload_secure_settings");
request.setJsonEntity("{\"secure_settings_password\":\"" + KEYSTORE_PASSWORD + "\"}");
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("cluster_name", map), equalTo("integTest"));
assertThat(map.get("nodes"), instanceOf(Map.class));
final Map<String, Object> nodes = (Map<String, Object>) map.get("nodes");
assertThat(nodes.size(), equalTo(NUM_NODES));
for (Map.Entry<String, Object> entry : nodes.entrySet()) {
assertThat(entry.getValue(), instanceOf(Map.class));
final Map<String, Object> node = (Map<String, Object>) entry.getValue();
assertThat(node.get("reload_exception"), nullValue());
}
}
@SuppressWarnings("unchecked")
public void testReloadSecureSettingsWithInCorrectPassword() throws Exception {
final Request request = new Request("POST", "_nodes/reload_secure_settings");
request.setJsonEntity("{\"secure_settings_password\":\"" + KEYSTORE_PASSWORD + randomAlphaOfLength(7) + "\"}");
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("cluster_name", map), equalTo("integTest"));
assertThat(map.get("nodes"), instanceOf(Map.class));
final Map<String, Object> nodes = (Map<String, Object>) map.get("nodes");
assertThat(nodes.size(), equalTo(NUM_NODES));
for (Map.Entry<String, Object> entry : nodes.entrySet()) {
assertThat(entry.getValue(), instanceOf(Map.class));
final Map<String, Object> node = (Map<String, Object>) entry.getValue();
assertThat(node.get("reload_exception"), instanceOf(Map.class));
assertThat(ObjectPath.eval("reload_exception.reason", node), equalTo("Provided keystore password was incorrect"));
assertThat(ObjectPath.eval("reload_exception.type", node), equalTo("security_exception"));
}
}
@SuppressWarnings("unchecked")
public void testReloadSecureSettingsWithEmptyPassword() throws Exception {
final Request request = new Request("POST", "_nodes/reload_secure_settings");
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("cluster_name", map), equalTo("integTest"));
assertThat(map.get("nodes"), instanceOf(Map.class));
final Map<String, Object> nodes = (Map<String, Object>) map.get("nodes");
assertThat(nodes.size(), equalTo(NUM_NODES));
for (Map.Entry<String, Object> entry : nodes.entrySet()) {
assertThat(entry.getValue(), instanceOf(Map.class));
final Map<String, Object> node = (Map<String, Object>) entry.getValue();
assertThat(node.get("reload_exception"), instanceOf(Map.class));
assertThat(ObjectPath.eval("reload_exception.reason", node), equalTo("Provided keystore password was incorrect"));
assertThat(ObjectPath.eval("reload_exception.type", node), equalTo("security_exception"));
}
}
@Override
protected Settings restClientSettings() {
String token = basicAuthHeaderValue("test-user", new SecureString("test-password".toCharArray()));
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", token)
.build();
}
@Override
protected Settings restAdminSettings() {
String token = basicAuthHeaderValue("admin_user", new SecureString("admin-password".toCharArray()));
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", token)
.build();
}
}

View File

@ -0,0 +1,4 @@
# user needs to call cluster:admin/nodes/reload_secure_settings
user_role:
cluster: [ALL]
indices: []

View File

@ -0,0 +1,31 @@
= Keystore Details
This document details the steps used to create the certificate and keystore files in this directory.
== Instructions on generating certificates
The certificates in this directory have been generated using elasticsearch-certutil (8.0.0 SNAPSHOT)
[source,shell]
-----------------------------------------------------------------------------------------------------------
elasticsearch-certutil ca --pem --out=ca.zip --pass="ca-password" --days=3500
unzip ca.zip
mv ca/ca.* ./
rm ca.zip
rmdir ca
-----------------------------------------------------------------------------------------------------------
[source,shell]
-----------------------------------------------------------------------------------------------------------
elasticsearch-certutil cert --pem --name=transport --out=transport.zip --pass="transport-password" --days=3500 \
--ca-cert=ca.crt --ca-key=ca.key --ca-pass="ca-password" \
--dns=localhost --dns=localhost.localdomain --dns=localhost4 --dns=localhost4.localdomain4 --dns=localhost6 --dns=localhost6.localdomain6 \
--ip=127.0.0.1 --ip=0:0:0:0:0:0:0:1
unzip transport.zip
mv transport/transport.* ./
rm transport.zip
rmdir transport
-----------------------------------------------------------------------------------------------------------

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSTCCAjGgAwIBAgIUGuBmPtwyEv7WZ1H0Yy5vyEEYVR8wDQYJKoZIhvcNAQEL
BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l
cmF0ZWQgQ0EwHhcNMjAwNDA3MTEzMDA1WhcNMjkxMTA2MTEzMDA1WjA0MTIwMAYD
VQQDEylFbGFzdGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALeTNx0a6X+Fhf6IQj4ggN9U
1HGIzJKEHGIpATDgbfdIv88e0O0I6HN7pmLf5LuUPDGc2oLGnxqATgnFek5eJ4QW
sKgflGB4C0EgQH4JAooIG0EI6aj3IcdzBwH8bdymAdsGj0Zcvm6wjhLixgiN3yIM
8KJAtJrSCITI88gfXhXyU0XCSzgruFkdvHjFBCWpCaK3hnjoiO65186PcGbrZHB8
Izs2soa6H1AHVDMhmJjlwJWYtibjok+sgrjkDWG7cBh6Al7yXGUBOs9SgMXUpI3Y
0r/dDdeISdI5VzwKZpX6qYcNJI+jtgZUD0alMKBxjq3+v8GlDE/QVNyDwp/7SA8C
AwEAAaNTMFEwHQYDVR0OBBYEFMdSVLWtAhqfDXRQj+5o80nK1XaQMB8GA1UdIwQY
MBaAFMdSVLWtAhqfDXRQj+5o80nK1XaQMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAEiaX+JtgDb6PuPqO9NtxuPEsg6Ghj0dVYLOYE9cmKS9xeyy
sIDJbPl8vy7gomVBDYe+3qM2UElKFdApKGjOctjNlH8npkQxg72sarBUzOfuFPx5
Z6u5TuKMfqjx2dwpTr3Yp/iUFs9e9zcDMnQYt5B6SCjKpKKodKKh+CE6wOg4nbSF
43OYO9HFHfwIlEqvof7j0r5gnPf5fYSYybYFAqy4oAfpESPq9lJuEvA46TrGpmP6
IpMYkJJ6O+98A7RHo5kstZJdnG5paAKobdPEYxbIZvRyMJ8IxW8kSAaTKsK7W34k
IYciDd/YY3R+nhnh8F5DjVcyc79Zkv9Cjig/OxQ=
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,8209E02F62E3909502FECF5E5E9CF7A7
EdOFZ6/z/e4elfeAKs2B++/Px/IpiZdmiseZPjfwa6jgpY+8sehmze5+34vrxYJT
cMBH3QafmhdQZ4/Eo7DVFONrjJ3OmD5//ZiTIujTPwMsgGAdeq0yMC0cDkzg8SQ7
KvTh0PY0feC6bVsY+YjDprDfpqIWf89F8ikgat9cmucV9YO3RbYnxgxRIztbHLP3
GenAtdG+v7DzdefAdRQktBSNldkadsY6d/kVBknOHcA4pB/UtDpz77ZF40CNB95z
1Tr37nNnuRBUNHbKklXuozkvYLah66tFxA5v7Rf6F37d2QGBkgDphg/QMbJrrB+q
MsfiXeXqRaCzBN/ZuzTQAdQ/67XpQ+Ax89UOiT6SkKBKN1uNDk7Juzv5zHrq7aWS
aj1qtHDG2vMB+UM5A1MngD1LtXzs21Q0+9a2UT83x+VIP0hVq2uKmO8wAQ9gbBe9
wkBPca4gLYlbIMWzaAe4DV0rcmux+i7Ezk8oVYW1JcoGjoZ3f9KewIQynBUlXXuO
EzSl4R3yiF/birrK9Lo6c9hOcQKCW2qAX73BKq8PjKgWT3rnqzg97q9PPK/vaien
fwSrTXDgEoy1RCwsPsxjyRf0LGFYLUFRVqrbFPhhjg4aEiuzawcpvRxjorC5UX0L
dpImNssdALDd0BbiqAbChUryFSSxFhQ2yo6hfUXZevD236b09V0jUpnZeyQjeTTk
fhhAUUpnd1YzWuYneD2JZQKvGdgWgYRyEKParFeHLjp95rXNWPSOgoAM+w0fFEjq
zkYQMaDGSnUWbc6LVv2exyRIRTrLAWamKnne7z8VxzetqXXmuX0WJb2lFiYMUw4/
wf31RA8ZsVSgb9werSyPD9aRe/+YZM+kM3/3MC4jJGc6OJuDqEOhhB06L2Df2AWU
UQwZ7y+2yUC1kcFzc8+oT1TNgBHixouY+oqWkhbdCkbUFUe4FwXNXrMyrY9gZs1/
PEkhVxxYgpLwifkbfQRJPeJvXxh7NxeolXyISaVENdLkMMYUhdsKTa+GOQbO2yfa
4BhOwAqJvyDFfsRxLiDlbxjzvY5qnMl0e/q8wZ60onHJOFCTCfm2BNx7sW+Sk5Kx
zm0Rxsz4rIIxA5S6zbbdsHxjTC9XiUelKaq+W0XTg76USYneORQNN/Mk9sCXvTud
HUqmSf1wREA1PdEcoJ3tMoAOZWGY43/IrdoG3bTNT96AdToD+D+Or8M2VcOZorVf
c3IRNfxGv2/SwhxW/z4tSLSToSJlt4QKxU9Xzm4UundDy1cHmS1faN6+bBnI5+/F
OKwzPCCUJ6H02CAjx2P/P6YEjoLl8B+7h4whlOfT/+IQbzOcGMpPyGu4jSf1KffA
asAQeBvYTx0QPdv2E7e216RLOlp/ERMzkUvF1G7UYKF7Ao6cUpSH6nvGABPLKNXV
fqjpWq8O4R1UEUXi6dqF1HfAHllI+vMw7LzRJK/5zVrWlJPm4c/Rng5OkK7aAGee
J0eTSlCdNpyaZzjyk2ZAQ54kZVqAS90zS1zo6lg2v9yfAfz6eYlfl2OGfFVG40Jt
oYxEVcG9LeD3XOkPOnTblHdKMor8cQt+TEJPu9eM31ay1QSilixx2yfOOFTgJZOi
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIVAIdpYPATbRn96E+eVTG/s0byNh/FMA0GCSqGSIb3DQEB
CwUAMDQxMjAwBgNVBAMTKUVsYXN0aWMgQ2VydGlmaWNhdGUgVG9vbCBBdXRvZ2Vu
ZXJhdGVkIENBMB4XDTIwMDQwNzExMzA1OFoXDTI5MTEwNjExMzA1OFowFDESMBAG
A1UEAxMJdHJhbnNwb3J0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
pvfY9x3F8xR2+noX0PWxsc6XrKCxTQcbSHkkEr2Qr6BqGVXwEj/d6ocqdgcC7IZy
0HEwewBbO69Pg8INQ/21stcwZzW+4ZnKYsBmaZx1yCxYcplndFm9t5Fj/jTBsoJ3
PemvJwMrewuVSwsE4WHVkXz4KVETfUd8DZiWoVnlRgaXfvvudib1DNxtuGEra+Zh
d3JcC1Wsn51qjpHsj/6s/usT6hmlm4Bu5tjAMxXFVX6J0skfRSVhLmNWgr86WBKB
9/qTJU34FBQGh2Ok/knkiO5rae+UCPpEpCNCCV3rFcMdxP613WfemRRqhUL0V6g3
n4ExJa0853SsfvPEyHOADQIDAQABo4HgMIHdMB0GA1UdDgQWBBSraIvkNPX2TQQg
h8Ee3mWCALYr/zAfBgNVHSMEGDAWgBTHUlS1rQIanw10UI/uaPNJytV2kDCBjwYD
VR0RBIGHMIGEgglsb2NhbGhvc3SCF2xvY2FsaG9zdDYubG9jYWxkb21haW42hwR/
AAABhxAAAAAAAAAAAAAAAAAAAAABggpsb2NhbGhvc3Q0ggpsb2NhbGhvc3Q2ghVs
b2NhbGhvc3QubG9jYWxkb21haW6CF2xvY2FsaG9zdDQubG9jYWxkb21haW40MAkG
A1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBABRKISeKdMRRQAvZBan2ONUFUD3P
7Aq8JP+GttC9Y+1uI0CTIZsnih+tuZ9l2Gq9nyfBVMcDSutlnBoQZLg5lCYQPaK7
SuFhPRHGauEGYw1TjhrMEPokxzoT/X0/Sz5Ter6/qWzPKQs9EuaVJ27XfZd+kySn
S+cXd95woi+S8JQzQbcpA0rXCnWJ3l2frwG/3Hg8f82x2c6vgOzTG0Hklp0sFkUt
UqaBHGXPLiitaB01jUX60HZbxt5HIEseLctUmQlDtAEWwA3X6cRUEjulwRx8s52T
1FT2ORbVJ7ybKARGBSs932Fv2rWGmg8pOBA4ulJTJNvT0T+ob/H/i40Qd04=
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,DAC0DDB93011ABD08161118074F353A7
hPjzr8y4t3omv6jItFSxF/UeirrdlMhFoxxsw+E5fl4hRjD2J6LuUpOl0XBuvrCO
2NN9Simlkfo57l2O8tZ3xwKU037x9qP2O3wo0FZ4OuRcLbXZtp5kIV30/wdo0kbp
GV+18PtGfReo75rszs/VAm9Hg1URqVw0La2r7DomYQB9FJY8N8mwSdSvF194kjGO
pBxiuzzECUwXEGuMRzmc1Cddbw7NsIdg43FRd1uoC4dqj9yBonYEYe5P8WgopL4N
obTi6PzH+kqDSCaJo7Fdr9CYo37f2YsSbtHmuEZP58J/aSB9nl5wdAmas3/dohrI
5GSM9zp+UocFuV6Uf+X9TTJMt97BlRgFdPODh88pTKGLVQyKeBPQbVjgwl9mttxO
i+c/dej/jHt0gwlt8cvZw0Ss50YdNnWtck91yYpXE7iz59CTY+QI24DEvsaP4bkR
QYdIhJHOYamGW0ttCSU8bw1h9RubIvSa+BoiuB+1TaCYU+azuaAYnFlyuR31z4rD
yniPMnb0+5uOkU/srwb4MxVVw/0iYkKAGTEwdLPKhyheuDU9ixkNOQ/k12zV0R7d
gzMFQOlrB4v8Y4LrsNPnAz/uCTvKgBrOS8p076qeGkSX+JIZVNHYyzLnSy7p6hjO
eD3tDx/SA1HaiLzD1VqujnYb6wshYjQGkSPSY3COq8dQgpCqMAlkOycUQO1SbuNt
HZFv9X0w2z5HjPJXtKLLXMLeluNNRQD+IVhvbZjIM1cAUQNqL3OQPGa7W5RYoFYK
rDffzQAzukD5dt6jH+uu3cwnEeJiW8hxZ0+DHJR1X5EJWpN544yTl8jgSPT8MPAU
kxq7OyE0F/JY+UWP1hPILimDrf3Ov8KRtTDGsSvV3IcX+92QKMcvnK21QBZqZjSs
zcmjp2jN1MLwieJyZ3un0MUT9kOyG/5vGoAJe9O/KDtv6rrhKQN5JHi5yKw0Uwi9
CwrwwkxbRLSBbWugZGXyBHkR/RGIuEEysLKRFej2q4WBZrPOzZlgyvgBbd6/4Eg5
twngo6JTmYALwVJNW/drok1H0JelanZ6jM/JjNRFWMZnS5o+4cwQURB5O7TIKHdV
7zEkaw82Ng0Nq8dPrU8N9G3LTmIC1E4t++L+D18C2lV0ZDd0Svh3NIA65FXSRvX9
2g9GQGfGGbgydmjv9j5lx6VdhuTgYKRL4b5bS7VnH+F9m864g/MtSQpXQPR5B54g
YHFGiKCAzruZt1MmJ5m8Jvpg84i2lIZkGImwAstV7xVkmQoC3i77awmcQP6s7rJd
Lo7RKEysVPDbzdnZnWGK0PWJwtgsqrAvVcK7ghygi+vSQkDF0L7qunchOKa00oZR
LZa08b5BWuXeqw4lXZDQDT7hk3NUyW3H7Z1uxUlt1kvcGb6zrInW6Bin0hqsODvj
0drMOZp/5NTDSwcEzkW+LgjfKZw8Szmhlt3v+luNFr3KzbnFtEvewD1OVikNGzm9
sfZ899zNkWfvNJaXL3bvzbTn9d8T15YKCwO9RqPpYKDqXBaC4+OjbNsy4AW/JHPr
H/i3D3rhMXR/CALhp4+Knq4o3vMA+3TsUeZ3lOTogobVloWfixIIiRXfaqT4LmEC
-----END RSA PRIVATE KEY-----