Skip to content

Commit

Permalink
Revert "Support PKCS#12 encoded certificates (#17261)" (#17801)
Browse files Browse the repository at this point in the history
* Revert "Support PKCS#12 encoded certificates (#17261)"

This reverts commit de91bd0.

* Fixing tests
  • Loading branch information
kobelb authored Apr 19, 2018
1 parent e042460 commit 97df91f
Show file tree
Hide file tree
Showing 16 changed files with 65 additions and 218 deletions.
9 changes: 0 additions & 9 deletions docs/setup/production.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,6 @@ server.ssl.key: /path/to/your/server.key
server.ssl.certificate: /path/to/your/server.crt
----

Alternatively, you can specify a PKCS#12 encoded certificate with the `server.ssl.keystore.path` property in `kibana.yml`:

[source,text]
----
# SSL for outgoing requests from the Kibana Server (PKCS#12 formatted)
server.ssl.enabled: true
server.ssl.keystore.path: /path/to/your/server.p12
----

If you are using X-Pack Security or a proxy that provides an HTTPS endpoint for Elasticsearch,
you can configure Kibana to access Elasticsearch via HTTPS so communications between
the Kibana server and Elasticsearch are encrypted.
Expand Down
9 changes: 1 addition & 8 deletions docs/setup/settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,8 @@ To send *no* client-side headers, set this value to [] (an empty list).
`elasticsearch.requestTimeout:`:: *Default: 30000* Time in milliseconds to wait for responses from the back end or
Elasticsearch. This value must be a positive integer.
`elasticsearch.shardTimeout:`:: *Default: 30000* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable.
`elasticsearch.ssl.keystore.path`:: Optional setting that provides the path to the PKCS#12-format SSL Certificate and Key file. This file is used to verify the identity of Kibana
to Elasticsearch. Either this, or `elasticsearch.ssl.certificate`/`elasticsearch.ssl.key` pair is required when `xpack.ssl.verification_mode` in Elasticsearch is set to either
`certificate` or `full`. Specifying both `elasticsearch.ssl.keystore.path` and `elasticsearch.ssl.certificate` is not allowed.
`elasticsearch.ssl.certificate:` and `elasticsearch.ssl.key:`:: Optional settings that provide the paths to the PEM-format SSL
certificate and key files. These files are used to verify the identity of Kibana to Elasticsearch.
Either this, or `elasticsearch.ssl.keystore.path` is required when `xpack.ssl.verification_mode` in Elasticsearch is set to either `certificate` or `full`.
Specifying both `elasticsearch.ssl.certificate` and `elasticsearch.ssl.keystore.path` is not allowed.
certificate and key files. These files are used to verify the identity of Kibana to Elasticsearch and are required when `xpack.ssl.verification_mode` in Elasticsearch is set to either `certificate` or `full`.
`elasticsearch.ssl.certificateAuthorities:`:: Optional setting that enables you to specify a list of paths to the PEM file for the certificate
authority for your Elasticsearch instance.
`elasticsearch.ssl.keyPassphrase:`:: The passphrase that will be used to decrypt the private key. This value is optional as the key may not be encrypted.
Expand Down Expand Up @@ -102,8 +97,6 @@ By turning this off, only the layers that are configured here will be included.
`server.port:`:: *Default: 5601* Kibana is served by a back end server. This setting specifies the port to use.
`server.ssl.enabled:`:: *Default: "false"* Enables SSL for outgoing requests from the Kibana server to the browser. When set to `true`, `server.ssl.certificate` and `server.ssl.key` are required
`server.ssl.certificate:` and `server.ssl.key:`:: Paths to the PEM-format SSL certificate and SSL key files, respectively.
`server.ssl.keystore.path`:: Path to the PKCS#12 encoded SSL certificate and key. This is an alternative to setting `server.ssl.certificate` and `server.ssl.key`.
`server.ssl.keystore.password`:: The password that will be used to decrypt the private key within the keystore. This value is optional as the key may not be encrypted.
`server.ssl.certificateAuthorities:`:: List of paths to PEM encoded certificate files that should be trusted.
`server.ssl.cipherSuites:`:: *Default: ECDHE-RSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-GCM-SHA384, DHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, DHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-SHA384, DHE-RSA-AES256-SHA384, ECDHE-RSA-AES256-SHA256, DHE-RSA-AES256-SHA256, HIGH,!aNULL, !eNULL, !EXPORT, !DES, !RC4, !MD5, !PSK, !SRP, !CAMELLIA*. Details on the format, and the valid options, are available via the [OpenSSL cipher list format documentation](https://www.openssl.org/docs/man1.0.2/apps/ciphers.html#CIPHER-LIST-FORMAT)
`server.ssl.keyPassphrase:`:: The passphrase that will be used to decrypt the private key. This value is optional as the key may not be encrypted.
Expand Down
27 changes: 6 additions & 21 deletions src/cli/cluster/base_path_proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,13 @@ export default class BasePathProxy {

const sslEnabled = config.get('server.ssl.enabled');
if (sslEnabled) {
const agentOptions = {
ca: map(config.get('server.ssl.certificateAuthorities'), (certAuthority) => readFileSync(certAuthority)),
this.proxyAgent = new HttpsAgent({
key: readFileSync(config.get('server.ssl.key')),
passphrase: config.get('server.ssl.keyPassphrase'),
cert: readFileSync(config.get('server.ssl.certificate')),
ca: map(config.get('server.ssl.certificateAuthorities'), readFileSync),
rejectUnauthorized: false
};

const keystoreConfig = config.get('server.ssl.keystore.path');
const pemConfig = config.get('server.ssl.certificate');

if (keystoreConfig && pemConfig) {
throw new Error(`Invalid Configuration: please specify either "server.ssl.keystore.path" or "server.ssl.certificate", not both.`);
}

if (keystoreConfig) {
agentOptions.pfx = readFileSync(keystoreConfig);
agentOptions.passphrase = config.get('server.ssl.keystore.password');
} else {
agentOptions.key = readFileSync(config.get('server.ssl.key'));
agentOptions.cert = readFileSync(pemConfig);
agentOptions.passphrase = config.get('server.ssl.keyPassphrase');
}

this.proxyAgent = new HttpsAgent(agentOptions);
});
}

if (!this.basePath) {
Expand Down
17 changes: 0 additions & 17 deletions src/cli/cluster/base_path_proxy.test.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/cli/serve/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function readServerSettings(opts, extraCliOptions) {
set('server.ssl.enabled', true);
}

if (opts.ssl && !has('server.ssl.keystore.path') && !has('server.ssl.certificate') && !has('server.ssl.key')) {
if (opts.ssl && !has('server.ssl.certificate') && !has('server.ssl.key')) {
set('server.ssl.certificate', DEV_SSL_CERT_PATH);
set('server.ssl.key', DEV_SSL_KEY_PATH);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ const createAgent = (server) => {
}

// Add client certificate and key if required by elasticsearch
if (config.get('elasticsearch.ssl.keystore.path')) {
agentOptions.pfx = readFile(config.get('elasticsearch.ssl.keystore.path'));
agentOptions.passphrase = config.get('elasticsearch.ssl.keystore.password');
} else if (config.get('elasticsearch.ssl.certificate') && config.get('elasticsearch.ssl.key')) {
if (config.get('elasticsearch.ssl.certificate') && config.get('elasticsearch.ssl.key')) {
agentOptions.cert = readFile(config.get('elasticsearch.ssl.certificate'));
agentOptions.key = readFile(config.get('elasticsearch.ssl.key'));
agentOptions.passphrase = config.get('elasticsearch.ssl.keyPassphrase');
Expand Down
52 changes: 23 additions & 29 deletions src/core_plugins/elasticsearch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,33 @@ export default function (kibana) {
return new kibana.Plugin({
require: ['kibana'],
config(Joi) {
const sslSchema = Joi.object({
verificationMode: Joi.string().valid('none', 'certificate', 'full').default('full'),
certificateAuthorities: Joi.array().single().items(Joi.string()),
certificate: Joi.string(),
key: Joi.when('certificate', {
is: Joi.exist(),
then: Joi.string().required(),
otherwise: Joi.string().forbidden()
}),
keystore: Joi.object({
path: Joi.string(),
password: Joi.string()
}).default(),
keyPassphrase: Joi.string()
const { array, boolean, number, object, string, ref } = Joi;

const sslSchema = object({
verificationMode: string().valid('none', 'certificate', 'full').default('full'),
certificateAuthorities: array().single().items(string()),
certificate: string(),
key: string(),
keyPassphrase: string()
}).default();

return Joi.object({
enabled: Joi.boolean().default(true),
url: Joi.string().uri({ scheme: ['http', 'https'] }).default('http://localhost:9200'),
preserveHost: Joi.boolean().default(true),
username: Joi.string(),
password: Joi.string(),
shardTimeout: Joi.number().default(30000),
requestTimeout: Joi.number().default(30000),
requestHeadersWhitelist: Joi.array().items().single().default(DEFAULT_REQUEST_HEADERS),
customHeaders: Joi.object().default({}),
pingTimeout: Joi.number().default(Joi.ref('requestTimeout')),
startupTimeout: Joi.number().default(5000),
logQueries: Joi.boolean().default(false),
return object({
enabled: boolean().default(true),
url: string().uri({ scheme: ['http', 'https'] }).default('http://localhost:9200'),
preserveHost: boolean().default(true),
username: string(),
password: string(),
shardTimeout: number().default(30000),
requestTimeout: number().default(30000),
requestHeadersWhitelist: array().items().single().default(DEFAULT_REQUEST_HEADERS),
customHeaders: object().default({}),
pingTimeout: number().default(ref('requestTimeout')),
startupTimeout: number().default(5000),
logQueries: boolean().default(false),
ssl: sslSchema,
apiVersion: Joi.string().default('master'),
healthCheck: Joi.object({
delay: Joi.number().default(2500)
healthCheck: object({
delay: number().default(2500)
}).default(),
}).default();
},
Expand Down

This file was deleted.

22 changes: 1 addition & 21 deletions src/core_plugins/elasticsearch/lib/__tests__/parse_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ describe('plugins/elasticsearch', function () {
serverConfig = {
url: 'https://localhost:9200',
ssl: {
verificationMode: 'full',
keystore: {}
verificationMode: 'full'
}
};
});
Expand Down Expand Up @@ -87,25 +86,6 @@ describe('plugins/elasticsearch', function () {
const config = parseConfig(serverConfig);
expect(config.ssl.passphrase).to.be('secret');
});

it(`sets pfx when a PKCS#12 certificate bundle is specified`, function () {
serverConfig.ssl.keystore.path = __dirname + '/fixtures/cert.pfx';
serverConfig.ssl.keystore.password = 'secret';

const config = parseConfig(serverConfig);
expect(Buffer.isBuffer(config.ssl.pfx)).to.be(true);
expect(config.ssl.pfx.toString('utf-8')).to.be('test pfx\n');
expect(config.ssl.passphrase).to.be('secret');
});

it('throws an error when both pfx and certificate are specified', function () {
serverConfig.ssl.certificate = __dirname + '/fixtures/cert.crt';
serverConfig.ssl.keystore.path = __dirname + '/fixtures/cert.pfx';

expect(() => parseConfig(serverConfig)).to.throwError(
`Invalid Configuration: please specify either "elasticsearch.ssl.keystore.path" or "elasticsearch.ssl.certificate", not both.`
);
});
});
});
});
17 changes: 2 additions & 15 deletions src/core_plugins/elasticsearch/lib/parse_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { readFileSync } from 'fs';
import Bluebird from 'bluebird';

const readFile = (file) => readFileSync(file, 'utf8');
const readBinaryFile = (file) => readFileSync(file);

export function parseConfig(serverConfig = {}) {
const config = {
Expand Down Expand Up @@ -57,20 +56,8 @@ export function parseConfig(serverConfig = {}) {
}

// Add client certificate and key if required by elasticsearch
const keystoreConfig = get(serverConfig, 'ssl.keystore.path');
const pemConfig = get(serverConfig, 'ssl.certificate');

if (keystoreConfig && pemConfig) {
throw new Error(
`Invalid Configuration: please specify either "elasticsearch.ssl.keystore.path" or "elasticsearch.ssl.certificate", not both.`
);
}

if (keystoreConfig) {
config.ssl.pfx = readBinaryFile(keystoreConfig);
config.ssl.passphrase = get(serverConfig, 'ssl.keystore.password');
} else if (pemConfig && get(serverConfig, 'ssl.key')) {
config.ssl.cert = readFile(pemConfig);
if (get(serverConfig, 'ssl.certificate') && get(serverConfig, 'ssl.key')) {
config.ssl.cert = readFile(serverConfig.ssl.certificate);
config.ssl.key = readFile(serverConfig.ssl.key);
config.ssl.passphrase = serverConfig.ssl.keyPassphrase;
}
Expand Down
1 change: 0 additions & 1 deletion src/server/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ export class Config {
const child = schema._inner.children[i];
// If the child is an object recurse through it's children and return
// true if there's a match

if (child.schema._type === 'object') {
if (has(key, child.schema, path.concat([child.key]))) return true;
// if the child matches, return true
Expand Down
36 changes: 16 additions & 20 deletions src/server/config/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,6 @@ import os from 'os';
import { fromRoot } from '../../utils';
import { getData } from '../path';

const sslSchema = Joi.object({
enabled: Joi.boolean().optional().default(false),
redirectHttpFromPort: Joi.number().default(),
keystore: Joi.object({
path: Joi.string(),
password: Joi.string()
}).default(),
certificate: Joi.string(),
key: Joi.when('certificate', {
is: Joi.exist(),
then: Joi.string().required(),
otherwise: Joi.string().forbidden()
}),
keyPassphrase: Joi.string(),
certificateAuthorities: Joi.array().single().items(Joi.string()).default([]),
supportedProtocols: Joi.array().items(Joi.string().valid('TLSv1', 'TLSv1.1', 'TLSv1.2')).default([]),
cipherSuites: Joi.array().items(Joi.string()).default(cryptoConstants.defaultCoreCipherList.split(':'))
});

export default () => Joi.object({
pkg: Joi.object({
version: Joi.string().default(Joi.ref('$version')),
Expand Down Expand Up @@ -78,7 +59,22 @@ export default () => Joi.object({
otherwise: Joi.default(false),
}),
customResponseHeaders: Joi.object().unknown(true).default({}),
ssl: sslSchema.default(),
ssl: Joi.object({
enabled: Joi.boolean().default(false),
redirectHttpFromPort: Joi.number(),
certificate: Joi.string().when('enabled', {
is: true,
then: Joi.required(),
}),
key: Joi.string().when('enabled', {
is: true,
then: Joi.required()
}),
keyPassphrase: Joi.string(),
certificateAuthorities: Joi.array().single().items(Joi.string()).default([]),
supportedProtocols: Joi.array().items(Joi.string().valid('TLSv1', 'TLSv1.1', 'TLSv1.2')),
cipherSuites: Joi.array().items(Joi.string()).default(cryptoConstants.defaultCoreCipherList.split(':'))
}).default(),
cors: Joi.when('$dev', {
is: true,
then: Joi.object().default({
Expand Down
40 changes: 8 additions & 32 deletions src/server/config/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,58 +119,34 @@ describe('Config schema', function () {
const { error } = validate(config);
expect(error).toBe(null);
});
});

describe('key', function () {
it('isn\'t required when ssl isn\'t enabled', function () {
const config = {};
set(config, 'server.ssl.enabled', false);
const { error } = validate(config);
expect(error).toBe(null);
});

it('is required when ssl is enabled', function () {
const config = {};
set(config, 'server.ssl.enabled', true);
set(config, 'server.ssl.certificate', '/path.cert');
set(config, 'server.ssl.key', '/path.key');
const { error } = validate(config);
expect(error).toBeInstanceOf(Object);
expect(error).toHaveProperty('details');
expect(error.details[0]).toHaveProperty('path', 'server.ssl.key');
expect(error.details[0]).toHaveProperty('path', 'server.ssl.certificate');
});
});

describe('keystore.path', function () {
it('isn\'t required when ssl isn\'t enabled', function () {
const config = {};
set(config, 'server.ssl.enabled', false);
const { error } = validate(config);
expect(error).toBe(null);
});

it('is allowed when ssl is enabled, and a certificate is not specified', function () {
const config = {};
set(config, 'server.ssl.enabled', true);
set(config, 'server.ssl.keystore.path', '/path.p12');
const { error } = validate(config);
expect(error).toBe(null);
});
});

describe('keystore.password', function () {
describe('key', function () {
it('isn\'t required when ssl isn\'t enabled', function () {
const config = {};
set(config, 'server.ssl.enabled', false);
const { error } = validate(config);
expect(error).toBe(null);
});

it('is allowed when ssl is enabled, and a certificate is not specified', function () {
it('is required when ssl is enabled', function () {
const config = {};
set(config, 'server.ssl.enabled', true);
set(config, 'server.ssl.keystore.password', 'secret');
set(config, 'server.ssl.certificate', '/path.cert');
const { error } = validate(config);
expect(error).toBe(null);
expect(error).toBeInstanceOf(Object);
expect(error).toHaveProperty('details');
expect(error.details[0]).toHaveProperty('path', 'server.ssl.key');
});
});

Expand Down
8 changes: 2 additions & 6 deletions src/server/config/transform_deprecations.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@ const serverSslEnabled = (settings, log) => {
const has = partial(_.has, settings);
const set = partial(_.set, settings);

const hasPkcs12Cert = has('server.ssl.keystore.path');
const hasPemCert = has('server.ssl.certificate') && has('server.ssl.key');

if (!has('server.ssl.enabled') && (hasPkcs12Cert || hasPemCert)) {
if (!has('server.ssl.enabled') && has('server.ssl.certificate') && has('server.ssl.key')) {
set('server.ssl.enabled', true);
log('Enabling ssl by only specifying server.ssl.keystore.path or server.ssl.certificate/key is deprecated. '
+ 'Please set server.ssl.enabled to true');
log('Enabling ssl by only specifying server.ssl.certificate and server.ssl.key is deprecated. Please set server.ssl.enabled to true');
}
};

Expand Down
Loading

0 comments on commit 97df91f

Please sign in to comment.