Serializes upload state to an HMAC token for subsequent requests
To support clustered registry, upload UUIDs must be recognizable by registries that did not issue the UUID. By creating an HMAC verifiable upload state token, registries can validate upload requests that other instances authorized. The tokenProvider interface could also use a redis store or other system for token handling in the future.
This commit is contained in:
65
tokens.go
Normal file
65
tokens.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution/storage"
|
||||
)
|
||||
|
||||
// tokenProvider contains methods for serializing and deserializing state from token strings.
|
||||
type tokenProvider interface {
|
||||
// LayerUploadStateFromToken retrieves the LayerUploadState for a given state token.
|
||||
LayerUploadStateFromToken(stateToken string) (storage.LayerUploadState, error)
|
||||
|
||||
// LayerUploadStateToToken returns a token string representing the given LayerUploadState.
|
||||
LayerUploadStateToToken(layerUploadState storage.LayerUploadState) (string, error)
|
||||
}
|
||||
|
||||
type hmacTokenProvider struct {
|
||||
secret string
|
||||
}
|
||||
|
||||
func newHMACTokenProvider(secret string) tokenProvider {
|
||||
return &hmacTokenProvider{secret: secret}
|
||||
}
|
||||
|
||||
// LayerUploadStateFromToken deserializes the given HMAC stateToken and validates the prefix HMAC
|
||||
func (ts *hmacTokenProvider) LayerUploadStateFromToken(stateToken string) (storage.LayerUploadState, error) {
|
||||
var lus storage.LayerUploadState
|
||||
|
||||
tokenBytes, err := base64.URLEncoding.DecodeString(stateToken)
|
||||
if err != nil {
|
||||
return lus, err
|
||||
}
|
||||
mac := hmac.New(sha256.New, []byte(ts.secret))
|
||||
|
||||
if len(tokenBytes) < mac.Size() {
|
||||
return lus, fmt.Errorf("Invalid token")
|
||||
}
|
||||
|
||||
macBytes := tokenBytes[:mac.Size()]
|
||||
messageBytes := tokenBytes[mac.Size():]
|
||||
|
||||
mac.Write(messageBytes)
|
||||
if !hmac.Equal(mac.Sum(nil), macBytes) {
|
||||
return lus, fmt.Errorf("Invalid token")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(messageBytes, &lus); err != nil {
|
||||
return lus, err
|
||||
}
|
||||
|
||||
return lus, nil
|
||||
}
|
||||
|
||||
// LayerUploadStateToToken serializes the given LayerUploadState to JSON with an HMAC prepended
|
||||
func (ts *hmacTokenProvider) LayerUploadStateToToken(lus storage.LayerUploadState) (string, error) {
|
||||
mac := hmac.New(sha256.New, []byte(ts.secret))
|
||||
stateJSON := fmt.Sprintf("{\"Name\": \"%s\", \"UUID\": \"%s\", \"Offset\": %d}", lus.Name, lus.UUID, lus.Offset)
|
||||
mac.Write([]byte(stateJSON))
|
||||
return base64.URLEncoding.EncodeToString(append(mac.Sum(nil), stateJSON...)), nil
|
||||
}
|
Reference in New Issue
Block a user