diff options
author | Jacob Vosmaer <contact@jacobvosmaer.nl> | 2020-02-20 16:49:19 +0300 |
---|---|---|
committer | Jacob Vosmaer <contact@jacobvosmaer.nl> | 2020-02-20 16:49:19 +0300 |
commit | a51521d682c0a424d22c4adba027030d7a8ccb4d (patch) | |
tree | 8257d89324e84f15380b1c16bb223589fcf8231c /auth | |
parent | fde7ec76313edd43f55704239b5ad50dac6af663 (diff) |
Clarify how Gitaly v2 auth works
Diffstat (limited to 'auth')
-rw-r--r-- | auth/README.md | 27 | ||||
-rw-r--r-- | auth/rpccredentials.go | 44 |
2 files changed, 47 insertions, 24 deletions
diff --git a/auth/README.md b/auth/README.md new file mode 100644 index 000000000..b5dc6f568 --- /dev/null +++ b/auth/README.md @@ -0,0 +1,27 @@ +# Gitaly authentication middleware for Go + +This package contains code that plugs into +github.com/grpc-ecosystem/go-grpc-middleware/auth to provide client +and server authentication middleware for Gitaly. + +Gitaly has two authentication schemes. + +## V1 authentication (deprecated) + +This scheme uses a shared secret. The shared secret is base64-encoded +and passed by the client as a bearer token. + +## V2 authentication + +This scheme uses a time limited token derived from a shared secret. + +The client creates a timestamp and computes the SHA256 HMAC signature +for that timestamp, treating the timestamp as the message. The shared +secret is used as the key for the HMAC. The client then sends both the +message and the signature to the server as a bearer token. + +The server takes the message and computes the signature. If the +client-provided signature matches the computed signature the message is +accepted. Next, the server checks if its current time is no more than +30 seconds ahead or behind the timestamp. If the timestamp is too old +or too new the request is denied. Otherwise it goes ahead. diff --git a/auth/rpccredentials.go b/auth/rpccredentials.go index ca54901da..fc39a620b 100644 --- a/auth/rpccredentials.go +++ b/auth/rpccredentials.go @@ -11,46 +11,42 @@ import ( ) // RPCCredentials can be used with grpc.WithPerRPCCredentials to create a -// grpc.DialOption that inserts the supplied token for authentication -// with a Gitaly server. -func RPCCredentials(token string) credentials.PerRPCCredentials { - return &rpcCredentials{token: base64.StdEncoding.EncodeToString([]byte(token))} +// grpc.DialOption that uses v1 Gitaly authentication for authentication +// with a Gitaly server. The shared secret must match the one used on the +// Gitaly server. +func RPCCredentials(sharedSecret string) credentials.PerRPCCredentials { + return &rpcCredentials{sharedSecret: base64.StdEncoding.EncodeToString([]byte(sharedSecret))} } type rpcCredentials struct { - token string + sharedSecret string } func (*rpcCredentials) RequireTransportSecurity() bool { return false } func (rc *rpcCredentials) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { - return map[string]string{"authorization": "Bearer " + rc.token}, nil + return map[string]string{"authorization": "Bearer " + rc.sharedSecret}, nil } -// RPCCredentialsV2 can be used with grpc.WithPerRPCCredentials to create a -// grpc.DialOption that inserts an HMAC token with the current timestamp -// for authentication with a Gitaly server. -func RPCCredentialsV2(token string) credentials.PerRPCCredentials { - return &rpcCredentialsV2{token: token} +// RPCCredentialsV2 can be used with grpc.WithPerRPCCredentials to create +// a grpc.DialOption that inserts an V2 (HMAC) token with the current +// timestamp for authentication with a Gitaly server. The shared secret +// must match the one used on the Gitaly server. +func RPCCredentialsV2(sharedSecret string) credentials.PerRPCCredentials { + return &rpcCredentialsV2{sharedSecret: sharedSecret} } type rpcCredentialsV2 struct { - token string + sharedSecret string } func (*rpcCredentialsV2) RequireTransportSecurity() bool { return false } -func (rc *rpcCredentialsV2) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { - return map[string]string{"authorization": "Bearer " + rc.hmacToken()}, nil -} - -func (rc *rpcCredentialsV2) hmacToken() string { - return hmacToken("v2", []byte(rc.token), time.Now()) -} - -func hmacToken(version string, secret []byte, timestamp time.Time) string { - intTime := timestamp.Unix() - signedTimestamp := hmacSign(secret, strconv.FormatInt(intTime, 10)) +func (rc2 *rpcCredentialsV2) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { + message := strconv.FormatInt(time.Now().Unix(), 10) + signature := hmacSign([]byte(rc2.sharedSecret), message) - return fmt.Sprintf("%s.%x.%d", version, signedTimestamp, intTime) + return map[string]string{ + "authorization": "Bearer " + fmt.Sprintf("v2.%x.%s", signature, message), + }, nil } |