-
Notifications
You must be signed in to change notification settings - Fork 10
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
transitapi: add http handler enc/dec #1199
base: main
Are you sure you want to change the base?
Conversation
2b3b3ce
to
9cd6b17
Compare
eg.Go(func() error { | ||
mux := transitengine.NewTransitEngineAPI(meshAuth, logger) | ||
logger.Info("Transit Engine API initialized") | ||
port := 8200 | ||
fmt.Printf("Serving transit engine API on port %d\n", port) | ||
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), mux); err != nil { | ||
logger.Error("Failed to start transit engine API", "err", err) | ||
} | ||
return nil | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only be enabled after authorization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, it was intended for testing purposes only and i'll drop this when squashing everything!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave the comment open until it's actually resolved.
8c98f39
to
26f2591
Compare
d12a895
to
031533a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the thorough review, @3u13r!
http.Error(w, fmt.Sprint("URL version: %w", err), http.StatusBadRequest) | ||
return | ||
} | ||
key, err := deriveEncryptionKey(authority, workloadSecretID+strVersion) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This potentially allows access to secrets for other workloads, because we can't tell the boundary between workloadSecretId
and strVersion
. E.g.:
workloadSecretId := `pod1`
strVersion := `2`
// vs
workloadSecretId := `pod`
strVersion := `12`
I think we should either derive twice (first for the name, then for the version), or define a separation character, ensure that the version does not contain that character and then derive for strVersion + sepChar + id
(because the id does not have any character set limitations).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out and the thorough review! I changed this to your suggestion. StrVersion implicitly always was ensured to match ^v(\d+)$
, therefore i added '_' as seperator and changed the order to mitigate the described attack possibilities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But as described in the other comments, i dropped the strVersion completely as it does not specify the key_version as expected and used the key_version
value instead. In the decryptionRequest, the version is still matched against the regex ^v(\d+)$
, because it is part of the ciphertext field. And for the encryptionRequest the key_version
value is autoparsed during json unmarshalling to int.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can i mark this as resolved this with the updated separation logic? @burgerdev
eg.Go(func() error { | ||
mux := transitengine.NewTransitEngineAPI(meshAuth, logger) | ||
logger.Info("Transit Engine API initialized") | ||
port := 8200 | ||
fmt.Printf("Serving transit engine API on port %d\n", port) | ||
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), mux); err != nil { | ||
logger.Error("Failed to start transit engine API", "err", err) | ||
} | ||
return nil | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave the comment open until it's actually resolved.
8b6dbdb
to
047b6bd
Compare
This commit drops the version parsing of the URL, as it was refering to the API endpoint and not the key_version which was a wrong assumption. It introduces the key_version field to the encryption request and adds the necessary logic to the derivation, to ensure inputs are synthesized and seperated by disallowed characters. It reduces the (un)marshalling functions to only implement ciphertext container marshalling and instead rely on the starndard go library.
047b6bd
to
5879177
Compare
55d25fa
to
f2c3a9f
Compare
type ( | ||
// ciphertextContainer describes a base64-encoded ciphertext prepended with the nonce and specified key version. | ||
ciphertextContainer struct { | ||
nonce []byte | ||
ciphertext []byte | ||
keyVersion uint | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs gofmt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was expecting gofmt to convert this to
type ciphertextContainer struct {
}
Please do that manually.
encodedNonce := b64.StdEncoding.EncodeToString(c.nonce) | ||
encodedCiphertext := b64.StdEncoding.EncodeToString(c.ciphertext) | ||
// Convert to "vault:vX:base64" format | ||
versioned := fmt.Sprintf("vault:v%d:%s%s", c.keyVersion, encodedNonce, encodedCiphertext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if keyVersion==0
, considering that Vault key versions start with 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding https://developer.hashicorp.com/vault/api-docs/secret/transit#encrypt-data default of key_version is 0, i looked it up to be compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you referring to key_version (int: 0)
? I took that to mean
The default for
key_version
is 0, which means "use latest key".
which would be consistent with the example responses in the docs.
f2c3a9f
to
9c72d62
Compare
9c72d62
to
029fcec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're closing in!
type ( | ||
// ciphertextContainer describes a base64-encoded ciphertext prepended with the nonce and specified key version. | ||
ciphertextContainer struct { | ||
nonce []byte | ||
ciphertext []byte | ||
keyVersion uint | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was expecting gofmt to convert this to
type ciphertextContainer struct {
}
Please do that manually.
// parseRequest parses the given HTTP request body into the struct, which should represent to the | ||
// received request body. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// parseRequest parses the given HTTP request body into the struct, which should represent to the | |
// received request body. | |
// parseRequest parses the given HTTP request body into the struct |
data, exist := decRespBody["data"].(map[string]any) | ||
require.True(exist) | ||
receivedPlaintext, exist = data["plaintext"].(string) | ||
require.True(exist) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not existence checks, but type cast checks. If the expected fields are not found, this will panic. You could avoid the type cast by assigning the expected type to begin with:
var decRespBody map[string]map[string]string
This PR adds basic http handling functionality for the encrypt and decrypt endpoints of the transit engine API, allowing the auto-unsealing process for user-managed Vaults. Additionally a cyclic cryptographic unit test, testing the implemented handler functions for encrypt and decrypt was added.
ToDo's