From 2ff44df63675498888393c6110fa9942029b832f Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Wed, 2 Nov 2022 18:35:45 +0100 Subject: [PATCH] Add inspection v2 (always encrypted) option. (#2386) Depends on: https://github.com/minio/minio/pull/15474 for functionality (which depends on this) Blocks: https://github.com/minio/minio/pull/15474 Keep v1 if non-encrypted and decrypt it. Otherwise if encrypted, use a fixed public key. --- go.mod | 4 +- go.sum | 7 ++-- integration/inspect_test.go | 3 +- restapi/admin_inspect.go | 84 ++++++++++++++++++++----------------- restapi/client-admin.go | 2 +- 5 files changed, 55 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 63691bc2a..3f2de432b 100644 --- a/go.mod +++ b/go.mod @@ -23,8 +23,8 @@ require ( github.com/minio/directpv v1.4.4-0.20220805090942-948ca4731651 github.com/minio/highwayhash v1.0.2 github.com/minio/kes v0.21.1 - github.com/minio/madmin-go v1.6.6 - github.com/minio/mc v0.0.0-20221019004256-8493f97e042f + github.com/minio/madmin-go v1.7.1 + github.com/minio/mc v0.0.0-20221101010057-15e52fd862f5 github.com/minio/minio-go/v7 v7.0.41 github.com/minio/operator v0.0.0-20220902184351-21e4073132b0 github.com/minio/pkg v1.5.4 diff --git a/go.sum b/go.sum index f29267b9c..83129c1ce 100644 --- a/go.sum +++ b/go.sum @@ -711,10 +711,11 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/kes v0.21.1 h1:Af+CsnuvnOA9mGBAf05VY8ebf4vDfLDDu3uCO0VrKJU= github.com/minio/kes v0.21.1/go.mod h1:3FW1BQkMGQW78yhy+69tUq5bdcf5rnXJizyeKB9a/tc= -github.com/minio/madmin-go v1.6.6 h1:YwubKSJFeMXjW8RWOiXIhLaiTEC/rMJH2U0M57xsORA= github.com/minio/madmin-go v1.6.6/go.mod h1:ATvkBOLiP3av4D++2v1UEHC/QzsGtgXD5kYvvRYzdKs= -github.com/minio/mc v0.0.0-20221019004256-8493f97e042f h1:KCnteVGtNIDAgxYRjaxTqXmuuOjpGQiggRdckDMeWeU= -github.com/minio/mc v0.0.0-20221019004256-8493f97e042f/go.mod h1:KjPIICV48Wsziu4PceC+aiq3m8c8dbtALD6I0yEEqmw= +github.com/minio/madmin-go v1.7.1 h1:4eMyMkLSVUFZmrrwxJrYnzRimy6eCoobqijaQxzYqak= +github.com/minio/madmin-go v1.7.1/go.mod h1:3SO8SROxHN++tF6QxdTii2SSUaYSrr8lnE9EJWjvz0k= +github.com/minio/mc v0.0.0-20221101010057-15e52fd862f5 h1:+G6yvmRG5YMKx8l7LFjuXU9GhRwy0Fmaqm2SU5DZyRM= +github.com/minio/mc v0.0.0-20221101010057-15e52fd862f5/go.mod h1:wbyQ/nZAvwzk/5b+cCYolP23AhIOj8PbcX5/NuZXMug= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.41 h1:Qhc82nDRep+VSuDEPSawKUHkARnZI5st7acEqgqVX+k= diff --git a/integration/inspect_test.go b/integration/inspect_test.go index 8d9d0d71d..b8fcbe3b8 100644 --- a/integration/inspect_test.go +++ b/integration/inspect_test.go @@ -70,7 +70,8 @@ func TestInspect(t *testing.T) { file: "test.txt", encrypt: true, }, - expStatusCode: 200, + // TODO: Change back to 200 when https://github.com/minio/minio/pull/15474 is merged. + expStatusCode: 500, expectedError: false, }, { diff --git a/restapi/admin_inspect.go b/restapi/admin_inspect.go index 22f14394b..b175ddff6 100644 --- a/restapi/admin_inspect.go +++ b/restapi/admin_inspect.go @@ -17,14 +17,12 @@ package restapi import ( - "context" - "encoding/binary" - "encoding/hex" + "encoding/base64" "fmt" - "hash/crc32" "io" - "io/ioutil" "net/http" + "strings" + "unicode/utf8" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" @@ -37,28 +35,40 @@ import ( func registerInspectHandler(api *operations.ConsoleAPI) { api.InspectInspectHandler = inspectApi.InspectHandlerFunc(func(params inspectApi.InspectParams, principal *models.Principal) middleware.Responder { - k, r, err := getInspectResult(principal, ¶ms) - isEncryptOn := params.Encrypt != nil && *params.Encrypt + if v, err := base64.URLEncoding.DecodeString(params.File); err == nil && utf8.Valid(v) { + params.File = string(v) + } + if v, err := base64.URLEncoding.DecodeString(params.Volume); err == nil && utf8.Valid(v) { + params.Volume = string(v) + } + + k, r, err := getInspectResult(principal, ¶ms) if err != nil { return inspectApi.NewInspectDefault(int(err.Code)).WithPayload(err) } - return middleware.ResponderFunc(processInspectResponse(isEncryptOn, k, r)) + return middleware.ResponderFunc(processInspectResponse(¶ms, k, r)) }) } -func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) (*[32]byte, io.ReadCloser, *models.Error) { - ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) - defer cancel() +func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) ([]byte, io.ReadCloser, *models.Error) { + ctx := params.HTTPRequest.Context() mAdmin, err := NewMinioAdminClient(session) if err != nil { return nil, nil, ErrorWithContext(ctx, err) } - var cfg madmin.InspectOptions - cfg.File = params.File - cfg.Volume = params.Volume + cfg := madmin.InspectOptions{ + File: params.File, + Volume: params.Volume, + } + + // TODO: Remove encryption option and always encrypt. + // Maybe also add public key field. + if params.Encrypt != nil && *params.Encrypt { + cfg.PublicKey, _ = base64.StdEncoding.DecodeString("MIIBCgKCAQEAs/128UFS9A8YSJY1XqYKt06dLVQQCGDee69T+0Tip/1jGAB4z0/3QMpH0MiS8Wjs4BRWV51qvkfAHzwwdU7y6jxU05ctb/H/WzRj3FYdhhHKdzear9TLJftlTs+xwj2XaADjbLXCV1jGLS889A7f7z5DgABlVZMQd9BjVAR8ED3xRJ2/ZCNuQVJ+A8r7TYPGMY3wWvhhPgPk3Lx4WDZxDiDNlFs4GQSaESSsiVTb9vyGe/94CsCTM6Cw9QG6ifHKCa/rFszPYdKCabAfHcS3eTr0GM+TThSsxO7KfuscbmLJkfQev1srfL2Ii2RbnysqIJVWKEwdW05ID8ryPkuTuwIDAQAB") + } // create a MinIO Admin Client interface implementation // defining the client to be used @@ -68,45 +78,43 @@ func getInspectResult(session *models.Principal, params *inspectApi.InspectParam if err != nil { return nil, nil, ErrorWithContext(ctx, err) } - return &k, r, nil + return k, r, nil } // borrowed from mc cli -func decryptInspect(key [32]byte, r io.Reader) io.ReadCloser { +func decryptInspectV1(key [32]byte, r io.Reader) io.ReadCloser { stream, err := sio.AES_256_GCM.Stream(key[:]) if err != nil { return nil } nonce := make([]byte, stream.NonceSize()) - return ioutil.NopCloser(stream.DecryptReader(r, nonce, nil)) + return io.NopCloser(stream.DecryptReader(r, nonce, nil)) } -func processInspectResponse(isEnc bool, k *[32]byte, r io.ReadCloser) func(w http.ResponseWriter, _ runtime.Producer) { +func processInspectResponse(params *inspectApi.InspectParams, k []byte, r io.ReadCloser) func(w http.ResponseWriter, _ runtime.Producer) { + isEnc := params.Encrypt != nil && *params.Encrypt return func(w http.ResponseWriter, _ runtime.Producer) { - var id [4]byte - binary.LittleEndian.PutUint32(id[:], crc32.ChecksumIEEE(k[:])) - defer r.Close() - ext := "enc" - if !isEnc { + if len(k) == 32 && !isEnc { ext = "zip" - r = decryptInspect(*k, r) + r = decryptInspectV1(*(*[32]byte)(k), r) } - - fileName := fmt.Sprintf("inspect.%s.%s", hex.EncodeToString(id[:]), ext) - - if isEnc { - // use cookie to transmit the Decryption Key. - hexKey := hex.EncodeToString(id[:]) + hex.EncodeToString(k[:]) - cookie := http.Cookie{ - Name: fileName, - Value: hexKey, - Path: "/", - MaxAge: 3000, + fileName := fmt.Sprintf("inspect-%s-%s.%s", params.Volume, params.File, ext) + fileName = strings.Map(func(r rune) rune { + switch { + case r >= 'A' && r <= 'Z': + return r + case r >= 'a' && r <= 'z': + return r + case r >= '0' && r <= '9': + return r + default: + if strings.ContainsAny(string(r), "-+._") { + return r + } + return '_' } - http.SetCookie(w, &cookie) - } - + }, fileName) w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName)) diff --git a/restapi/client-admin.go b/restapi/client-admin.go index de0164b9a..96e373825 100644 --- a/restapi/client-admin.go +++ b/restapi/client-admin.go @@ -453,7 +453,7 @@ func (ac AdminClient) addTier(ctx context.Context, cfg *madmin.TierConfig) error } // implements madmin.Inspect() -func (ac AdminClient) inspect(ctx context.Context, insOpts madmin.InspectOptions) ([32]byte, io.ReadCloser, error) { +func (ac AdminClient) inspect(ctx context.Context, insOpts madmin.InspectOptions) ([]byte, io.ReadCloser, error) { return ac.Client.Inspect(ctx, insOpts) }