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

Cassandra: Refactor PEM parsing logic #11861

Merged
merged 21 commits into from Jun 21, 2021
Merged

Cassandra: Refactor PEM parsing logic #11861

merged 21 commits into from Jun 21, 2021

Conversation

pcman312
Copy link
Contributor

@pcman312 pcman312 commented Jun 14, 2021

Description

This fixes an issue in the Cassandra database plugin TLS logic where providing only a custom CA doesn't work when using pem_bundle and partially works with pem_json. This now supports setting either a client certificate & private key, a custom CA, or both.

How does this work?

The certutil.ParsePEMBundle function makes assumptions about the PEM data it is parsing:

  1. It assumes the PEM bundle will be used for mTLS and therefore assumes the presence of both a client certificate and a custom CA, though it doesn't error if only the CA is provided. If only a CA is provided to this function, the CA certificate ends up in the Certificate field rather than CAChain which ends up not being interpreted correctly when converting to a tls.Config.
  2. It assumes the PEM data is in a specific order (client certificate before CA chain)

A new function has been written (pemBundleToTLSConfig) that parses the PEM bundle very similarly to certutil.ParsePEMBundle but does not make either of those assumptions.

Similarly, the certutil.ParsePKIJSON function ends up having some similar assumptions, however it does parse the CA correctly and thus the tls.Config works when connecting to Cassandra. However, the CA is also included as a client certificate when it shouldn't be. However, it appears that Cassandra will ignore a provided client certificate if not configured to use mTLS.

A new function was created (jsonBundleToTLSConfig) that parses the certificate data into a tls.Config object.

Testing

Additional automated tests were added in connection_producer_test.go that start up a Cassandra instance using TLS with a custom CA and connects to it using the same custom CA.

Also this was tested with a real Cassandra instance (also configured with a custom CA) and a real Vault instance configured to use just the custom CA when talking to Cassandra.

The ParsePEMBundle and ParsePKIJSON functions in the certutil package assumes
both a client certificate and a custom CA are specified. Cassandra needs to
allow for either a client certificate, a custom CA, or both. This revamps the
parsing of pem_json and pem_bundle to accomodate for any of these configurations
@pcman312 pcman312 added bug Used to indicate a potential bug secret/database/cassandra labels Jun 14, 2021
@pcman312 pcman312 marked this pull request as ready for review June 14, 2021 22:25
@pcman312 pcman312 requested a review from a team June 14, 2021 22:25
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 15, 2021 17:16 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 15, 2021 17:16 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 15, 2021 21:43 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 15, 2021 21:43 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 15, 2021 22:04 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 15, 2021 22:04 Inactive
helper/testhelpers/docker/testhelpers.go Outdated Show resolved Hide resolved
plugins/database/cassandra/cassandra_test.go Show resolved Hide resolved
plugins/database/cassandra/tls.go Outdated Show resolved Hide resolved
plugins/database/cassandra/tls.go Outdated Show resolved Hide resolved
plugins/database/cassandra/tls.go Show resolved Hide resolved
@@ -83,38 +82,46 @@ func (c *cassandraConnectionProducer) Initialize(ctx context.Context, req dbplug
return fmt.Errorf("username cannot be empty")
case len(c.Password) == 0:
return fmt.Errorf("password cannot be empty")
case len(c.PemJSON) > 0 && len(c.PemBundle) > 0:
return fmt.Errorf("cannot specify both pem_json and pem_bundle")
Copy link
Member

Choose a reason for hiding this comment

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

I think that this is good to do, but note that we previously allowed both to be specified at the same time, with preference on pem_json so it's technically a breaking change. Would be good to get the docs updated as well to indicate that this is a one of between the two parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, this is a breaking change. I'm not a fan of allowing for both as it can create confusion ("which one is being used?"). I'll update the docs to describe this change unless anyone has any objections to making this change.

caChain := []string{}

count := 0
for len(pemBytes) > 0 {
Copy link
Member

@calvn calvn Jun 15, 2021

Choose a reason for hiding this comment

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

Does the ordering while parsing matter? For example, should we check/ensure that there's no private key exists in the middle of a chain?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as I can tell there is no required ordering down stream. The current ParsePEMBundle function also doesn't enforce any ordering (other than the implicit assertion that the client certificate is before the CA cert). We can enforce ordering if we want, but the current implementation doesn't require it.

Copy link
Member

Choose a reason for hiding this comment

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

I noticed that ParsePEMBundle performs validity checks around the client certs and the CA chain, via parsedBundle.Verify(). Wondering if we should do the same.

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 looked into the Verify function and it is specific to mTLS so we can't do the exact same validation. I'm suspicious that we don't need to do much of what it's doing in that function as it's probably handled downstream in the TLS code, but I'll try to double check that.

@vercel vercel bot temporarily deployed to Preview – vault-storybook June 16, 2021 20:59 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 16, 2021 20:59 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 16, 2021 21:37 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 16, 2021 21:37 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 16, 2021 21:52 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 17, 2021 19:29 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 17, 2021 20:04 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 17, 2021 20:04 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 17, 2021 20:27 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 17, 2021 20:27 Inactive
@vercel vercel bot temporarily deployed to Preview – vault June 17, 2021 20:40 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 17, 2021 20:40 Inactive
Copy link
Contributor

@jasonodonnell jasonodonnell left a comment

Choose a reason for hiding this comment

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

LGTM and works as expected!


- `pem_json` `(string: "")` – Specifies JSON containing a certificate and
private key; a certificate, private key, and issuing CA certificate; or just a
CA certificate. For convenience format is the same as the output of the
`issue` command from the `pki` secrets engine; see
[the pki documentation](/docs/secrets/pki).
[the pki documentation](/docs/secrets/pki). Only one of `pem_bundle` or `pem_json` can be specified.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have a note that the certs/key can't have newlines?

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've added a note that the PEM data must be properly JSON encoded.

where Vault will not parse the CA certificate correctly. To work around this, use `pem_json` with the
following structure: `{"ca_chain": ["-----BEGIN CERTIFICATE-----\nMIIEFjC...FNYakP7I\n-----END CERTIFICATE-----"]}`
Also make sure the PEM data is properly JSON encoded with `\n` instead of newlines.
certificate; or just a CA certificate. Only one of `pem_bundle` or `pem_json` can be specified.
Copy link
Member

Choose a reason for hiding this comment

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

Do you think there's value in providing a sample command for constructing this concatenated bundle to minimize the chances of a malformed bundle being provided?

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 lean towards no because such a command is going to be fairly specific to my development environment.

Copy link
Member

@calvn calvn left a comment

Choose a reason for hiding this comment

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

Minor comment and a reminder to add a changelog file, but otherwise looks good!

@vercel vercel bot temporarily deployed to Preview – vault June 21, 2021 16:39 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook June 21, 2021 16:39 Inactive
@pcman312 pcman312 merged commit 43ccb63 into main Jun 21, 2021
@pcman312 pcman312 deleted the cassandra-tls-custom-ca branch June 21, 2021 17:38
pcman312 added a commit that referenced this pull request Jun 22, 2021
* Refactor TLS parsing

The ParsePEMBundle and ParsePKIJSON functions in the certutil package assumes
both a client certificate and a custom CA are specified. Cassandra needs to
allow for either a client certificate, a custom CA, or both. This revamps the
parsing of pem_json and pem_bundle to accomodate for any of these configurations
pcman312 added a commit that referenced this pull request Jun 23, 2021
* Refactor TLS parsing

The ParsePEMBundle and ParsePKIJSON functions in the certutil package assumes
both a client certificate and a custom CA are specified. Cassandra needs to
allow for either a client certificate, a custom CA, or both. This revamps the
parsing of pem_json and pem_bundle to accomodate for any of these configurations
jartek pushed a commit to jartek/vault that referenced this pull request Sep 11, 2021
* Refactor TLS parsing

The ParsePEMBundle and ParsePKIJSON functions in the certutil package assumes
both a client certificate and a custom CA are specified. Cassandra needs to
allow for either a client certificate, a custom CA, or both. This revamps the
parsing of pem_json and pem_bundle to accomodate for any of these configurations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Used to indicate a potential bug secret/database/cassandra
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants