forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Agent] Allow CA cert pinning on the Elasticsearch output or any code…
… that user tlscommon.TLSConfig builder. (elastic#16019) * Add a sha256 pin for the CA Certificate When multiples CA are presents on the system we cannot ensure that a specific one was used to validates the chains exposer by the server. This PRs adds a `ca_sha256` option to the `tlscommon.TLSConfig` that is used by all the code that has to create a TCP client with TLS support. When the option is set, it will hook a new callback in the validation chains that will inspect the verified and validated chains by Go to ensure that a lets a certificate in the chains match the provided sha256. Usage example for the Elasticsearch output. ``` output.elasticsearch: hosts: [127.0.0.1:9200] ssl.ca_sha256: <base64_encoded_sha1> ``` You can generate the pin using the **openssl** binary with the following command: ``` openssl x509 -in ca.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 ``` OpenSSL's [documentation](https://www.openssl.org/docs/manmaster/man1/dgst.html) You will need to start Elasticsearch with the following options ```yaml xpack.security.enabled: true indices.id_field_data.enabled: true xpack.license.self_generated.type: trial xpack.security.http.ssl.enabled: true xpack.security.http.ssl.key: /etc/pki/localhost/localhost.key" xpack.security.http.ssl.certificate: /etc/pki/localhost/localhost.crt" xpack.security.http.ssl.certificate_authorities: /etc/pki/ca/ca.crt" ``` This pull request also include a new service in the docker-compose.yml that will start a new Elasticsearch server with TLS and security configured.
- Loading branch information
Showing
28 changed files
with
889 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Licensed to Elasticsearch B.V. under one or more contributor | ||
// license agreements. See the NOTICE file distributed with | ||
// this work for additional information regarding copyright | ||
// ownership. Elasticsearch B.V. 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. | ||
|
||
package tlscommon | ||
|
||
import ( | ||
"crypto/sha256" | ||
"crypto/x509" | ||
"encoding/base64" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// ErrCAPinMissmatch is returned when no pin is matched in the verified chain. | ||
var ErrCAPinMissmatch = errors.New("provided CA certificate pins doesn't match any of the certificate authorities used to validate the certificate") | ||
|
||
type pins []string | ||
|
||
func (p pins) Matches(candidate string) bool { | ||
for _, pin := range p { | ||
if pin == candidate { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// verifyPeerCertFunc is a callback defined on the tls.Config struct that will called when a | ||
// TLS connection is used. | ||
type verifyPeerCertFunc func([][]byte, [][]*x509.Certificate) error | ||
|
||
// MakeCAPinCallback loops through the verified chains and will try to match the certificates pin. | ||
// | ||
// NOTE: Defining a PIN to check certificates is not a replacement for the normal TLS validations it's | ||
// an additional validation. In fact if you set `InsecureSkipVerify` to true and a PIN, the | ||
// verifiedChains variable will be empty and the added validation will fail. | ||
func MakeCAPinCallback(hashes pins) func([][]byte, [][]*x509.Certificate) error { | ||
return func(_ [][]byte, verifiedChains [][]*x509.Certificate) error { | ||
// The chain of trust has been already established before the call to the VerifyPeerCertificate | ||
// function, after we go through the chain to make sure we have at least a certificate certificate | ||
// that match the provided pin. | ||
for _, chain := range verifiedChains { | ||
for _, certificate := range chain { | ||
h := Fingerprint(certificate) | ||
if hashes.Matches(h) { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
return ErrCAPinMissmatch | ||
} | ||
} | ||
|
||
// Fingerprint takes a certificate and create a hash of the DER encoded public key. | ||
func Fingerprint(certificate *x509.Certificate) string { | ||
hash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo) | ||
return base64.StdEncoding.EncodeToString(hash[:]) | ||
} |
Oops, something went wrong.