-
Notifications
You must be signed in to change notification settings - Fork 187
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
Disperser auth #984
base: master
Are you sure you want to change the base?
Disperser auth #984
Conversation
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
adding fuzz tests for kms key methods.
Fuzz tests for kms.go
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
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.
lgtm, few comments
uint32 disperserID = 2; | ||
|
||
// Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature | ||
// is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing. |
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.
🤣
return nil | ||
} | ||
|
||
key, err := a.getDisperserKey(ctx, now, request.DisperserID) |
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 would result in an extra ETH call for any new DisperserID
it sees.
Since we're already hardcoding the only allowed disperser, does it make sense if we consider a request invalid if DisperserID
is unknown (not EigenLabsDisperserID) in isAuthenticationStillValid
?
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.
var defaultAddress gethcommon.Address
if err != nil {
return defaultAddress, fmt.Errorf("failed to get disperser address: %w", err)
}
if address == defaultAddress {
return defaultAddress, fmt.Errorf("disperser with id %d not found", disperserID)
}
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.
I think this reply is for another comment in reader
.
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.
Oops, you're correct @ian-shim, wires got crossed here. 🙃
The response I intended to type here was that the code does cache this value inside this cache: keyCache *lru.Cache[uint32, *keyWithTimeout]
.
disperser/cmd/controller/main.go
Outdated
nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, logger) | ||
|
||
var requestSigner clients.RequestSigner | ||
if !config.DisperserStoreChunksSigningDisabled { |
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.
if the store chunks signing is disabled, we should probably throw a warning on the start up logs.
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.
var requestSigner clients.DispersalRequestSigner
if config.DisperserStoreChunksSigningDisabled {
logger.Warn("StoreChunks() signing is disabled")
} else {
requestSigner, err = clients.NewDispersalRequestSigner(
context.Background(),
config.AwsClientConfig.Region,
config.AwsClientConfig.EndpointURL,
config.DisperserKMSKeyID)
if err != nil {
return fmt.Errorf("failed to create request signer: %v", err)
}
}
|
||
// authenticationTimeoutDuration is the duration for which an auth is valid. | ||
// If this is zero, then auth saving is disabled, and each request will be authenticated independently. | ||
authenticationTimeoutDuration time.Duration |
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 keep this value low for the beginning.
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.
I've set this to be one minute by default. Is that sufficient, or should we lower it further?
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
config *NodeClientConfig | ||
initOnce sync.Once | ||
conn *grpc.ClientConn | ||
requestSigner DispersalRequestSigner |
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.
A slightly different structuring of it: shall we pass in an interface, not a concrete implementation?
Like we have RequestSigners interface (and passed around), but with a KMS based implementation. This leaves space for potential non-KMS (i.e. non AWS specific) options (in a decentralized scenario it may be needed).
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.
I'm a little confused about this comment. DispersalRequestSigner
is already an interface. Is the interface ok in its current form?
// DispersalRequestSigner encapsulates the logic for signing GetChunks requests.
type DispersalRequestSigner interface {
// SignStoreChunksRequest signs a StoreChunksRequest. Does not modify the request
// (i.e. it does not insert the signature).
SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error)
}
&bind.CallOpts{ | ||
Context: ctx, | ||
}, | ||
disperserID) |
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 we settle on either ID or Key? Using both is unnecessary and adds a little bit confusion.
(if this is meant to be an int, ID seems fit)
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.
I'm happy with calling this an "ID". These are serial numbers. Eventually when we allow the community to register dispersers, we will allocate these in monotonic order via a smart contract.
Where do you see the terminology "key" being used in this context?
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.
Ah, I think I see what you are getting at. There is a method in the chain reader that references the disperser key:
getDisperserKey(
ctx context.Context,
now time.Time,
disperserID uint32) (*gethcommon.Address, error)
The disperser key and the disperser ID are actually distinct things.
- Disperser ID: a unique identifier for the disperser, a 32 bit serial number
- Disperser (public) key: the public key used to verify
StoreChunks()
requests from the disperser, is an eth address
The reason why the disperser's public key is not a good identifier for the disperser is that we want the capability to re-key the disperser (e.g. if we lose the key, it gets compromised, or we just want to rotate it).
api/clients/v2/request_signer.go
Outdated
@@ -0,0 +1,62 @@ | |||
package clients |
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.
nit: we can update the file name as well?
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.
Oops, forgot to rename the file. 😅 Fixed.
return nil | ||
} | ||
|
||
key, err := a.getDisperserKey(ctx, now, request.DisperserID) |
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.
I think this reply is for another comment in reader
.
// chainReader is used to read the chain state. | ||
chainReader core.Reader | ||
|
||
// keyCache is used to cache the public keys of dispersers. |
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 public key has only 32 bits?
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.
The uint32
is the relay ID. These are serial numbers, not eth keys. Currently we have exactly one disperser, and its ID is hard coded to be uint32(0)
.
node/auth/authenticator.go
Outdated
return fmt.Errorf("failed to verify request: %w", err) | ||
} | ||
|
||
a.saveAuthenticationResult(now, origin) |
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.
nit: cacheAuthenticationResult
, "save" feels like it's persisting the data which it's not
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.
renamed
} | ||
} | ||
|
||
address, err := a.chainReader.GetDisperserAddress(ctx, disperserID) |
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.
I'd cache this ID to address mapping to save RPCs
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 is cached (see keyCache *lru.Cache[uint32, *keyWithTimeout]
). By design, we re-fetch the same key again after a timeout just in case the key was changed. But not for each RPC.
@@ -238,6 +238,33 @@ var ( | |||
EnvVar: common.PrefixEnvVar(EnvVarPrefix, "CHUNK_DOWNLOAD_TIMEOUT"), | |||
Value: 20 * time.Second, | |||
} | |||
DisableDispersalAuthenticationFlag = cli.BoolFlag{ |
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.
Why disabling is an option? shouldn't it be non optional?
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.
It's enabled by default. Necessary to get things working in inabox. Inabox integration is kind of complex, so I'd like to merge this code with it disabled in the e2e test, and to enable it in a follow up PR.
node/auth/request_signing.go
Outdated
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 file might be better fit in api/clients
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.
I've moved all GRPC hashing code to a new package api/hashing
. There is now no longer a dependency (outside of test code) between the client and this file.
api/clients/v2/node_client.go
Outdated
@@ -3,6 +3,7 @@ package clients | |||
import ( | |||
"context" | |||
"fmt" | |||
"github.com/Layr-Labs/eigenda/node/auth" |
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.
It doesn't seem to me this is a good dependency direction for api/ to depend on node/
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 dependency is no longer present
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Why are these changes needed?
Authenticate
StoreChunks()
requests.Checks