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

fix ssec: missing base64 encoding and md5 hash #225

Merged
merged 4 commits into from
Dec 16, 2024

Conversation

gschei
Copy link
Contributor

@gschei gschei commented Nov 10, 2024

This PR is based on #224 and replaces PR #217 (which has some code duplication and is not complete).

It fixes these issues:

  • the SSEC-CustomerKey is stored in the Velero Container as plain 32 byte key and must be base64 encoded before sending it to AWS.
  • as shown in PR 217 MD5 hash is missing for head/get/put operations

I tested this change successfully with an encrypted backup and restore with Velero and S3 Bucket.

Signed-off-by: Gilbert Scheiblhofer <gilbert.scheiblhofer@gmx.at>
Signed-off-by: Gilbert Scheiblhofer <gilbert.scheiblhofer@gmx.at>
@gschei gschei changed the title gschei/fix ssec base64 md5 fix ssec: missing base64 encoding and md5 hash Nov 10, 2024
@gschei gschei mentioned this pull request Nov 10, 2024
@tonobo
Copy link

tonobo commented Nov 22, 2024

I'm pretty new to velero, i tried this mr locally and noticed there are issues while accessing the logs and futher contents from the bucket, probably caused by not passing the sse-c headers. am i holding it wrong or is there still something missing.

@@ -242,7 +247,7 @@ func readCustomerKey(customerKeyEncryptionFile string) (string, error) {
fileHandle.Close()

if nBytes != 32 {
return "", errors.Wrapf(err, "contents of %s (%s) are not exactly 32 bytes", customerKeyEncryptionFileKey, customerKeyEncryptionFile)
return "", fmt.Errorf("contents of %s (%s) are not exactly 32 bytes", customerKeyEncryptionFileKey, customerKeyEncryptionFile)
Copy link
Member

Choose a reason for hiding this comment

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

Is this necessary? This will mean the log trace won't propagate to velero pod logs with stack trace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point of the fix is that err is always nil in this situation and errors.Wrapf here always returns nil, so to velero looks like key was read successfully (but is ""). I encountered this situation during testing, having a key < 32 bytes and no errors showed up in velero (but overall encryption did not work).

With this change, now the error is shown in the Backup-Resource correctly (but no stacktrace though).

Copy link
Member

Choose a reason for hiding this comment

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

ahh.. got it. Make sense. Here's a revised suggestion.

Suggested change
return "", fmt.Errorf("contents of %s (%s) are not exactly 32 bytes", customerKeyEncryptionFileKey, customerKeyEncryptionFile)
return "", errors.Errorf("contents of %s (%s) are not exactly 32 bytes", customerKeyEncryptionFileKey, customerKeyEncryptionFile)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks..PR updated

@@ -186,7 +189,9 @@ func (o *ObjectStore) Init(config map[string]string) error {
if err != nil {
return err
}
o.sseCustomerKey = customerKey
o.sseCustomerKey = base64.StdEncoding.EncodeToString([]byte(customerKey))
Copy link
Member

Choose a reason for hiding this comment

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

Why are we encoding string to []byte then string again?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, your idea is to change the function readCustomerKey to return a []byte instead of a string? Would make sense, I can update the PR for 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 see now that the description says "<your_b64_encoded_32byte_string>" which is wrong, the key must be plain and is then encoded to base64 before sending it.

Does "the key must be plain" holds true prior to this PR? Are we creating breaking change from customerKey is base64 -> customerKey must be plain?

Is the reason you are adding encoding here is because you wanted plain key instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the question is, should the key be stored plain or base64 encoded in the container? I assume plain because here

keyBytes := make([]byte, 32)

we read exactly 32 bytes. If the key would be base64 encoded, then it would be longer then 32 bytes, and the mentioned line would be incorrect and needs to be fixed.

@gschei
Copy link
Contributor Author

gschei commented Nov 23, 2024

I'm pretty new to velero, i tried this mr locally and noticed there are issues while accessing the logs and futher contents from the bucket, probably caused by not passing the sse-c headers. am i holding it wrong or is there still something missing.

To test it successfully, you must mount the encryption key into the velero container (eg. from a secret) and then reference the file containing the key in the config of the BackupStorageLocation. See description here: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/backupstoragelocation.md.

I see now that the description says "<your_b64_encoded_32byte_string>" which is wrong, the key must be plain and is then encoded to base64 before sending it.

Signed-off-by: Gilbert Scheiblhofer <gilbert.scheiblhofer@gmx.at>
@tonobo
Copy link

tonobo commented Nov 24, 2024

@gschei you're right that's not my concern. I get it to work after checking the actual code not the problem, but once i enable the encryption part i'm unable to call velero backup logs for example. I always get an common error message as it's not passing the credentials somewhere. Local and within the container.

@tonobo
Copy link

tonobo commented Nov 24, 2024

EDIT: Nevermind, handled totally differnent

besides that, the internal kopia data mover seems to have trouble as well.

 data path backup failed: Failed to run data path service for DataUpload
    daily-pvc-backup-20241124210813-lkshf: error to initialize data path: error to
    boost backup repository connection backups-apps-kopia: error to connect backup
    repo: error to connect repo with storage: error to connect to repository: repository
    not initialized in the provided storage'

Can you confirm csi data movement works with sse-c backup locations?

P.S by dropping the sse-c key at all, it just works.

@gschei
Copy link
Contributor Author

gschei commented Nov 25, 2024

@gschei you're right that's not my concern. I get it to work after checking the actual code not the problem, but once i enable the encryption part i'm unable to call velero backup logs for example. I always get an common error message as it's not passing the credentials somewhere. Local and within the container.

Yes I see velero backup logs creates a DownloadRequest Resources which does not contain the Encryption Headers. Looks like object_store.CreateSignedURL needs to be extended too with Encryption Parameters.

@gschei
Copy link
Contributor Author

gschei commented Nov 26, 2024

It looks like AWS getSignedUrl dies not support SSECustomerKey, see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property

Note:

Not all operation parameters are supported when using pre-signed URLs. Certain parameters, such as SSECustomerKey, ACL, Expires, ContentLength, or Tagging must be provided as headers when sending a request. If you are using pre-signed URLs to upload from a browser and need to use these fields, see createPresignedPost().

@kaovilai
Copy link
Member

getSignedUrl

is currently required until vmware-tanzu/velero#6167 gets implemented.

@gschei
Copy link
Contributor Author

gschei commented Dec 10, 2024

One additional remark regarding the encryption key: original implementation ac31cd9 (from feb/22) stated that the key should be a "32 byte string". Later (oct/23) a863977 a documentation update was done which apparently was wrong because it contradicts the original statement, saying that the key should now be a "base64 encoded 32 byte string" which is not correct, because within the code only the first 32 bytes are read anyway (and base64 endoding will make the string longer).

From my point of view current implementation is not working at the moment - once due to missing base64 (or alternatively correct parsing of base64 encoded key) and also due to missing md5 hash - a problem which appeared later.

velero backup logs statement is generally not working for encrypted backups and will also not work after merging this PR. But creation and restore of encrypted backups would work now.

@kaovilai @reasonerjt Could you please have a look at this again and merge if OK?

@kaovilai
Copy link
Member

Please update readme and or backupstoragelocation.md to reflect this PR or the current state of using customerKey since you're probably most versed in how to use this PR as is.

@kaovilai
Copy link
Member

# Specify the file that contains the SSE-C customer key to enable customer key encryption of the backups
# stored in S3. The referenced file should contain a 32-byte string.
#
# The customerKeyEncryptionFile points to a mounted secret within the velero container.
# Add the below values to the velero cloud-credentials secret:
# customer-key: <your_b64_encoded_32byte_string>
# The default value below points to the already mounted secret.
#
# Cannot be used in conjunction with kmsKeyId.
#
# Optional (defaults to "", which means SSE-C is disabled).
customerKeyEncryptionFile: "/credentials/customer-key"

Is this accurate as is?

Signed-off-by: Gilbert Scheiblhofer <gilbert.scheiblhofer@gmx.at>
@gschei
Copy link
Contributor Author

gschei commented Dec 10, 2024

@kaovilai I clarified the description. The point is, when I base64 encode the key within the kubernetes secret, it will be automatically base64 decoded by kubernetes when mounted into the container.

Copy link
Member

@kaovilai kaovilai left a comment

Choose a reason for hiding this comment

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

This lgtm. Just some clarifications. Thanks for the PR!

Copy link
Contributor

@reasonerjt reasonerjt 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

@reasonerjt reasonerjt merged commit e5b0f0d into vmware-tanzu:main Dec 16, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants