From 933c60bc3af755f85c28e771be614afef5fd371f Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 6 Jan 2020 16:15:22 -0800 Subject: [PATCH] Add crypto context errors (#8740) Currently when connections to vault fail, client perpetually retries this leads to assumptions that the server has issues and masks the problem. Re-purpose *crypto.Error* type to send appropriate errors back to the client. --- Dockerfile | 2 +- Dockerfile.dev | 2 +- Dockerfile.release | 2 +- cmd/api-errors.go | 4 +-- cmd/crypto/config.go | 3 +- cmd/crypto/error.go | 55 +++++++++++++++++++++++++------------ cmd/crypto/kes.go | 19 ++++++------- cmd/crypto/key.go | 3 +- cmd/crypto/legacy.go | 3 +- cmd/crypto/metadata.go | 19 ++++++------- cmd/crypto/metadata_test.go | 14 +++++----- cmd/crypto/parse.go | 7 ++--- cmd/crypto/vault.go | 53 ++++++++++++++++++++++------------- cmd/web-handlers.go | 9 +++++- pkg/iam/policy/error.go | 21 ++++++++------ pkg/iam/policy/policy.go | 2 +- pkg/policy/error.go | 19 +++++++------ pkg/policy/policy.go | 2 +- 18 files changed, 139 insertions(+), 100 deletions(-) diff --git a/Dockerfile b/Dockerfile index 029434e26..40b0be493 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN \ git clone https://github.com/minio/minio && cd minio && \ go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" -FROM alpine:3.9 +FROM alpine:3.10 ENV MINIO_UPDATE off ENV MINIO_ACCESS_KEY_FILE=access_key \ diff --git a/Dockerfile.dev b/Dockerfile.dev index 64950aca4..149749461 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM alpine:3.9 +FROM alpine:3.10 LABEL maintainer="MinIO Inc " diff --git a/Dockerfile.release b/Dockerfile.release index b415acca0..fe96a03fd 100644 --- a/Dockerfile.release +++ b/Dockerfile.release @@ -8,7 +8,7 @@ RUN \ apk add --no-cache git && \ git clone https://github.com/minio/minio -FROM alpine:3.9 +FROM alpine:3.10 LABEL maintainer="MinIO Inc " diff --git a/cmd/api-errors.go b/cmd/api-errors.go index a5df03034..af30bd01c 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -1727,8 +1727,6 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) { apiErr = ErrUnsupportedNotification case BackendDown: apiErr = ErrBackendDown - case crypto.Error: - apiErr = ErrObjectTampered case ObjectNameTooLong: apiErr = ErrKeyTooLongError default: @@ -1781,7 +1779,7 @@ func toAPIError(ctx context.Context, err error) APIError { } case crypto.Error: apiErr = APIError{ - Code: "XKMSInternalError", + Code: "XMinIOEncryptionError", Description: e.Error(), HTTPStatusCode: http.StatusBadRequest, } diff --git a/cmd/crypto/config.go b/cmd/crypto/config.go index 6642aeaba..81a79743c 100644 --- a/cmd/crypto/config.go +++ b/cmd/crypto/config.go @@ -16,7 +16,6 @@ package crypto import ( "errors" - "fmt" "net/http" "reflect" "strconv" @@ -342,7 +341,7 @@ func LookupVaultConfig(kvs config.KVS) (VaultConfig, error) { if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" { vcfg.Key.Version, err = strconv.Atoi(keyVersion) if err != nil { - return vcfg, fmt.Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion) + return vcfg, Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion) } } diff --git a/cmd/crypto/error.go b/cmd/crypto/error.go index dcbe6d579..1012f5c42 100644 --- a/cmd/crypto/error.go +++ b/cmd/crypto/error.go @@ -14,60 +14,79 @@ package crypto -import "errors" +import ( + "fmt" +) // Error is the generic type for any error happening during decrypting // an object. It indicates that the object itself or its metadata was // modified accidentally or maliciously. -type Error string +type Error struct { + err error +} -func (e Error) Error() string { return string(e) } +// Errorf - formats according to a format specifier and returns +// the string as a value that satisfies error of type crypto.Error +func Errorf(format string, a ...interface{}) error { + return Error{err: fmt.Errorf(format, a...)} +} + +// Unwrap the internal error. +func (e Error) Unwrap() error { return e.err } + +// Error 'error' compatible method. +func (e Error) Error() string { + if e.err == nil { + return "crypto: cause " + } + return e.err.Error() +} var ( // ErrInvalidEncryptionMethod indicates that the specified SSE encryption method // is not supported. - ErrInvalidEncryptionMethod = errors.New("The encryption method is not supported") + ErrInvalidEncryptionMethod = Errorf("The encryption method is not supported") // ErrInvalidCustomerAlgorithm indicates that the specified SSE-C algorithm // is not supported. - ErrInvalidCustomerAlgorithm = errors.New("The SSE-C algorithm is not supported") + ErrInvalidCustomerAlgorithm = Errorf("The SSE-C algorithm is not supported") // ErrMissingCustomerKey indicates that the HTTP headers contains no SSE-C client key. - ErrMissingCustomerKey = errors.New("The SSE-C request is missing the customer key") + ErrMissingCustomerKey = Errorf("The SSE-C request is missing the customer key") // ErrMissingCustomerKeyMD5 indicates that the HTTP headers contains no SSE-C client key // MD5 checksum. - ErrMissingCustomerKeyMD5 = errors.New("The SSE-C request is missing the customer key MD5") + ErrMissingCustomerKeyMD5 = Errorf("The SSE-C request is missing the customer key MD5") // ErrInvalidCustomerKey indicates that the SSE-C client key is not valid - e.g. not a // base64-encoded string or not 256 bits long. - ErrInvalidCustomerKey = errors.New("The SSE-C client key is invalid") + ErrInvalidCustomerKey = Errorf("The SSE-C client key is invalid") // ErrSecretKeyMismatch indicates that the provided secret key (SSE-C client key / SSE-S3 KMS key) // does not match the secret key used during encrypting the object. - ErrSecretKeyMismatch = errors.New("The secret key does not match the secret key used during upload") + ErrSecretKeyMismatch = Errorf("The secret key does not match the secret key used during upload") // ErrCustomerKeyMD5Mismatch indicates that the SSE-C key MD5 does not match the // computed MD5 sum. This means that the client provided either the wrong key for // a certain MD5 checksum or the wrong MD5 for a certain key. - ErrCustomerKeyMD5Mismatch = errors.New("The provided SSE-C key MD5 does not match the computed MD5 of the SSE-C key") + ErrCustomerKeyMD5Mismatch = Errorf("The provided SSE-C key MD5 does not match the computed MD5 of the SSE-C key") // ErrIncompatibleEncryptionMethod indicates that both SSE-C headers and SSE-S3 headers were specified, and are incompatible // The client needs to remove the SSE-S3 header or the SSE-C headers - ErrIncompatibleEncryptionMethod = errors.New("Server side encryption specified with both SSE-C and SSE-S3 headers") + ErrIncompatibleEncryptionMethod = Errorf("Server side encryption specified with both SSE-C and SSE-S3 headers") ) -const ( - errMissingInternalIV Error = "The object metadata is missing the internal encryption IV" - errMissingInternalSealAlgorithm Error = "The object metadata is missing the internal seal algorithm" +var ( + errMissingInternalIV = Errorf("The object metadata is missing the internal encryption IV") + errMissingInternalSealAlgorithm = Errorf("The object metadata is missing the internal seal algorithm") - errInvalidInternalIV Error = "The internal encryption IV is malformed" - errInvalidInternalSealAlgorithm Error = "The internal seal algorithm is invalid and not supported" + errInvalidInternalIV = Errorf("The internal encryption IV is malformed") + errInvalidInternalSealAlgorithm = Errorf("The internal seal algorithm is invalid and not supported") - errMissingUpdatedKey Error = "The key update returned no error but also no sealed key" + errMissingUpdatedKey = Errorf("The key update returned no error but also no sealed key") ) var ( // errOutOfEntropy indicates that the a source of randomness (PRNG) wasn't able // to produce enough random data. This is fatal error and should cause a panic. - errOutOfEntropy = errors.New("Unable to read enough randomness from the system") + errOutOfEntropy = Errorf("Unable to read enough randomness from the system") ) diff --git a/cmd/crypto/kes.go b/cmd/crypto/kes.go index 85c46300f..825f39971 100644 --- a/cmd/crypto/kes.go +++ b/cmd/crypto/kes.go @@ -19,7 +19,6 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -76,13 +75,13 @@ type KesConfig struct { func (k KesConfig) Verify() (err error) { switch { case k.Endpoint == "": - err = errors.New("crypto: missing kes endpoint") + err = Errorf("crypto: missing kes endpoint") case k.CertFile == "": - err = errors.New("crypto: missing cert file") + err = Errorf("crypto: missing cert file") case k.KeyFile == "": - err = errors.New("crypto: missing key file") + err = Errorf("crypto: missing key file") case k.DefaultKeyID == "": - err = errors.New("crypto: missing default key id") + err = Errorf("crypto: missing default key id") } return err } @@ -153,7 +152,7 @@ func (kes *kesService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea return key, nil, err } if len(plainKey) != len(key) { - return key, nil, errors.New("crypto: received invalid plaintext key size from KMS") + return key, nil, Errorf("crypto: received invalid plaintext key size from KMS") } copy(key[:], plainKey) return key, sealedKey, nil @@ -176,7 +175,7 @@ func (kes *kesService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k return key, err } if len(plainKey) != len(key) { - return key, errors.New("crypto: received invalid plaintext key size from KMS") + return key, Errorf("crypto: received invalid plaintext key size from KMS") } copy(key[:], plainKey) return key, nil @@ -301,7 +300,7 @@ func (c *kesClient) parseErrorResponse(resp *http.Response) error { if _, err := io.Copy(&errMsg, io.LimitReader(resp.Body, limit)); err != nil { return err } - return fmt.Errorf("%s: %s", http.StatusText(resp.StatusCode), errMsg.String()) + return Errorf("%s: %s", http.StatusText(resp.StatusCode), errMsg.String()) } // loadCACertificates returns a new CertPool @@ -334,7 +333,7 @@ func loadCACertificates(path string) (*x509.CertPool, error) { if os.IsNotExist(err) || os.IsPermission(err) { return rootCAs, nil } - return nil, fmt.Errorf("crypto: cannot open '%s': %v", path, err) + return nil, Errorf("crypto: cannot open '%s': %v", path, err) } // If path is a file, parse as PEM-encoded certifcate @@ -346,7 +345,7 @@ func loadCACertificates(path string) (*x509.CertPool, error) { return nil, err } if !rootCAs.AppendCertsFromPEM(cert) { - return nil, fmt.Errorf("crypto: '%s' is not a valid PEM-encoded certificate", path) + return nil, Errorf("crypto: '%s' is not a valid PEM-encoded certificate", path) } return rootCAs, nil } diff --git a/cmd/crypto/key.go b/cmd/crypto/key.go index 84a01d5b4..f069159df 100644 --- a/cmd/crypto/key.go +++ b/cmd/crypto/key.go @@ -21,7 +21,6 @@ import ( "crypto/rand" "encoding/binary" "errors" - "fmt" "io" "path" @@ -108,7 +107,7 @@ func (key *ObjectKey) Unseal(extKey [32]byte, sealedKey SealedKey, domain, bucke ) switch sealedKey.Algorithm { default: - return Error(fmt.Sprintf("The sealing algorithm '%s' is not supported", sealedKey.Algorithm)) + return Errorf("The sealing algorithm '%s' is not supported", sealedKey.Algorithm) case SealAlgorithm: mac := hmac.New(sha256.New, extKey[:]) mac.Write(sealedKey.IV[:]) diff --git a/cmd/crypto/legacy.go b/cmd/crypto/legacy.go index 8534a40c7..a074e160b 100644 --- a/cmd/crypto/legacy.go +++ b/cmd/crypto/legacy.go @@ -17,7 +17,6 @@ package crypto import ( - "fmt" "reflect" "strconv" @@ -167,7 +166,7 @@ func lookupConfigLegacy(kvs config.KVS) (VaultConfig, error) { if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" { vcfg.Key.Version, err = strconv.Atoi(keyVersion) if err != nil { - return vcfg, fmt.Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)", + return vcfg, Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)", EnvLegacyVaultKeyVersion, keyVersion) } } diff --git a/cmd/crypto/metadata.go b/cmd/crypto/metadata.go index 5942019df..c89255c0d 100644 --- a/cmd/crypto/metadata.go +++ b/cmd/crypto/metadata.go @@ -18,7 +18,6 @@ import ( "context" "encoding/base64" "errors" - "fmt" "github.com/minio/minio/cmd/logger" ) @@ -126,7 +125,7 @@ func CreateMultipartMetadata(metadata map[string]string) map[string]string { // is nil. func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string { if sealedKey.Algorithm != SealAlgorithm { - logger.CriticalIf(context.Background(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) + logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) } // There are two possibilites: @@ -172,7 +171,7 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte } b64SealedKey, ok := metadata[S3SealedKey] if !ok { - return keyID, kmsKey, sealedKey, Error("The object metadata is missing the internal sealed key for SSE-S3") + return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3") } // There are two possibilites: @@ -182,10 +181,10 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte keyID, idPresent := metadata[S3KMSKeyID] b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey] if !idPresent && kmsKeyPresent { - return keyID, kmsKey, sealedKey, Error("The object metadata is missing the internal KMS key-ID for SSE-S3") + return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3") } if idPresent && !kmsKeyPresent { - return keyID, kmsKey, sealedKey, Error("The object metadata is missing the internal sealed KMS data key for SSE-S3") + return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3") } // Check whether all extracted values are well-formed @@ -198,12 +197,12 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte } encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) if err != nil || len(encryptedKey) != 64 { - return keyID, kmsKey, sealedKey, Error("The internal sealed key for SSE-S3 is invalid") + return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid") } if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key. kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey) if err != nil { - return keyID, kmsKey, sealedKey, Error("The internal sealed KMS data key for SSE-S3 is invalid") + return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid") } } @@ -217,7 +216,7 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte // It allocates a new metadata map if metadata is nil. func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string { if sealedKey.Algorithm != SealAlgorithm { - logger.CriticalIf(context.Background(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm)) + logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm)) } if metadata == nil { @@ -244,7 +243,7 @@ func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err } b64SealedKey, ok := metadata[SSECSealedKey] if !ok { - return sealedKey, Error("The object metadata is missing the internal sealed key for SSE-C") + return sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-C") } // Check whether all extracted values are well-formed @@ -257,7 +256,7 @@ func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err } encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) if err != nil || len(encryptedKey) != 64 { - return sealedKey, Error("The internal sealed key for SSE-C is invalid") + return sealedKey, Errorf("The internal sealed key for SSE-C is invalid") } sealedKey.Algorithm = algorithm diff --git a/cmd/crypto/metadata_test.go b/cmd/crypto/metadata_test.go index 4c3731baa..e77f4e7e4 100644 --- a/cmd/crypto/metadata_test.go +++ b/cmd/crypto/metadata_test.go @@ -125,15 +125,15 @@ var s3ParseMetadataTests = []struct { DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, }, // 1 { - ExpectedErr: Error("The object metadata is missing the internal sealed key for SSE-S3"), + ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-S3"), Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, }, // 2 { - ExpectedErr: Error("The object metadata is missing the internal KMS key-ID for SSE-S3"), + ExpectedErr: Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3"), Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSSealedKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, }, // 3 { - ExpectedErr: Error("The object metadata is missing the internal sealed KMS data key for SSE-S3"), + ExpectedErr: Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3"), Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, }, // 4 @@ -150,7 +150,7 @@ var s3ParseMetadataTests = []struct { DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, }, // 6 { - ExpectedErr: Error("The internal sealed key for SSE-S3 is invalid"), + ExpectedErr: Errorf("The internal sealed key for SSE-S3 is invalid"), Metadata: map[string]string{ SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "", S3KMSKeyID: "", S3KMSSealedKey: "", @@ -158,7 +158,7 @@ var s3ParseMetadataTests = []struct { DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, }, // 7 { - ExpectedErr: Error("The internal sealed KMS data key for SSE-S3 is invalid"), + ExpectedErr: Errorf("The internal sealed KMS data key for SSE-S3 is invalid"), Metadata: map[string]string{ SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "key-1", @@ -218,7 +218,7 @@ var ssecParseMetadataTests = []struct { {ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0 {ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, SealedKey: SealedKey{}}, // 1 { - ExpectedErr: Error("The object metadata is missing the internal sealed key for SSE-C"), + ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-C"), Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, SealedKey: SealedKey{}, }, // 2 { @@ -233,7 +233,7 @@ var ssecParseMetadataTests = []struct { SealedKey: SealedKey{}, }, // 4 { - ExpectedErr: Error("The internal sealed key for SSE-C is invalid"), + ExpectedErr: Errorf("The internal sealed key for SSE-C is invalid"), Metadata: map[string]string{ SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "", }, diff --git a/cmd/crypto/parse.go b/cmd/crypto/parse.go index 0aee4b2eb..5930d82d3 100644 --- a/cmd/crypto/parse.go +++ b/cmd/crypto/parse.go @@ -16,7 +16,6 @@ package crypto import ( "encoding/hex" - "fmt" "strings" ) @@ -25,18 +24,18 @@ import ( func ParseMasterKey(envArg string) (KMS, error) { values := strings.SplitN(envArg, ":", 2) if len(values) != 2 { - return nil, fmt.Errorf("Invalid KMS master key: %s does not contain a ':'", envArg) + return nil, Errorf("Invalid KMS master key: %s does not contain a ':'", envArg) } var ( keyID = values[0] hexKey = values[1] ) if len(hexKey) != 64 { // 2 hex bytes = 1 byte - return nil, fmt.Errorf("Invalid KMS master key: %s not a 32 bytes long HEX value", hexKey) + return nil, Errorf("Invalid KMS master key: %s not a 32 bytes long HEX value", hexKey) } var masterKey [32]byte if _, err := hex.Decode(masterKey[:], []byte(hexKey)); err != nil { - return nil, err + return nil, Errorf("Invalid KMS master key: %v", err) } return NewMasterKey(keyID, masterKey), nil } diff --git a/cmd/crypto/vault.go b/cmd/crypto/vault.go index 201beaee6..b108f68d5 100644 --- a/cmd/crypto/vault.go +++ b/cmd/crypto/vault.go @@ -17,7 +17,6 @@ package crypto import ( "bytes" "encoding/base64" - "errors" "fmt" "strings" "time" @@ -27,7 +26,7 @@ import ( var ( //ErrKMSAuthLogin is raised when there is a failure authenticating to KMS - ErrKMSAuthLogin = errors.New("Vault service did not return auth info") + ErrKMSAuthLogin = Errorf("Vault service did not return auth info") ) // VaultKey represents vault encryption key-ring. @@ -75,17 +74,17 @@ var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements func (v *VaultConfig) Verify() (err error) { switch { case v.Endpoint == "": - err = errors.New("crypto: missing hashicorp vault endpoint") + err = Errorf("crypto: missing hashicorp vault endpoint") case strings.ToLower(v.Auth.Type) != "approle": - err = fmt.Errorf("crypto: invalid hashicorp vault authentication type: %s is not supported", v.Auth.Type) + err = Errorf("crypto: invalid hashicorp vault authentication type: %s is not supported", v.Auth.Type) case v.Auth.AppRole.ID == "": - err = errors.New("crypto: missing hashicorp vault AppRole ID") + err = Errorf("crypto: missing hashicorp vault AppRole ID") case v.Auth.AppRole.Secret == "": - err = errors.New("crypto: missing hashicorp vault AppSecret ID") + err = Errorf("crypto: missing hashicorp vault AppSecret ID") case v.Key.Name == "": - err = errors.New("crypto: missing hashicorp vault key name") + err = Errorf("crypto: missing hashicorp vault key name") case v.Key.Version < 0: - err = errors.New("crypto: invalid hashicorp vault key version: The key version must not be negative") + err = Errorf("crypto: invalid hashicorp vault key version: The key version must not be negative") } return } @@ -107,7 +106,7 @@ func NewVault(config VaultConfig) (KMS, error) { } client, err := vault.NewClient(&vaultCfg) if err != nil { - return nil, err + return nil, Errorf("crypto: client error %w", err) } if config.Namespace != "" { client.SetNamespace(config.Namespace) @@ -167,6 +166,7 @@ func (v *vaultService) authenticate() (err error) { var secret *vault.Secret secret, err = v.client.Logical().Write("auth/approle/login", payload) if err != nil { + err = Errorf("crypto: client error %w", err) return } if secret == nil { @@ -217,14 +217,23 @@ func (v *vaultService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea } s, err := v.client.Logical().Write(fmt.Sprintf("/transit/datakey/plaintext/%s", keyID), payload) if err != nil { - return key, sealedKey, err + return key, sealedKey, Errorf("crypto: client error %w", err) } - sealKey := s.Data["ciphertext"].(string) - plainKey, err := base64.StdEncoding.DecodeString(s.Data["plaintext"].(string)) + sealKey, ok := s.Data["ciphertext"].(string) + if !ok { + return key, sealedKey, Errorf("crypto: incorrect 'ciphertext' key type %v", s.Data["ciphertext"]) + } + + plainKeyB64, ok := s.Data["plaintext"].(string) + if !ok { + return key, sealedKey, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"]) + } + + plainKey, err := base64.StdEncoding.DecodeString(plainKeyB64) if err != nil { - return key, sealedKey, err + return key, sealedKey, Errorf("crypto: invalid base64 key %w", err) } - copy(key[:], []byte(plainKey)) + copy(key[:], plainKey) return key, []byte(sealKey), nil } @@ -243,16 +252,22 @@ func (v *vaultService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k "ciphertext": string(sealedKey), "context": base64.StdEncoding.EncodeToString(contextStream.Bytes()), } + s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload) if err != nil { - return key, err + return key, Errorf("crypto: client error %w", err) } - base64Key := s.Data["plaintext"].(string) + + base64Key, ok := s.Data["plaintext"].(string) + if !ok { + return key, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"]) + } + plainKey, err := base64.StdEncoding.DecodeString(base64Key) if err != nil { - return key, err + return key, Errorf("crypto: invalid base64 key %w", err) } - copy(key[:], []byte(plainKey)) + copy(key[:], plainKey) return key, nil } @@ -273,7 +288,7 @@ func (v *vaultService) UpdateKey(keyID string, sealedKey []byte, ctx Context) (r } s, err := v.client.Logical().Write(fmt.Sprintf("/transit/rewrap/%s", keyID), payload) if err != nil { - return nil, err + return nil, Errorf("crypto: client error %w", err) } ciphertext, ok := s.Data["ciphertext"] if !ok { diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index f26778b40..bc07599b9 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -2026,13 +2026,20 @@ func toJSONError(ctx context.Context, err error, params ...string) (jerr *json2. // toWebAPIError - convert into error into APIError. func toWebAPIError(ctx context.Context, err error) APIError { switch err { + case errNoAuthToken: + return APIError{ + Code: "WebTokenMissing", + HTTPStatusCode: http.StatusBadRequest, + Description: err.Error(), + } case errServerNotInitialized: return APIError{ Code: "XMinioServerNotInitialized", HTTPStatusCode: http.StatusServiceUnavailable, Description: err.Error(), } - case errAuthentication, auth.ErrInvalidAccessKeyLength, auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID: + case errAuthentication, auth.ErrInvalidAccessKeyLength, + auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID: return APIError{ Code: "AccessDenied", HTTPStatusCode: http.StatusForbidden, diff --git a/pkg/iam/policy/error.go b/pkg/iam/policy/error.go index 51d56cc42..984858181 100644 --- a/pkg/iam/policy/error.go +++ b/pkg/iam/policy/error.go @@ -18,22 +18,25 @@ package iampolicy import "fmt" -// Error generic iam policy error type +// Error is the generic type for any error happening during policy +// parsing. type Error struct { - Err string + err error } // Errorf - formats according to a format specifier and returns -// the string as a value that satisfies error of type iampolicy.Error +// the string as a value that satisfies error of type policy.Error func Errorf(format string, a ...interface{}) error { - return Error{Err: fmt.Sprintf(format, a...)} + return Error{err: fmt.Errorf(format, a...)} } -// New initializes a new Error -func New(err string) error { - return Error{Err: err} -} +// Unwrap the internal error. +func (e Error) Unwrap() error { return e.err } +// Error 'error' compatible method. func (e Error) Error() string { - return e.Err + if e.err == nil { + return "iam: cause " + } + return e.err.Error() } diff --git a/pkg/iam/policy/policy.go b/pkg/iam/policy/policy.go index d85b7de24..cba9771cf 100644 --- a/pkg/iam/policy/policy.go +++ b/pkg/iam/policy/policy.go @@ -165,7 +165,7 @@ func ParseConfig(reader io.Reader) (*Policy, error) { decoder := json.NewDecoder(reader) decoder.DisallowUnknownFields() if err := decoder.Decode(&iamp); err != nil { - return nil, err + return nil, Errorf("%w", err) } return &iamp, iamp.Validate() diff --git a/pkg/policy/error.go b/pkg/policy/error.go index df1234553..3fe216235 100644 --- a/pkg/policy/error.go +++ b/pkg/policy/error.go @@ -18,22 +18,25 @@ package policy import "fmt" -// Error generic policy parse error type +// Error is the generic type for any error happening during policy +// parsing. type Error struct { - Err string + err error } // Errorf - formats according to a format specifier and returns // the string as a value that satisfies error of type policy.Error func Errorf(format string, a ...interface{}) error { - return Error{Err: fmt.Sprintf(format, a...)} + return Error{err: fmt.Errorf(format, a...)} } -// New initializes a new Error -func New(err string) error { - return Error{Err: err} -} +// Unwrap the internal error. +func (e Error) Unwrap() error { return e.err } +// Error 'error' compatible method. func (e Error) Error() string { - return e.Err + if e.err == nil { + return "policy: cause " + } + return e.err.Error() } diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 070e3465a..38d374e27 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -167,7 +167,7 @@ func ParseConfig(reader io.Reader, bucketName string) (*Policy, error) { decoder := json.NewDecoder(reader) decoder.DisallowUnknownFields() if err := decoder.Decode(&policy); err != nil { - return nil, err + return nil, Errorf("%w", err) } err := policy.Validate(bucketName)