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

nitro configuration management #1385

Open
wants to merge 3 commits into
base: nitro-payments
Choose a base branch
from
Open

nitro configuration management #1385

wants to merge 3 commits into from

Conversation

husobee
Copy link
Contributor

@husobee husobee commented Apr 17, 2022

Summary

configuration handler to dynamically set configurations, with a secrets conf item which will retrieve secrets

Type of change ( select one )

  • Product feature
  • Bug fix
  • Performance improvement
  • Refactor
  • Other

Tested Environments

  • Development
  • Staging
  • Production

Before submitting this PR:

  • Does your code build cleanly without any errors or warnings?
  • Have you used auto closing keywords?
  • Have you added tests for new functionality?
  • Have validated query efficiency for new database queries?
  • Have documented new functionality in README or in comments?
  • Have you squashed all intermediate commits?
  • Is there a clear title that explains what the PR does?
  • Have you used intuitive function, variable and other naming?
  • Have you requested security / privacy review if needed
  • Have you performed a self review of this PR?

Manual Test Plan:

@husobee husobee self-assigned this Apr 17, 2022
Copy link
Member

@kdenhartog kdenhartog left a comment

Choose a reason for hiding this comment

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

Overall this seems like a sound implementation with a workable design. When reading through it though I began to realize that there's a few ways that we could simplify the design, but I'm not certain of the requirements that led us to this design or that I'm properly understanding the bigger picture of this. Let's talk through this today during our sync meeting.

func initService(ctx context.Context) (*Service, error) {
logger := logging.Logger(ctx, "initService")
// generate the ed25519 pub/priv keypair for secrets management
pubKey, privKey, err := box.GenerateKey(rand.Reader)
Copy link
Member

Choose a reason for hiding this comment

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

This looks like a good way to generate a new key per service on the fly. We're relying on a well known CSPRNG and using the nacl box implementation (a well known port of Nacl to Go-lang) to perform key generation.

}

// initialize the service
func initService(ctx context.Context) (*Service, error) {
Copy link
Member

Choose a reason for hiding this comment

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

The only place that I see this is being called at the moment is via the controllers_test.go file. Is the expectation here that we'd be generating an ephemeral keypair for each time that this is called (e.g. in the GET /conf handler) or would this be called once on initialization of the microservice? This is my first time looking at a Go backend microservice so if I'm missing something obvious my apologies.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

https://github.com/brave-intl/bat-go/blob/nitro-payments-dev/ this is the branch which holds all the collated code, and uses initService in the main microservice.

}

var decryptKey [32]byte
copy(decryptKey[:], key[:32])
Copy link
Member

Choose a reason for hiding this comment

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

Is there other data that's included in this "key" array? Ideally we wouldn't want to copy this key material to a separate piece of memory if not necessary and we'd ideally want to be cleaning up the memory ourselves by overwriting all places where private key material is stored in memory once we're certain they'll no longer be used rather than relying on the garbage collector for this. The main reason for this is to reduce the possiblity of someone discovering the key by inspecting the memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@husobee: reuse key instead of performing copy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@husobee: zeroize capabilities in constant time.

var decryptKey [32]byte
copy(decryptKey[:], key[:32])

// decrypted is the encryption key used to decrypt secrets now
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// decrypted is the encryption key used to decrypt secrets now
// decryptKey is the symmetrical encryption key used to decrypt configuration secrets now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@husobee: accept

copy(senderKeyT[:], senderKey[:32])

var privKey [32]byte
copy(privKey[:], s.privKey[:32])
Copy link
Member

Choose a reason for hiding this comment

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

Same here - Ideally we wouldn't want to be making a copy of the privateKey if we don't need to. I'm uncertain if this copy is done in constant time which could create a side channel attack for the private key.

Since we didn't generate the s.privKey within the scope of this function we'd not want to overwrite it though so that's fine as is. If we do want to make copies of the key here (I need to dig more into this) then we would want to clean them up after usage on line 70 since we know the scope of usage of this key.

I'll do some digging to see if I can find a good way to overwrite an array with all zeros in constant time and also if there's an issue calling this copy function to copy private keys.

Copy link
Member

Choose a reason for hiding this comment

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

Double checked the copy operation in the spec. As far as I can tell this shouldn't be an issue. Here's a PoC I wrote as well to check the assembly.


// PaymentsEncryptionKeyCTXKey - the context key for getting the application secrets file location
PaymentsEncryptionKeyCTXKey CTXKey = "payments_encryption_key"
// PaymentsSenderPublicKeyCTXKey - the context key for getting the application secrets file location
Copy link
Member

Choose a reason for hiding this comment

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

Same here - can you double check this one as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@husobee: adjust comment accordingly

@@ -0,0 +1,10 @@
package service
Copy link
Member

Choose a reason for hiding this comment

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

nit: Can we rename this file to SecretManager.go to reflect the interface being defined in this file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@husobee: to fix name file

)

// ConfigurationMiddleware applies the current state of the service's configuration on the ctx
func (service *Service) ConfigurationMiddleware() func(http.Handler) http.Handler {
Copy link
Member

Choose a reason for hiding this comment

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

nit: would be nice to have this named PaymentConfigurationMiddleware to more accurately reflect it's purpose since it's not a generic Configuration middleware

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@husobee to adjust naming to reflect

}
}

func TestGetConfigurationHandler(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

This test looks good to me and easy to read - nice work!

reqBody := configurationHandlerRequest(map[appctx.CTXKey]interface{}{
appctx.CommitCTXKey: "value", // commit is a configuration pushed in
appctx.SecretsURICTXKey: "secrets uri", // tell configuration to pull new secrets
appctx.PaymentsEncryptionKeyCTXKey: base64.StdEncoding.EncodeToString(encrypted), // tell configuration to pull new secrets
Copy link
Member

Choose a reason for hiding this comment

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

What's the threat model and motivation here that led to us using the nitro_shim to encrypt a blob and store it in an S3 bucket and then encrypt the symmetrical key for the payments service? I can think of a few ways that we could simplify this architecture, but I'm not certain of the requirements that led us to this construction or that I properly understand the design so I want to understand these things first before suggesting changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly we can figure a way to archive/eliminate existing s3 buckets

@husobee husobee mentioned this pull request Nov 4, 2022
18 tasks
@pavelbrm
Copy link
Contributor

Since #1691 is closed now, I wonder if this should be closed as well?

cc @Sneagan

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.

3 participants