Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for certificates in PKCS#12 (P12) key stores #53810

Merged

Conversation

jportner
Copy link
Contributor

@jportner jportner commented Dec 26, 2019

Summary

This PR allows Kibana to read certificates and keys from P12 key stores (and trust stores). It adds the following configuration options:

  • elasticsearch.ssl.keystore.path
  • elasticsearch.ssl.keystore.password
  • elasticsearch.ssl.truststore.path
  • elasticsearch.ssl.truststore.password
  • server.ssl.keystore.path
  • server.ssl.keystore.password
  • server.ssl.truststore.path
  • server.ssl.truststore.password

Details

I implemented this feature by using node-forge to parse each P12 key store and convert the keys/certs to PEM format. This means that all clients that currently rely on these files do not need any changes made -- at the end of the day, they will still use PEM-formatted keys/certs.

I regenerated the existing certificates in Kibana dev utils, adding some extra ones that weren't included before. Each P12 key store was generated using elasticsearch-certutil, and I extracted PEM certificates / keys from those key stores. The PEM and P12 formats can be used interchangeably (e.g., PEM CA and P12 end-entity key store). I chose not to include the CA private key; if anyone experimenting with Kibana chooses to add that CA to their system's trusted root certificates, it would be trivial to MITM TLS connections for that system. If these certificates need to be regenerated in the future, that can be done using the new instructions detailed in packages/kbn-dev-utils/certs/README.md.

In creating this PR, I made a few design decisions:

  • I created a new "crypto" utility (src/core/utils/crypto) to handle PKCS12 read operations. I'm not opposed to moving this somewhere else, but this seemed to be the most logical place to put this code.
  • I changed existing configuration code to read keys/certs, instead of relying on consumers to read those files. This allows us to do additional validation, receive more helpful errors for related failures, reduce filesystem I/O a bit, and avoid an unlikely scenario where keys/certs could be switched out during runtime.
  • I cleaned up and refactored several tests, removed unnecessary fixtures, and changed some tests to rely on certs/keys provided by kbn-dev-utils.
  • The Kibana HTTP servers and Elasticsearch clients will be able to use certificate authorities from any or all of the following: P12 key store (that includes an instance key/cert along with CA chain), P12 trust store (that only includes the CA chain), and PEM file/s. This provides a lot of flexibility to users so they won't be forced to try to convert any existing keys/certs that they might want to use.

NOTE: It will be much easier to review this PR commit by commit.

Resolves: #17039


"Release note: Kibana now supports the usage of PKCS#12 (P12) key stores and trust stores for certificates and keys."

@jportner jportner added v7.6.0 v8.0.0 release_note:enhancement Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! labels Dec 26, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-security (Team:Security)

@jportner jportner force-pushed the issue-17039-support-p12-certificates branch 2 times, most recently from 160111e to 4dc7117 Compare December 26, 2019 22:01
Copy link
Contributor Author

@jportner jportner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Author's notes for reviewers

Comment on lines +25 to +35
export const ES_P12_PATH = resolve(__dirname, '../certs/elasticsearch.p12');
export const ES_P12_PASSWORD = 'storepass';
export const ES_EMPTYPASSWORD_P12_PATH = resolve(
__dirname,
'../certs/elasticsearch_emptypassword.p12'
);
export const ES_NOPASSWORD_P12_PATH = resolve(__dirname, '../certs/elasticsearch_nopassword.p12');
export const KBN_KEY_PATH = resolve(__dirname, '../certs/kibana.key');
export const KBN_CERT_PATH = resolve(__dirname, '../certs/kibana.crt');
export const KBN_P12_PATH = resolve(__dirname, '../certs/kibana.p12');
export const KBN_P12_PASSWORD = 'storepass';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a P12 key store that contains the Elasticsearch key / cert / CA cert. I also added other certificates and key stores so we have one place where keys / certs reside for testing purposes. Other unit tests and functional tests now reference these.

Comment on lines -264 to +266
esArgs.push(`xpack.security.http.ssl.key=${ES_KEY_PATH}`);
esArgs.push(`xpack.security.http.ssl.certificate=${ES_CERT_PATH}`);
esArgs.push(`xpack.security.http.ssl.certificate_authorities=${CA_CERT_PATH}`);
esArgs.push(`xpack.security.http.ssl.keystore.path=${ES_P12_PATH}`);
esArgs.push(`xpack.security.http.ssl.keystore.type=PKCS12`);
esArgs.push(`xpack.security.http.ssl.keystore.password=${ES_P12_PASSWORD}`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change isn't strictly necessary, but it seemed appropriate to prefer the P12 key store since that is the default output of elasticsearch-certutil.

Comment on lines +86 to +88
certificateAuthorities: ['content-of-ca-path-1', 'content-of-ca-path-2'],
certificate: 'content-of-certificate-path',
key: 'content-of-key-path',
Copy link
Contributor Author

@jportner jportner Dec 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ElasticsearchClientConfig no longer reads the contents of these files -- ElasticsearchConfig handles that. So we don't need to mock the readFileSync implementation here and we can assume that the contents of these certs/keys are being passed in to the ElasticsearchClientConfig params.


jest.mock('fs', () => ({
readFileSync: jest.fn(),
}));

import { readFileSync } from 'fs';
Copy link
Contributor Author

@jportner jportner Dec 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old behavior of this unit test resulted in the creation of an HttpServer with SSL enabled, but an undefined key and certificate. Technically this worked without errors, but it seemed that this wasn't the original intent of this test. So, I changed this test to use an actual key/cert instead.

9WQ73QKBgQDAYZzplO4TPoPK9AnxoW/HpSwGEO7Fb8fLyPg94CvHn4QBCFJUKuTn
hBp/TJgF6CjQWQMr2FKVFF33Ow7+Qa96YGvmYlEjR/71D4Rlprj5JJpuO154DI3I
YIMNTjvwEQEI+YamMarKsz0Kq+I1EYSAf6bQ4H2PgxDxwTXaLkl0RA==
-----END RSA PRIVATE KEY-----
Copy link
Contributor Author

@jportner jportner Dec 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This key was a copy of the old CA key used in kbn-dev-utils. I regenerated the P12 key stores in this fixtures directory, using the new CA contained in kbn-dev-utils, but I decided not to include the new CA key in the repository. This key is outdated and not needed.

@elastic elastic deleted a comment from kibanamachine Dec 26, 2019
@jportner jportner force-pushed the issue-17039-support-p12-certificates branch from 4dc7117 to 0ff505d Compare December 27, 2019 17:42
Comment on lines +1 to 10
Bag Attributes
friendlyName: elasticsearch
localKeyID: 54 69 6D 65 20 31 35 37 37 34 36 36 31 39 38 30 33 37
Key Attributes: <No Attributes>
Bag Attributes
friendlyName: ca
2.16.840.1.113894.746875.1.1: <Unsupported tag 6>
subject=/CN=Elastic Certificate Tool Autogenerated CA
issuer=/CN=Elastic Certificate Tool Autogenerated CA
-----BEGIN CERTIFICATE-----
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These bag attributes are included because the certificate was generated as part of a P12 key store, then exported from that key store using OpenSSL. Any code that reads a PEM-formatted certificate or key will ignore this.

@jportner jportner force-pushed the issue-17039-support-p12-certificates branch 3 times, most recently from e8ec4f9 to 0593259 Compare December 30, 2019 16:45
Comment on lines -57 to +58
`--server.ssl.key=${ES_KEY_PATH}`,
`--server.ssl.certificate=${ES_CERT_PATH}`,
`--server.ssl.key=${KBN_KEY_PATH}`,
`--server.ssl.certificate=${KBN_CERT_PATH}`,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kbn/dev-utils now provides a separate certificate/key for Kibana, switched to those to avoid confusion.

@jportner jportner force-pushed the issue-17039-support-p12-certificates branch from 0593259 to b3ec790 Compare December 30, 2019 18:41
@kibanamachine
Copy link
Contributor

💚 Build Succeeded

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

Added Kibana certificate/keypair, and also added P12 formats for
both Kibana and Elasticsearch.
Removed obsolete dev certs/keys, and changed files and tests
where appropriate.
Not positive that this code should remain here, open to
suggestions.
The Kibana Platform supports configuration deprecations now, so we
can remove the custom deprecation logging that we had previously.
Logger was previously optional to avoid changing tests. Now it is
mandatory and the tests have been changed to mock the logger.
This test started failing until I accepted these changes.
These never should have been grouped together. Split them out to
improve maintainability and reduce clutter.
This includes:
* HTTPS server
* HTTPS redirect server
* BasePath proxy server
@jportner jportner force-pushed the issue-17039-support-p12-certificates branch from 7afb594 to c4d5163 Compare December 30, 2019 20:50
docs/settings/security-settings.asciidoc Show resolved Hide resolved
packages/kbn-dev-utils/certs/README.md Show resolved Hide resolved
src/cli/serve/serve.js Show resolved Hide resolved
src/cli/serve/serve.js Show resolved Hide resolved
src/core/server/elasticsearch/elasticsearch_config.ts Outdated Show resolved Hide resolved
src/core/utils/crypto/pkcs12.test.ts Show resolved Hide resolved
src/core/utils/crypto/pkcs12.test.ts Outdated Show resolved Hide resolved
src/core/server/http/ssl_config.test.ts Outdated Show resolved Hide resolved
docs/setup/settings.asciidoc Outdated Show resolved Hide resolved
docs/setup/settings.asciidoc Outdated Show resolved Hide resolved
Also changed readPkcs12Keystore to determine which end-entity key
and cert to return based on if they have a matching public key. The
old behavior would simply return the first key and cert as the EE
key and cert.
Copy link
Contributor

@pgayvallet pgayvallet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for platform changes

src/core/server/elasticsearch/elasticsearch_config.ts Outdated Show resolved Hide resolved
@jportner
Copy link
Contributor Author

jportner commented Jan 6, 2020

@kobelb ready for re-review!

Copy link
Contributor

@kobelb kobelb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking and working great! As discussed on Slack, the only configuration that we'll want to manually verify works properly is a CA that uses an intermediate certificate

src/core/utils/crypto/pkcs12.ts Outdated Show resolved Hide resolved
src/core/utils/crypto/pkcs12.ts Outdated Show resolved Hide resolved
src/core/utils/crypto/pkcs12.ts Outdated Show resolved Hide resolved
src/core/utils/crypto/pkcs12.test.ts Show resolved Hide resolved
Copy link
Contributor

@kobelb kobelb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on green CI

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@jportner jportner merged commit 719ff25 into elastic:master Jan 9, 2020
jportner added a commit to jportner/kibana that referenced this pull request Jan 9, 2020
Kibana now supports the usage of PKCS#12 (P12) key stores and trust stores for certificates and keys.
jportner added a commit that referenced this pull request Jan 9, 2020
…4383)

Kibana now supports the usage of PKCS#12 (P12) key stores and trust stores for certificates and keys.
jportner added a commit to jportner/kibana that referenced this pull request Jan 11, 2020
The Elasticsearch config should error out if a PKCS12 keystore
does not contain a key *or* a certificate. This was intended to be
the functionality in PR elastic#53810, but it was overlooked. Changing it
now since this PR is changing code in the same file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backported release_note:enhancement Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! v7.6.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PKCS #12 Certificates
8 participants