mirror of https://github.com/minio/minio.git
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.
This commit is contained in:
parent
796cca4166
commit
933c60bc3a
|
@ -12,7 +12,7 @@ RUN \
|
||||||
git clone https://github.com/minio/minio && cd minio && \
|
git clone https://github.com/minio/minio && cd minio && \
|
||||||
go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)"
|
go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)"
|
||||||
|
|
||||||
FROM alpine:3.9
|
FROM alpine:3.10
|
||||||
|
|
||||||
ENV MINIO_UPDATE off
|
ENV MINIO_UPDATE off
|
||||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.9
|
FROM alpine:3.10
|
||||||
|
|
||||||
LABEL maintainer="MinIO Inc <dev@min.io>"
|
LABEL maintainer="MinIO Inc <dev@min.io>"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ RUN \
|
||||||
apk add --no-cache git && \
|
apk add --no-cache git && \
|
||||||
git clone https://github.com/minio/minio
|
git clone https://github.com/minio/minio
|
||||||
|
|
||||||
FROM alpine:3.9
|
FROM alpine:3.10
|
||||||
|
|
||||||
LABEL maintainer="MinIO Inc <dev@min.io>"
|
LABEL maintainer="MinIO Inc <dev@min.io>"
|
||||||
|
|
||||||
|
|
|
@ -1727,8 +1727,6 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
|
||||||
apiErr = ErrUnsupportedNotification
|
apiErr = ErrUnsupportedNotification
|
||||||
case BackendDown:
|
case BackendDown:
|
||||||
apiErr = ErrBackendDown
|
apiErr = ErrBackendDown
|
||||||
case crypto.Error:
|
|
||||||
apiErr = ErrObjectTampered
|
|
||||||
case ObjectNameTooLong:
|
case ObjectNameTooLong:
|
||||||
apiErr = ErrKeyTooLongError
|
apiErr = ErrKeyTooLongError
|
||||||
default:
|
default:
|
||||||
|
@ -1781,7 +1779,7 @@ func toAPIError(ctx context.Context, err error) APIError {
|
||||||
}
|
}
|
||||||
case crypto.Error:
|
case crypto.Error:
|
||||||
apiErr = APIError{
|
apiErr = APIError{
|
||||||
Code: "XKMSInternalError",
|
Code: "XMinIOEncryptionError",
|
||||||
Description: e.Error(),
|
Description: e.Error(),
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -342,7 +341,7 @@ func LookupVaultConfig(kvs config.KVS) (VaultConfig, error) {
|
||||||
if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" {
|
if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" {
|
||||||
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
|
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vcfg, fmt.Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion)
|
return vcfg, Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,60 +14,79 @@
|
||||||
|
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// Error is the generic type for any error happening during decrypting
|
// Error is the generic type for any error happening during decrypting
|
||||||
// an object. It indicates that the object itself or its metadata was
|
// an object. It indicates that the object itself or its metadata was
|
||||||
// modified accidentally or maliciously.
|
// 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 <nil>"
|
||||||
|
}
|
||||||
|
return e.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrInvalidEncryptionMethod indicates that the specified SSE encryption method
|
// ErrInvalidEncryptionMethod indicates that the specified SSE encryption method
|
||||||
// is not supported.
|
// 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
|
// ErrInvalidCustomerAlgorithm indicates that the specified SSE-C algorithm
|
||||||
// is not supported.
|
// 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 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
|
// ErrMissingCustomerKeyMD5 indicates that the HTTP headers contains no SSE-C client key
|
||||||
// MD5 checksum.
|
// 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
|
// ErrInvalidCustomerKey indicates that the SSE-C client key is not valid - e.g. not a
|
||||||
// base64-encoded string or not 256 bits long.
|
// 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)
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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 (
|
var (
|
||||||
errMissingInternalIV Error = "The object metadata is missing the internal encryption IV"
|
errMissingInternalIV = Errorf("The object metadata is missing the internal encryption IV")
|
||||||
errMissingInternalSealAlgorithm Error = "The object metadata is missing the internal seal algorithm"
|
errMissingInternalSealAlgorithm = Errorf("The object metadata is missing the internal seal algorithm")
|
||||||
|
|
||||||
errInvalidInternalIV Error = "The internal encryption IV is malformed"
|
errInvalidInternalIV = Errorf("The internal encryption IV is malformed")
|
||||||
errInvalidInternalSealAlgorithm Error = "The internal seal algorithm is invalid and not supported"
|
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 (
|
var (
|
||||||
// errOutOfEntropy indicates that the a source of randomness (PRNG) wasn't able
|
// 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.
|
// 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")
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -76,13 +75,13 @@ type KesConfig struct {
|
||||||
func (k KesConfig) Verify() (err error) {
|
func (k KesConfig) Verify() (err error) {
|
||||||
switch {
|
switch {
|
||||||
case k.Endpoint == "":
|
case k.Endpoint == "":
|
||||||
err = errors.New("crypto: missing kes endpoint")
|
err = Errorf("crypto: missing kes endpoint")
|
||||||
case k.CertFile == "":
|
case k.CertFile == "":
|
||||||
err = errors.New("crypto: missing cert file")
|
err = Errorf("crypto: missing cert file")
|
||||||
case k.KeyFile == "":
|
case k.KeyFile == "":
|
||||||
err = errors.New("crypto: missing key file")
|
err = Errorf("crypto: missing key file")
|
||||||
case k.DefaultKeyID == "":
|
case k.DefaultKeyID == "":
|
||||||
err = errors.New("crypto: missing default key id")
|
err = Errorf("crypto: missing default key id")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -153,7 +152,7 @@ func (kes *kesService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea
|
||||||
return key, nil, err
|
return key, nil, err
|
||||||
}
|
}
|
||||||
if len(plainKey) != len(key) {
|
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)
|
copy(key[:], plainKey)
|
||||||
return key, sealedKey, nil
|
return key, sealedKey, nil
|
||||||
|
@ -176,7 +175,7 @@ func (kes *kesService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
if len(plainKey) != len(key) {
|
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)
|
copy(key[:], plainKey)
|
||||||
return key, nil
|
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 {
|
if _, err := io.Copy(&errMsg, io.LimitReader(resp.Body, limit)); err != nil {
|
||||||
return err
|
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
|
// loadCACertificates returns a new CertPool
|
||||||
|
@ -334,7 +333,7 @@ func loadCACertificates(path string) (*x509.CertPool, error) {
|
||||||
if os.IsNotExist(err) || os.IsPermission(err) {
|
if os.IsNotExist(err) || os.IsPermission(err) {
|
||||||
return rootCAs, nil
|
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
|
// If path is a file, parse as PEM-encoded certifcate
|
||||||
|
@ -346,7 +345,7 @@ func loadCACertificates(path string) (*x509.CertPool, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !rootCAs.AppendCertsFromPEM(cert) {
|
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
|
return rootCAs, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ func (key *ObjectKey) Unseal(extKey [32]byte, sealedKey SealedKey, domain, bucke
|
||||||
)
|
)
|
||||||
switch sealedKey.Algorithm {
|
switch sealedKey.Algorithm {
|
||||||
default:
|
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:
|
case SealAlgorithm:
|
||||||
mac := hmac.New(sha256.New, extKey[:])
|
mac := hmac.New(sha256.New, extKey[:])
|
||||||
mac.Write(sealedKey.IV[:])
|
mac.Write(sealedKey.IV[:])
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
@ -167,7 +166,7 @@ func lookupConfigLegacy(kvs config.KVS) (VaultConfig, error) {
|
||||||
if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" {
|
if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" {
|
||||||
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
|
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
|
||||||
if err != nil {
|
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)
|
EnvLegacyVaultKeyVersion, keyVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
)
|
)
|
||||||
|
@ -126,7 +125,7 @@ func CreateMultipartMetadata(metadata map[string]string) map[string]string {
|
||||||
// is nil.
|
// is nil.
|
||||||
func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
|
func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
|
||||||
if sealedKey.Algorithm != SealAlgorithm {
|
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:
|
// There are two possibilites:
|
||||||
|
@ -172,7 +171,7 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
|
||||||
}
|
}
|
||||||
b64SealedKey, ok := metadata[S3SealedKey]
|
b64SealedKey, ok := metadata[S3SealedKey]
|
||||||
if !ok {
|
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:
|
// There are two possibilites:
|
||||||
|
@ -182,10 +181,10 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
|
||||||
keyID, idPresent := metadata[S3KMSKeyID]
|
keyID, idPresent := metadata[S3KMSKeyID]
|
||||||
b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey]
|
b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey]
|
||||||
if !idPresent && kmsKeyPresent {
|
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 {
|
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
|
// 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)
|
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
||||||
if err != nil || len(encryptedKey) != 64 {
|
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.
|
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
|
||||||
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
||||||
if err != nil {
|
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.
|
// It allocates a new metadata map if metadata is nil.
|
||||||
func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string {
|
func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string {
|
||||||
if sealedKey.Algorithm != SealAlgorithm {
|
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 {
|
if metadata == nil {
|
||||||
|
@ -244,7 +243,7 @@ func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err
|
||||||
}
|
}
|
||||||
b64SealedKey, ok := metadata[SSECSealedKey]
|
b64SealedKey, ok := metadata[SSECSealedKey]
|
||||||
if !ok {
|
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
|
// 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)
|
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
||||||
if err != nil || len(encryptedKey) != 64 {
|
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
|
sealedKey.Algorithm = algorithm
|
||||||
|
|
|
@ -125,15 +125,15 @@ var s3ParseMetadataTests = []struct {
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 1
|
}, // 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{},
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 2
|
}, // 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{},
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSSealedKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 3
|
}, // 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: ""},
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: ""},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 4
|
}, // 4
|
||||||
|
@ -150,7 +150,7 @@ var s3ParseMetadataTests = []struct {
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 6
|
}, // 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{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "",
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "",
|
||||||
S3KMSKeyID: "", S3KMSSealedKey: "",
|
S3KMSKeyID: "", S3KMSSealedKey: "",
|
||||||
|
@ -158,7 +158,7 @@ var s3ParseMetadataTests = []struct {
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 7
|
}, // 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{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm,
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm,
|
||||||
S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "key-1",
|
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: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0
|
||||||
{ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, SealedKey: SealedKey{}}, // 1
|
{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{},
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, SealedKey: SealedKey{},
|
||||||
}, // 2
|
}, // 2
|
||||||
{
|
{
|
||||||
|
@ -233,7 +233,7 @@ var ssecParseMetadataTests = []struct {
|
||||||
SealedKey: SealedKey{},
|
SealedKey: SealedKey{},
|
||||||
}, // 4
|
}, // 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{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "",
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "",
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,6 @@ package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,18 +24,18 @@ import (
|
||||||
func ParseMasterKey(envArg string) (KMS, error) {
|
func ParseMasterKey(envArg string) (KMS, error) {
|
||||||
values := strings.SplitN(envArg, ":", 2)
|
values := strings.SplitN(envArg, ":", 2)
|
||||||
if len(values) != 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 (
|
var (
|
||||||
keyID = values[0]
|
keyID = values[0]
|
||||||
hexKey = values[1]
|
hexKey = values[1]
|
||||||
)
|
)
|
||||||
if len(hexKey) != 64 { // 2 hex bytes = 1 byte
|
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
|
var masterKey [32]byte
|
||||||
if _, err := hex.Decode(masterKey[:], []byte(hexKey)); err != nil {
|
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
|
return NewMasterKey(keyID, masterKey), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -27,7 +26,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//ErrKMSAuthLogin is raised when there is a failure authenticating to KMS
|
//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.
|
// 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) {
|
func (v *VaultConfig) Verify() (err error) {
|
||||||
switch {
|
switch {
|
||||||
case v.Endpoint == "":
|
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":
|
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 == "":
|
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 == "":
|
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 == "":
|
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:
|
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
|
return
|
||||||
}
|
}
|
||||||
|
@ -107,7 +106,7 @@ func NewVault(config VaultConfig) (KMS, error) {
|
||||||
}
|
}
|
||||||
client, err := vault.NewClient(&vaultCfg)
|
client, err := vault.NewClient(&vaultCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, Errorf("crypto: client error %w", err)
|
||||||
}
|
}
|
||||||
if config.Namespace != "" {
|
if config.Namespace != "" {
|
||||||
client.SetNamespace(config.Namespace)
|
client.SetNamespace(config.Namespace)
|
||||||
|
@ -167,6 +166,7 @@ func (v *vaultService) authenticate() (err error) {
|
||||||
var secret *vault.Secret
|
var secret *vault.Secret
|
||||||
secret, err = v.client.Logical().Write("auth/approle/login", payload)
|
secret, err = v.client.Logical().Write("auth/approle/login", payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = Errorf("crypto: client error %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if secret == nil {
|
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)
|
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/datakey/plaintext/%s", keyID), payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key, sealedKey, err
|
return key, sealedKey, Errorf("crypto: client error %w", err)
|
||||||
}
|
}
|
||||||
sealKey := s.Data["ciphertext"].(string)
|
sealKey, ok := s.Data["ciphertext"].(string)
|
||||||
plainKey, err := base64.StdEncoding.DecodeString(s.Data["plaintext"].(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 {
|
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
|
return key, []byte(sealKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,16 +252,22 @@ func (v *vaultService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k
|
||||||
"ciphertext": string(sealedKey),
|
"ciphertext": string(sealedKey),
|
||||||
"context": base64.StdEncoding.EncodeToString(contextStream.Bytes()),
|
"context": base64.StdEncoding.EncodeToString(contextStream.Bytes()),
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload)
|
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload)
|
||||||
if err != nil {
|
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)
|
plainKey, err := base64.StdEncoding.DecodeString(base64Key)
|
||||||
if err != nil {
|
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
|
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)
|
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/rewrap/%s", keyID), payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, Errorf("crypto: client error %w", err)
|
||||||
}
|
}
|
||||||
ciphertext, ok := s.Data["ciphertext"]
|
ciphertext, ok := s.Data["ciphertext"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -2026,13 +2026,20 @@ func toJSONError(ctx context.Context, err error, params ...string) (jerr *json2.
|
||||||
// toWebAPIError - convert into error into APIError.
|
// toWebAPIError - convert into error into APIError.
|
||||||
func toWebAPIError(ctx context.Context, err error) APIError {
|
func toWebAPIError(ctx context.Context, err error) APIError {
|
||||||
switch err {
|
switch err {
|
||||||
|
case errNoAuthToken:
|
||||||
|
return APIError{
|
||||||
|
Code: "WebTokenMissing",
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
Description: err.Error(),
|
||||||
|
}
|
||||||
case errServerNotInitialized:
|
case errServerNotInitialized:
|
||||||
return APIError{
|
return APIError{
|
||||||
Code: "XMinioServerNotInitialized",
|
Code: "XMinioServerNotInitialized",
|
||||||
HTTPStatusCode: http.StatusServiceUnavailable,
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
||||||
Description: err.Error(),
|
Description: err.Error(),
|
||||||
}
|
}
|
||||||
case errAuthentication, auth.ErrInvalidAccessKeyLength, auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID:
|
case errAuthentication, auth.ErrInvalidAccessKeyLength,
|
||||||
|
auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID:
|
||||||
return APIError{
|
return APIError{
|
||||||
Code: "AccessDenied",
|
Code: "AccessDenied",
|
||||||
HTTPStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
|
|
|
@ -18,22 +18,25 @@ package iampolicy
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Error generic iam policy error type
|
// Error is the generic type for any error happening during policy
|
||||||
|
// parsing.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Err string
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf - formats according to a format specifier and returns
|
// 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 {
|
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
|
// Unwrap the internal error.
|
||||||
func New(err string) error {
|
func (e Error) Unwrap() error { return e.err }
|
||||||
return Error{Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Error 'error' compatible method.
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
return e.Err
|
if e.err == nil {
|
||||||
|
return "iam: cause <nil>"
|
||||||
|
}
|
||||||
|
return e.err.Error()
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ func ParseConfig(reader io.Reader) (*Policy, error) {
|
||||||
decoder := json.NewDecoder(reader)
|
decoder := json.NewDecoder(reader)
|
||||||
decoder.DisallowUnknownFields()
|
decoder.DisallowUnknownFields()
|
||||||
if err := decoder.Decode(&iamp); err != nil {
|
if err := decoder.Decode(&iamp); err != nil {
|
||||||
return nil, err
|
return nil, Errorf("%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &iamp, iamp.Validate()
|
return &iamp, iamp.Validate()
|
||||||
|
|
|
@ -18,22 +18,25 @@ package policy
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Error generic policy parse error type
|
// Error is the generic type for any error happening during policy
|
||||||
|
// parsing.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Err string
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf - formats according to a format specifier and returns
|
// Errorf - formats according to a format specifier and returns
|
||||||
// the string as a value that satisfies error of type policy.Error
|
// the string as a value that satisfies error of type policy.Error
|
||||||
func Errorf(format string, a ...interface{}) 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
|
// Unwrap the internal error.
|
||||||
func New(err string) error {
|
func (e Error) Unwrap() error { return e.err }
|
||||||
return Error{Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Error 'error' compatible method.
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
return e.Err
|
if e.err == nil {
|
||||||
|
return "policy: cause <nil>"
|
||||||
|
}
|
||||||
|
return e.err.Error()
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ func ParseConfig(reader io.Reader, bucketName string) (*Policy, error) {
|
||||||
decoder := json.NewDecoder(reader)
|
decoder := json.NewDecoder(reader)
|
||||||
decoder.DisallowUnknownFields()
|
decoder.DisallowUnknownFields()
|
||||||
if err := decoder.Decode(&policy); err != nil {
|
if err := decoder.Decode(&policy); err != nil {
|
||||||
return nil, err
|
return nil, Errorf("%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := policy.Validate(bucketName)
|
err := policy.Validate(bucketName)
|
||||||
|
|
Loading…
Reference in New Issue