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

Hashicorp Vault KMS implementation and kubernetes-example #749

Merged
merged 18 commits into from
Dec 7, 2023

Conversation

k-wall
Copy link
Contributor

@k-wall k-wall commented Nov 27, 2023

Type of change

Select the type of your PR

  • Enhancement / new feature

Description

  • Implementation of the KMS interface for HasiCorp Vault, with support integration tests.
  • kubernetes-examples/topicencryption ready to illustrate the topic encryption. It deploys Hashicorp using the Helm Chart in a mode suitable for development (i.e. auto-unsealed).

Additional Context

Why are you making this pull request?

Checklist

Please go through this checklist and make sure all applicable tasks have been done

  • Write tests
  • Make sure all tests pass
  • Review performance test results. Ensure that any degradations to performance numbers are understood and justified.
  • Make sure all Sonar-Lint warnings are addressed or are justifiably ignored.
  • Update documentation
  • Reference relevant issue(s) and close them after merging
  • For user facing changes, update CHANGELOG.md (remember to include changes affecting the API of the test artefacts too).

@k-wall k-wall force-pushed the env-enc-kw2 branch 2 times, most recently from 16f7a18 to 505c780 Compare November 27, 2023 17:57
@k-wall k-wall changed the title kubernetes-examples/topicencryption example + growing the Hashicorp KMS kubernetes-examples/topicencryption example + growing the Hashicorp KMS impl Nov 27, 2023
@k-wall k-wall force-pushed the env-enc-kw2 branch 3 times, most recently from 137c735 to 2ebe5ac Compare November 28, 2023 17:31
@k-wall
Copy link
Contributor Author

k-wall commented Nov 28, 2023

There's a demo of this working on Kube https://asciinema.org/a/623785

Copy link
Contributor

@tombentley tombentley left a comment

Choose a reason for hiding this comment

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

Thanks Keith!

Comment on lines 13 to 14
int numAuthBits,
byte[] iv,
Copy link
Contributor

Choose a reason for hiding this comment

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

AFAICS, Vault does not include an immutable key id in the edek itself, but I'm inclined to include the key name here anyway. That's because I think it's better to push the bookkeeping to keep the kek id and edek together into the KMSes. AWS does this anyway, so why not make use of that? And for those KMSes which do provide an immutable key id alongside mutable key aliases it makes for a stronger contract for our Kms abstraction to force the Kms implementation to resolve it up front.

Coming back to Vault... it seems the only kind of key identifier it supports is a name. It also supports a delete key operation. So name is not immutable in time and space, and therefore the following is possible:

  1. Create key foo.
  2. Generate data key bar.
  3. Use data key
  4. Delete foo
  5. You now cannot decrypt your data, but at least it's obvious why: You should get a 4xx on Vault's decrypt operation.
  6. Create a new foo.
  7. What happens if we try to decrypt the encrypted bar using foo now? Does it fail? Does the failure have an sensible error?

I think we need to:

  • document that this is a known behaviour when using vault. We simply have to rely on users not deleting keys.
  • document how it will appear to users at step 5 and step 7.

Another thing that vault lets you do is export and import keys. This means that the key with a given id could disappear for a time (export, delete, import). Or effectively get renamed (export, delete, import under a different name).

(One thing we could do to detect this situation is use the read-key operation to capture the keys dict. We expect the keys at encryption time to be a submap of the keys at decryption time. We could use that fact to be able to say with certainty "Your kek has been deleted and recreated". I don't think we actually want to do this though).

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 argee, the documentation is a good idea. I'll add it.

It looks like Vault associates a timestamp with each key. That would give an alternative way to detect the deleted/recreated case. I'll log the ideas I don't think we need to do them right away.

this is a key that's been rotated once

{
  "request_id": "5c5584fc-2f74-438b-7dfd-cc9f2eb66864",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": {
    "allow_plaintext_backup": false,
    "auto_rotate_period": 0,
    "deletion_allowed": false,
    "derived": false,
    "exportable": false,
    "imported_key": false,
    "keys": {
      "1": 1701799313,
      "2": 1701799318
    },
    "latest_version": 2,
    "min_available_version": 0,
    "min_decryption_version": 1,
    "min_encryption_version": 0,
    "name": "mykey",
    "supports_decryption": true,
    "supports_derivation": true,
    "supports_encryption": true,
    "supports_signing": false,
    "type": "aes256-gcm96"
  },
  "warnings": null
}

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 raised this as #784

@k-wall k-wall force-pushed the env-enc-kw2 branch 4 times, most recently from 08391b9 to 80305f0 Compare December 4, 2023 18:48
@k-wall k-wall marked this pull request as ready for review December 4, 2023 19:53
@k-wall
Copy link
Contributor Author

k-wall commented Dec 4, 2023

There's still some issues I want to address, which I'll get to tomorrow morning.
I also intend to open issues for the 'missing bits'. My intention is that we can get this merged soon and work together on the outstanding stuff that's a priority.

@k-wall k-wall linked an issue Dec 4, 2023 that may be closed by this pull request
Copy link
Member

@SamBarker SamBarker left a comment

Choose a reason for hiding this comment

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

LGTM thanks @k-wall

We should probably add a Readme (a skelaton at least in this PR). To document how to configure Kroxylicious for use with Vault.

kroxylicious-sample/sample-proxy-config.yml Outdated Show resolved Hide resolved
- name: kroxylicious
image: quay.io/kroxylicious/kroxylicious-developer:0.4.0-SNAPSHOT
imagePullPolicy: Always
args: ["--config", "/opt/kroxylicious/config/config.yaml"]
Copy link
Member

Choose a reason for hiding this comment

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

Seems like a sensible default path should we update the default (in a new PR)?

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 suppose we could update the Dockerfile to assume that path, but that would require users (Docker user or Kubernetes user) to mount the config file in the same place. I'm not sure that's too helpful.

Copy link
Contributor

@robobario robobario left a comment

Choose a reason for hiding this comment

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

LGTM!

kroxylicious-kms-provider-hashicorp-vault/pom.xml Outdated Show resolved Hide resolved

@Override
public void serialize(VaultEdek edek, @NonNull ByteBuffer buffer) {
var keyRefBuf = edek.kekRef().getBytes(StandardCharsets.UTF_8);
Copy link
Contributor

Choose a reason for hiding this comment

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

EDEKs get serialized in the encrypted record and may be deserialized by a different version of the Vault KMS provider. So I wonder if it's worth expending a byte on a version number, just so we've got wiggle room in the future to change the serialization schema.

You could also argue for a magic number to detect when we're trying to deserialize a EDEK that wasn't written by the vault KMS (e.g. some other KMS).

We don't have to decide either of these right now, but we should at least open an issue and have the discussion.

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 Javadoc to the VaultEdekSerde class and added a TODO to consider adding a version byte.

.POST(HttpRequest.BodyPublishers.noBody())
.build();

return vaultClient.sendAsync(request, statusHandler(kekRef, new JsonBodyHandler<VaultResponse<DataKeyData>>(new TypeReference<>() {
Copy link
Contributor

Choose a reason for hiding this comment

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

What thread does this sendAsync complete on? Because IIUC that details leaks to the caller of generateDekPair, who might need to volatile/synchronized etc to make their code threadsafe.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like the HTTP client by default would have been given a dedicated cached thread pool (another resource we should look at sharing across channels in future), so I imagine it will be completed from that pool, needs checking though.

The caller of generateDekPair is already working with a CompletionStage isn't the default assumption that any thread could be completing it and clients should code accordingly?

Copy link
Contributor

Choose a reason for hiding this comment

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

Could javadoc it and leave it up to the caller to handle switching to a different executor if required? Or I guess you could change the Kms apis so the client could optionally provide an executor they want the completion to happen on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What thread does this sendAsync complete on?

A HTTP pool thread will complete the future when the HTTP response is returned. IIUC that means that actions chained to that future will execute in that same thread, unless explicit provision is made. So in our case that means the thenApply action in InBandKeyManager#makeKeyContext will run on the HTTP thread. AFAICS that code looks thread safe (it uses only objects that are finals and returns a new created object via a completion stage).

If the apply in InBandKeyManager#makeKeyContext ever needed to rely on mutable state, then it would be up to it achieve thread safety. Using thenApplyAsync would be an option.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a sentence to the Javadoc of the Kms to say that the thread on which stages complete is undefined. That way at least the contract is explicit.

Copy link
Member

@SamBarker SamBarker left a comment

Choose a reason for hiding this comment

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

I'm happy to merge this and address the TODO comments in a separate PRs

Comment on lines 48 to 50
Serde.putUnsignedByte(buffer, keyRefBuf.length);
buffer.put(keyRefBuf);
buffer.put(edek.edek());
Copy link
Member

Choose a reason for hiding this comment

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

Interacting with the buffer both directly and through Serde feels a bit odd to me. I wonder if we should have Serde.putByteArray or Serder.copyArray

Copy link
Contributor Author

@k-wall k-wall Dec 7, 2023

Choose a reason for hiding this comment

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

That code now uses some Kafka Common utility method to do the unsigned varint work. I'm not sure that addresses your point. I suspect we'll want to refactor this further as we work on #767.
Hoping this is good enough for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Eventually we might want a separate module just for the Serde abstraction (which is not really KMS specific) and utility methods for interacting with ByteBuffer, but like Keith I'm inclined to defer that for now.

k-wall and others added 2 commits December 7, 2023 09:49
…xylicious/kms/provider/hashicorp/vault/VaultEdekSerde.java

Co-authored-by: Sam Barker <sam@quadrocket.co.uk>
Signed-off-by: Keith Wall <kwall@redhat.com>
Co-authored-by: Tom Bentley <tombentley@users.noreply.github.com>
Signed-off-by: Keith Wall <kwall@redhat.com>
@k-wall k-wall force-pushed the env-enc-kw2 branch 2 times, most recently from bdc91d1 to 1dec9d6 Compare December 7, 2023 15:36
@k-wall k-wall changed the title kubernetes-examples/topicencryption example + growing the Hashicorp KMS impl Hashicorp Vault KMS implementation and kubernetes-example Dec 7, 2023
@k-wall k-wall added this to the 0.4.0 milestone Dec 7, 2023
@k-wall
Copy link
Contributor Author

k-wall commented Dec 7, 2023

@tombentley @robobario @SamBarker I hope I've addressed the review comments to your satisfaction. In some cases where I think deferring would be appropriate, I've raise a separate issue.

I'm hoping this will be good enough to merge. Indeed, it you want to merge during your working day, go right ahead and squash/merge it.

Copy link

sonarqubecloud bot commented Dec 7, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 4 Code Smells

94.5% 94.5% Coverage
0.0% 0.0% Duplication

@robobario robobario merged commit 276b061 into kroxylicious:main Dec 7, 2023
3 checks passed
@k-wall k-wall deleted the env-enc-kw2 branch December 8, 2023 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Implement Kms for Hashicorp vault
4 participants