[#562] Add tests for form encryption params
Signed-off-by: Roman Loginov <r.loginov@yadro.com>
This commit is contained in:
parent
a5614278a7
commit
d6c451c782
7 changed files with 196 additions and 15 deletions
|
@ -381,7 +381,7 @@ func (h *handler) formEncryptionParamsBase(r *http.Request, isCopySource bool) (
|
|||
if tlsTerminationStr := r.Header.Get(h.cfg.TLSTerminationHeader()); len(tlsTerminationStr) > 0 {
|
||||
tlsTermination, err := strconv.ParseBool(tlsTerminationStr)
|
||||
if err != nil {
|
||||
h.reqLogger(r.Context()).Warn(logs.WarnInvalidTypeTLSTerminationHeader, zap.Error(err))
|
||||
h.reqLogger(r.Context()).Warn(logs.WarnInvalidTypeTLSTerminationHeader, zap.String("header", tlsTerminationStr), zap.Error(err))
|
||||
} else {
|
||||
needCheckTLS = !tlsTermination
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
@ -634,6 +635,186 @@ func TestPutObjectWithContentLanguage(t *testing.T) {
|
|||
require.Equal(t, expectedContentLanguage, w.Header().Get(api.ContentLanguage))
|
||||
}
|
||||
|
||||
func TestFormEncryptionParamsBase(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
|
||||
userSecret := "test1customer2secret3with32char4"
|
||||
expectedEncKey := []byte(userSecret)
|
||||
emptyEncKey := []byte(nil)
|
||||
|
||||
validAlgo := "AES256"
|
||||
validKey := "dGVzdDFjdXN0b21lcjJzZWNyZXQzd2l0aDMyY2hhcjQ="
|
||||
validMD5 := "zcQmPqFhtJaxkOIg5tXm9g=="
|
||||
|
||||
invalidAlgo := "TTT111"
|
||||
invalidKeyBase64 := "dGVzdDFjdXN0b21lcjJzZWNyZXQzd2l0aDMyY2hhcjQ"
|
||||
invalidKeySize := "dGVzdDFjdXN0b21lcjJzZWNyZXQzd2l0aA=="
|
||||
invalidMD5Base64 := "zcQmPqFhtJaxkOIg5tXm9g"
|
||||
invalidMD5 := "zcQmPqPhtJaxkOIg5tXm9g=="
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
algo string
|
||||
key string
|
||||
md5 string
|
||||
tlsTermination string
|
||||
reqWithoutTLS bool
|
||||
reqWithoutSSE bool
|
||||
isCopySource bool
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "valid requst copy source",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
isCopySource: true,
|
||||
},
|
||||
{
|
||||
name: "valid request with TLS",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
},
|
||||
{
|
||||
name: "valid request without TLS and valid termination header",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
tlsTermination: "true",
|
||||
reqWithoutTLS: true,
|
||||
},
|
||||
{
|
||||
name: "request without tls and termination header",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
reqWithoutTLS: true,
|
||||
err: apierr.GetAPIError(apierr.ErrInsecureSSECustomerRequest),
|
||||
},
|
||||
{
|
||||
name: "request without tls and invalid header",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
tlsTermination: "invalid",
|
||||
reqWithoutTLS: true,
|
||||
err: apierr.GetAPIError(apierr.ErrInsecureSSECustomerRequest),
|
||||
},
|
||||
{
|
||||
name: "missing SSE customer algorithm",
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
err: apierr.GetAPIError(apierr.ErrMissingSSECustomerAlgorithm),
|
||||
},
|
||||
{
|
||||
name: "missing SSE customer key",
|
||||
algo: validAlgo,
|
||||
md5: validMD5,
|
||||
err: apierr.GetAPIError(apierr.ErrMissingSSECustomerKey),
|
||||
},
|
||||
{
|
||||
name: "invalid encryption algorithm",
|
||||
algo: invalidAlgo,
|
||||
key: validKey,
|
||||
md5: validMD5,
|
||||
err: apierr.GetAPIError(apierr.ErrInvalidEncryptionAlgorithm),
|
||||
},
|
||||
{
|
||||
name: "invalid base64 SSE customer key",
|
||||
algo: validAlgo,
|
||||
key: invalidKeyBase64,
|
||||
md5: validMD5,
|
||||
err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerKey),
|
||||
},
|
||||
{
|
||||
name: "invalid base64 SSE customer parameters",
|
||||
algo: validAlgo,
|
||||
key: invalidKeyBase64,
|
||||
md5: validMD5,
|
||||
isCopySource: true,
|
||||
err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerParameters),
|
||||
},
|
||||
{
|
||||
name: "invalid size of custom key",
|
||||
algo: validAlgo,
|
||||
key: invalidKeySize,
|
||||
md5: validMD5,
|
||||
err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerKey),
|
||||
},
|
||||
{
|
||||
name: "invalid size of custom key - copy source",
|
||||
algo: validAlgo,
|
||||
key: invalidKeySize,
|
||||
md5: validMD5,
|
||||
isCopySource: true,
|
||||
err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerParameters),
|
||||
},
|
||||
{
|
||||
name: "invalid base64 key md5 of customer",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: invalidMD5Base64,
|
||||
err: apierr.GetAPIError(apierr.ErrSSECustomerKeyMD5Mismatch),
|
||||
},
|
||||
{
|
||||
name: "invalid md5 sum key of customer",
|
||||
algo: validAlgo,
|
||||
key: validKey,
|
||||
md5: invalidMD5,
|
||||
err: apierr.GetAPIError(apierr.ErrSSECustomerKeyMD5Mismatch),
|
||||
},
|
||||
{
|
||||
name: "request without sse",
|
||||
reqWithoutSSE: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := prepareRequestForEncryption(tc.algo, tc.key, tc.md5, tc.tlsTermination, tc.reqWithoutTLS, tc.reqWithoutSSE, tc.isCopySource)
|
||||
|
||||
enc, err := hc.h.formEncryptionParamsBase(r, tc.isCopySource)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, tc.err, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.reqWithoutSSE {
|
||||
require.Equal(t, emptyEncKey, enc.Key())
|
||||
} else {
|
||||
require.Equal(t, expectedEncKey, enc.Key())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepareRequestForEncryption(algo, key, md5, tlsTermination string, reqWithoutTLS, reqWithoutSSE, isCopySource bool) *http.Request {
|
||||
r := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
|
||||
if !reqWithoutTLS {
|
||||
r.TLS = &tls.ConnectionState{}
|
||||
}
|
||||
|
||||
if !reqWithoutSSE {
|
||||
if isCopySource {
|
||||
r.Header.Set(api.AmzCopySourceServerSideEncryptionCustomerAlgorithm, algo)
|
||||
r.Header.Set(api.AmzCopySourceServerSideEncryptionCustomerKey, key)
|
||||
r.Header.Set(api.AmzCopySourceServerSideEncryptionCustomerKeyMD5, md5)
|
||||
} else {
|
||||
r.Header.Set(api.AmzServerSideEncryptionCustomerAlgorithm, algo)
|
||||
r.Header.Set(api.AmzServerSideEncryptionCustomerKey, key)
|
||||
r.Header.Set(api.AmzServerSideEncryptionCustomerKeyMD5, md5)
|
||||
}
|
||||
}
|
||||
|
||||
if tlsTermination != "" {
|
||||
r.Header.Set("X-Frostfs-TLS-Termination", tlsTermination)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func postObjectBase(hc *handlerContext, ns, bktName, key, filename, content string) *httptest.ResponseRecorder {
|
||||
policy := "eyJleHBpcmF0aW9uIjogIjIwMjUtMTItMDFUMTI6MDA6MDAuMDAwWiIsImNvbmRpdGlvbnMiOiBbCiBbInN0YXJ0cy13aXRoIiwgIiR4LWFtei1jcmVkZW50aWFsIiwgIiJdLAogWyJzdGFydHMtd2l0aCIsICIkeC1hbXotZGF0ZSIsICIiXSwKIFsic3RhcnRzLXdpdGgiLCAiJGtleSIsICIiXQpdfQ=="
|
||||
|
||||
|
|
|
@ -317,7 +317,7 @@ func (s *appSettings) update(v *viper.Viper, log *zap.Logger) {
|
|||
httpLoggingUseGzip := v.GetBool(cfgHTTPLoggingGzip)
|
||||
tombstoneMembersSize := fetchTombstoneMembersSize(v)
|
||||
tombstoneLifetime := fetchTombstoneLifetime(v)
|
||||
tlsTerminationHeader := v.GetString(cfgSSECTLSTerminationHeader)
|
||||
tlsTerminationHeader := v.GetString(cfgEncryptionTLSTerminationHeader)
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
|
|
@ -281,8 +281,8 @@ const ( // Settings.
|
|||
// Server.
|
||||
cfgReconnectInterval = "reconnect_interval"
|
||||
|
||||
// SSE-C.
|
||||
cfgSSECTLSTerminationHeader = "sse_c.tls_termination_header"
|
||||
// Encryption.
|
||||
cfgEncryptionTLSTerminationHeader = "encryption.tls_termination_header"
|
||||
|
||||
// envPrefix is an environment variables prefix used for configuration.
|
||||
envPrefix = "S3_GW"
|
||||
|
@ -950,8 +950,8 @@ func newSettings() *viper.Viper {
|
|||
// multinet
|
||||
v.SetDefault(cfgMultinetFallbackDelay, defaultMultinetFallbackDelay)
|
||||
|
||||
// sse-c
|
||||
v.SetDefault(cfgSSECTLSTerminationHeader, defaultTLSTerminationHeader)
|
||||
// encryption
|
||||
v.SetDefault(cfgEncryptionTLSTerminationHeader, defaultTLSTerminationHeader)
|
||||
|
||||
// Bind flags
|
||||
if err := bindFlags(v, flags); err != nil {
|
||||
|
|
|
@ -271,4 +271,4 @@ S3_GW_MULTINET_SUBNETS_1_MASK=1.2.3.4/24
|
|||
S3_GW_MULTINET_SUBNETS_1_SOURCE_IPS=1.2.3.4 1.2.3.5
|
||||
|
||||
# Header for determining the termination of TLS.
|
||||
S3_GW_SSE_C_TLS_TERMINATION_TLS_HEADER=X-Frostfs-TLS-Termination
|
||||
S3_GW_ENCRYPTION_TLS_TERMINATION_TLS_HEADER=X-Frostfs-TLS-Termination
|
||||
|
|
|
@ -319,5 +319,5 @@ multinet:
|
|||
- 1.2.3.4
|
||||
- 1.2.3.5
|
||||
|
||||
sse_c:
|
||||
encryption:
|
||||
tls_termination_header: X-Frostfs-TLS-Termination
|
||||
|
|
|
@ -196,7 +196,7 @@ There are some custom types used for brevity:
|
|||
| `containers` | [Containers configuration](#containers-section) |
|
||||
| `vhs` | [VHS configuration](#vhs-section) |
|
||||
| `multinet` | [Multinet configuration](#multinet-section) |
|
||||
| `sse_c` | [SSE-C configuration](#sse_c-section) |
|
||||
| `encryption` | [Encryption configuration](#encryption-section) |
|
||||
|
||||
### General section
|
||||
|
||||
|
@ -860,15 +860,15 @@ multinet:
|
|||
| `mask` | `string` | yes | | Destination subnet. |
|
||||
| `source_ips` | `[]string` | yes | | Array of source IP addresses to use when dialing destination subnet. |
|
||||
|
||||
# `sse_c` section
|
||||
# `encryption` section
|
||||
|
||||
Configuration of SSE-C.
|
||||
Configuration of encryption.
|
||||
|
||||
```yaml
|
||||
sse_c:
|
||||
encryption:
|
||||
tls_termination_header: X-Frostfs-TLS-Termination
|
||||
```
|
||||
|
||||
| Parameter | Type | SIGHUP reload | Default value | Description |
|
||||
| ------------------------ | -------- | ------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tls_termination_header` | `string` | yes | `X-Frostfs-TLS-Termination` | The header for determining whether TLS needs to be checked. If the system requests come through a proxy server and TLS can terminate at the proxy level, you should use this header to disable TLS verification at SSE-C. |
|
||||
| Parameter | Type | SIGHUP reload | Default value | Description |
|
||||
|--------------------------|----------|---------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `tls_termination_header` | `string` | yes | `X-Frostfs-TLS-Termination` | The header for determining whether TLS needs to be checked. If the system requests come through a proxy server and TLS can terminate at the proxy level, you should use this header to disable TLS verification at server-side encryption. |
|
||||
|
|
Loading…
Reference in a new issue