Commit 9c039474 authored by Matt Holt's avatar Matt Holt

Merge pull request #78 from guilhermebr/master

tls: Optional block for ciphers, protocols and cache options
parents cf2808ae b3783161
package setup
import (
"github.com/mholt/caddy/middleware"
"crypto/tls"
"log"
"strconv"
"strings"
"github.com/mholt/caddy/middleware"
)
// Map of supported protocols
// SSLv3 will be not supported in next release
var supportedProtocols = map[string]uint16{
"ssl3.0": tls.VersionSSL30,
"tls1.0": tls.VersionTLS10,
"tls1.1": tls.VersionTLS11,
"tls1.2": tls.VersionTLS12,
}
// Map of supported ciphers
// For security reasons caddy will not support RC4 ciphers
var supportedCiphers = map[string]uint16{
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
}
func TLS(c *Controller) (middleware.Middleware, error) {
c.TLS.Enabled = true
if c.Port == "http" {
......@@ -12,7 +40,7 @@ func TLS(c *Controller) (middleware.Middleware, error) {
log.Printf("Warning: TLS was disabled on host http://%s."+
" Make sure you are specifying https://%s in your config (if you haven't already)."+
" If you meant to serve tls on port 80,"+
" specify port 80 in your config (http://%s:80).", c.Host, c.Host, c.Host)
" specify port 80 in your config (https://%s:80).", c.Host, c.Host, c.Host)
}
for c.Next() {
......@@ -25,6 +53,69 @@ func TLS(c *Controller) (middleware.Middleware, error) {
return nil, c.ArgErr()
}
c.TLS.Key = c.Val()
// Optional block
for c.NextBlock() {
switch c.Val() {
case "protocols":
args := c.RemainingArgs()
if len(args) != 2 {
return nil, c.ArgErr()
}
value, ok := supportedProtocols[strings.ToLower(args[0])]
if !ok {
return nil, c.Errf("Wrong protocol name or protocol not supported '%s'", c.Val())
}
c.TLS.ProtocolMinVersion = value
value, ok = supportedProtocols[strings.ToLower(args[1])]
if !ok {
return nil, c.Errf("Wrong protocol name or protocol not supported '%s'", c.Val())
}
c.TLS.ProtocolMaxVersion = value
case "ciphers":
for c.NextArg() {
value, ok := supportedCiphers[strings.ToUpper(c.Val())]
if !ok {
return nil, c.Errf("Wrong cipher name or cipher not supported '%s'", c.Val())
}
c.TLS.Ciphers = append(c.TLS.Ciphers, value)
}
case "cache":
if !c.NextArg() {
return nil, c.ArgErr()
}
size, err := strconv.Atoi(c.Val())
if err != nil {
return nil, c.Errf("Cache parameter should be an number '%s': %v", c.Val(), err)
}
c.TLS.CacheSize = size
default:
return nil, c.Errf("Unknown keyword '%s'")
}
}
}
// If no Ciphers provided, use all caddy supportedCiphers
if len(c.TLS.Ciphers) == 0 {
for _, v := range supportedCiphers {
c.TLS.Ciphers = append(c.TLS.Ciphers, v)
}
}
// If no ProtocolMin provided, set default MinVersion to TLSv1.1 for security reasons
if c.TLS.ProtocolMinVersion == 0 {
c.TLS.ProtocolMinVersion = tls.VersionTLS11
}
//If no ProtocolMax provided, use crypto/tls default MaxVersion(tls1.2)
if c.TLS.ProtocolMaxVersion == 0 {
c.TLS.ProtocolMaxVersion = tls.VersionTLS12
}
//If no cachesize provided, set default to 64
if c.TLS.CacheSize == 0 {
c.TLS.CacheSize = 64
}
return nil, nil
......
package setup
import (
"crypto/tls"
"testing"
)
func TestTLSParseNoOptional(t *testing.T) {
c := newTestController(`tls cert.crt cert.key`)
_, err := TLS(c)
if err != nil {
t.Errorf("Expected no errors, got: %v", err)
}
if len(c.TLS.Ciphers) != len(supportedCiphers) {
t.Errorf("Expected %v Ciphers, got %v", len(supportedCiphers), len(c.TLS.Ciphers))
}
if c.TLS.ProtocolMinVersion != tls.VersionTLS11 {
t.Errorf("Expected 'tls1.1 (0x0302)' as ProtocolMinVersion, got %#v", c.TLS.ProtocolMinVersion)
}
if c.TLS.ProtocolMaxVersion != tls.VersionTLS12 {
t.Errorf("Expected 'tls1.2 (0x0303)' as ProtocolMaxVersion, got %v", c.TLS.ProtocolMaxVersion)
}
if c.TLS.CacheSize != 64 {
t.Errorf("Expected CacheSize 64, got %v", c.TLS.CacheSize)
}
}
func TestTLSParseIncompleteParams(t *testing.T) {
c := newTestController(`tls`)
_, err := TLS(c)
if err == nil {
t.Errorf("Expected errors, but no error returned")
}
c = newTestController(`tls cert.key`)
_, err = TLS(c)
if err == nil {
t.Errorf("Expected errors, but no error returned")
}
}
func TestTLSParseWithOptionalParams(t *testing.T) {
params := `tls cert.crt cert.key {
protocols ssl3.0 tls1.2
ciphers RSA-3DES-EDE-CBC-SHA RSA-AES256-CBC-SHA ECDHE-RSA-AES128-GCM-SHA256
cache 128
}`
c := newTestController(params)
_, err := TLS(c)
if err != nil {
t.Errorf("Expected no errors, got: %v", err)
}
if c.TLS.ProtocolMinVersion != tls.VersionSSL30 {
t.Errorf("Expected 'ssl3.0 (0x0300)' as ProtocolMinVersion, got %#v", c.TLS.ProtocolMinVersion)
}
if c.TLS.ProtocolMaxVersion != tls.VersionTLS12 {
t.Errorf("Expected 'tls1.2 (0x0302)' as ProtocolMaxVersion, got %#v", c.TLS.ProtocolMaxVersion)
}
if len(c.TLS.Ciphers) != 3 {
t.Errorf("Expected 3 Ciphers, got %v", len(c.TLS.Ciphers))
}
if c.TLS.CacheSize != 128 {
t.Errorf("Expected CacheSize 128, got %v", c.TLS.CacheSize)
}
}
func TestTLSParseWithWrongOptionalParams(t *testing.T) {
params := `tls cert.crt cert.key {
cache a
}`
c := newTestController(params)
_, err := TLS(c)
if err == nil {
t.Errorf("Expected errors, but no error returned")
}
// Test protocols wrong params
params = `tls cert.crt cert.key {
protocols ssl tls
}`
c = newTestController(params)
_, err = TLS(c)
if err == nil {
t.Errorf("Expected errors, but no error returned")
}
// Test ciphers wrong params
params = `tls cert.crt cert.key {
ciphers not-valid-cipher
}`
c = newTestController(params)
_, err = TLS(c)
if err == nil {
t.Errorf("Expected errors, but no error returned")
}
}
......@@ -55,8 +55,13 @@ func (c Config) Address() string {
// TLSConfig describes how TLS should be configured and used,
// if at all. A certificate and key are both required.
// Ciphers, Protocols and CacheSize are optional
type TLSConfig struct {
Enabled bool
Certificate string
Key string
Enabled bool
Certificate string
Key string
Ciphers []uint16
ProtocolMinVersion uint16
ProtocolMaxVersion uint16
CacheSize int
}
......@@ -132,8 +132,18 @@ func ListenAndServeTLSWithSNI(srv *http.Server, tlsConfigs []TLSConfig) error {
}
config.BuildNameToCertificate()
// Add a session cache LRU algorithm with default capacity (64)
config.ClientSessionCache = tls.NewLRUClientSessionCache(0)
// Here we change some crypto/tls defaults based on caddyfile
// If no config provided, we set defaults focused in security
// Add a session cache LRU algorithm
config.ClientSessionCache = tls.NewLRUClientSessionCache(tlsConfigs[0].CacheSize)
config.MinVersion = tlsConfigs[0].ProtocolMinVersion
config.MaxVersion = tlsConfigs[0].ProtocolMaxVersion
config.CipherSuites = tlsConfigs[0].Ciphers
// Server ciphers have priority over client ciphers
config.PreferServerCipherSuites = true
conn, err := net.Listen("tcp", addr)
if err != nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment