-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This change adds non-functional tests to check whether a minio endpoint (TLS) is configured properly. This includes: - SSL/TLS version checks - Cipher suite checks To separate TLS tests from functional tests this change adds a new subdirectory `/run/tls`. Fixes #253
- Loading branch information
Andreas Auernhammer
committed
Jan 22, 2018
1 parent
a2e835b
commit 3fee4f9
Showing
6 changed files
with
314 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#!/bin/bash -e | ||
# | ||
# Mint (C) 2018 Minio, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
test_run_dir="$MINT_RUN_CORE_DIR/security" | ||
go get -u github.com/sirupsen/logrus/... | ||
go build -o "$test_run_dir/tls-tests" "$test_run_dir/tls-tests.go" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#!/bin/bash | ||
# | ||
# Mint (C) 2018 Minio, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
# handle command line arguments | ||
if [ $# -ne 2 ]; then | ||
echo "usage: run.sh <OUTPUT-LOG-FILE> <ERROR-LOG-FILE>" | ||
exit -1 | ||
fi | ||
|
||
output_log_file="$1" | ||
error_log_file="$2" | ||
|
||
# run tests | ||
/mint/run/core/security/tls-tests 1>>"$output_log_file" 2>"$error_log_file" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
// Mint, (C) 2018 Minio, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
|
||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package main | ||
|
||
import ( | ||
"crypto/tls" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"time" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const testName = "TLS-tests" | ||
|
||
const ( | ||
// PASS indicate that a test passed | ||
PASS = "PASS" | ||
// FAIL indicate that a test failed | ||
FAIL = "FAIL" | ||
// NA indicates that a test is not applicable | ||
NA = "NA" | ||
) | ||
|
||
func main() { | ||
log.SetOutput(os.Stdout) | ||
log.SetFormatter(&mintJSONFormatter{}) | ||
log.SetLevel(log.InfoLevel) | ||
|
||
endpoint := os.Getenv("SERVER_ENDPOINT") | ||
secure := os.Getenv("ENABLE_HTTPS") | ||
if secure != "1" { | ||
log.WithFields(log.Fields{"name:": testName, "status": NA, "message": "TLS is not enabled"}).Info() | ||
return | ||
} | ||
|
||
testTLSVersions(endpoint) | ||
testTLSCiphers(endpoint) | ||
} | ||
|
||
// Tests whether the endpoint accepts SSL3.0, TLS1.0 or TLS1.1 connections - fail if so. | ||
// Tests whether the endpoint accepts TLS1.2 connections - fail if not. | ||
func testTLSVersions(endpoint string) { | ||
const function = "TLSVersions" | ||
startTime := time.Now() | ||
|
||
// Tests whether the endpoint accepts SSL3.0, TLS1.0 or TLS1.1 connections | ||
args := map[string]interface{}{ | ||
"MinVersion": "tls.VersionSSL30", | ||
"MaxVersion": "tls.VersionTLS11", | ||
} | ||
_, err := tls.Dial("tcp", endpoint, &tls.Config{ | ||
MinVersion: tls.VersionSSL30, | ||
MaxVersion: tls.VersionTLS11, | ||
}) | ||
if err == nil { | ||
failureLog(function, args, startTime, "", "Endpoint accepts insecure connection", err).Error() | ||
return | ||
} | ||
|
||
// Tests whether the endpoint accepts TLS1.2 connections | ||
args = map[string]interface{}{ | ||
"MinVersion": "tls.VersionTLS12", | ||
} | ||
_, err = tls.Dial("tcp", endpoint, &tls.Config{ | ||
MinVersion: tls.VersionTLS12, | ||
}) | ||
if err != nil { | ||
failureLog(function, args, startTime, "", "Endpoint rejects secure connection", err).Error() | ||
return | ||
} | ||
successLog(function, args, startTime) | ||
} | ||
|
||
// Tests whether the endpoint accepts SSL3.0, TLS1.0 or TLS1.1 connections - fail if so. | ||
// Tests whether the endpoint accepts TLS1.2 connections - fail if not. | ||
func testTLSCiphers(endpoint string) { | ||
const function = "TLSCiphers" | ||
startTime := time.Now() | ||
|
||
// Tests whether the endpoint accepts insecure ciphers | ||
args := map[string]interface{}{ | ||
"MinVersion": "tls.VersionTLS12", | ||
"CipherSuites": unsupportedCipherSuites, | ||
} | ||
_, err := tls.Dial("tcp", endpoint, &tls.Config{ | ||
MinVersion: tls.VersionTLS12, | ||
CipherSuites: unsupportedCipherSuites, | ||
}) | ||
if err == nil { | ||
failureLog(function, args, startTime, "", "Endpoint accepts insecure cipher suites", err).Error() | ||
return | ||
} | ||
|
||
// Tests whether the endpoint accepts at least one secure cipher | ||
args = map[string]interface{}{ | ||
"MinVersion": "tls.VersionTLS12", | ||
"CipherSuites": supportedCipherSuites, | ||
} | ||
_, err = tls.Dial("tcp", endpoint, &tls.Config{ | ||
MinVersion: tls.VersionTLS12, | ||
CipherSuites: supportedCipherSuites, | ||
}) | ||
if err != nil { | ||
failureLog(function, args, startTime, "", "Endpoint rejects all secure cipher suites", err).Error() | ||
return | ||
} | ||
|
||
// Tests whether the endpoint accepts at least one default cipher | ||
args = map[string]interface{}{ | ||
"MinVersion": "tls.VersionTLS12", | ||
"CipherSuites": nil, | ||
} | ||
_, err = tls.Dial("tcp", endpoint, &tls.Config{ | ||
MinVersion: tls.VersionTLS12, | ||
CipherSuites: nil, // default value | ||
}) | ||
if err != nil { | ||
failureLog(function, args, startTime, "", "Endpoint rejects default cipher suites", err).Error() | ||
return | ||
} | ||
successLog(function, args, startTime) | ||
} | ||
|
||
func successLog(function string, args map[string]interface{}, startTime time.Time) *log.Entry { | ||
duration := time.Since(startTime).Nanoseconds() / 1000000 | ||
return log.WithFields(log.Fields{ | ||
"name": testName, | ||
"function": function, | ||
"args": args, | ||
"duration": duration, | ||
"status": PASS, | ||
}) | ||
} | ||
|
||
func failureLog(function string, args map[string]interface{}, startTime time.Time, alert string, message string, err error) *log.Entry { | ||
duration := time.Since(startTime).Nanoseconds() / 1000000 | ||
fields := log.Fields{ | ||
"name": testName, | ||
"function": function, | ||
"args": args, | ||
"duration": duration, | ||
"status": FAIL, | ||
"alert": alert, | ||
"message": message, | ||
} | ||
if err != nil { | ||
fields["error"] = err | ||
} | ||
return log.WithFields(fields) | ||
} | ||
|
||
type mintJSONFormatter struct { | ||
} | ||
|
||
func (f *mintJSONFormatter) Format(entry *log.Entry) ([]byte, error) { | ||
data := make(log.Fields, len(entry.Data)) | ||
for k, v := range entry.Data { | ||
switch v := v.(type) { | ||
case error: | ||
// Otherwise errors are ignored by `encoding/json` | ||
// https://github.com/sirupsen/logrus/issues/137 | ||
data[k] = v.Error() | ||
default: | ||
data[k] = v | ||
} | ||
} | ||
|
||
serialized, err := json.Marshal(data) | ||
if err != nil { | ||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) | ||
} | ||
return append(serialized, '\n'), nil | ||
} | ||
|
||
// Secure Go implementations of modern TLS ciphers | ||
// The following ciphers are excluded because: | ||
// - RC4 ciphers: RC4 is broken | ||
// - 3DES ciphers: Because of the 64 bit blocksize of DES (Sweet32) | ||
// - CBC-SHA256 ciphers: No countermeasures against Lucky13 timing attack | ||
// - CBC-SHA ciphers: Legacy ciphers (SHA-1) and non-constant time | ||
// implementation of CBC. | ||
// (CBC-SHA ciphers can be enabled again if required) | ||
// - RSA key exchange ciphers: Disabled because of dangerous PKCS1-v1.5 RSA | ||
// padding scheme. See Bleichenbacher attacks. | ||
var supportedCipherSuites = []uint16{ | ||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, | ||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, | ||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, | ||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, | ||
} | ||
|
||
var unsupportedCipherSuites = []uint16{ | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // Go stack contains (some) countermeasures against timing attacks (Lucky13) | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // No countermeasures against timing attacks | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // Go stack contains (some) countermeasures against timing attacks (Lucky13) | ||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, // Broken cipher | ||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, // Sweet32 | ||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // Go stack contains (some) countermeasures against timing attacks (Lucky13) | ||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, // No countermeasures against timing attacks | ||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // Go stack contains (some) countermeasures against timing attacks (Lucky13) | ||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, // Broken cipher | ||
|
||
// all RSA-PKCS1-v1.5 ciphers are disabled - danger of Bleichenbacher attack variants | ||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // Sweet32 | ||
tls.TLS_RSA_WITH_AES_128_CBC_SHA, // Go stack contains (some) countermeasures against timing attacks (Lucky13) | ||
tls.TLS_RSA_WITH_AES_128_CBC_SHA256, // No countermeasures against timing attacks | ||
tls.TLS_RSA_WITH_AES_256_CBC_SHA, // Go stack contains (some) countermeasures against timing attacks (Lucky13) | ||
tls.TLS_RSA_WITH_RC4_128_SHA, // Broken cipher | ||
|
||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // Disabled because of RSA-PKCS1-v1.5 - AES-GCM is considered secure. | ||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384, // Disabled because of RSA-PKCS1-v1.5 - AES-GCM is considered secure. | ||
} |