Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a tool to generate the TLS configuration form Mozilla's ciphers recommendation #178

Merged
merged 5 commits into from
Feb 21, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add a tool which generates the TLS rule configuration from Mozilla se…
…rver side

TLS configuration
  • Loading branch information
ccojocar committed Feb 19, 2018
commit 3ddb7b4221e7f28831f52c0b1dfd235cd0ff925c
13 changes: 13 additions & 0 deletions cmd/tlsconfig/header_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import "text/template"

var generatedHeaderTmpl = template.Must(template.New("generated").Parse(`
package {{.}}

import (
"go/ast"

"github.com/GoASTScanner/gas"
)
`))
18 changes: 18 additions & 0 deletions cmd/tlsconfig/rule_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import "text/template"

var generatedRuleTmpl = template.Must(template.New("generated").Parse(`
// DO NOT EDIT - Generated by tlsconfig
func New{{.Name}}TLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
return &insecureConfigTLS{
requiredType: "crypto/tls.Config",
MinVersion: {{ .MinVersion }},
MaxVersion: {{ .MaxVersion }},
goodCiphers: []string{
{{range $cipherName := .Ciphers }} "{{$cipherName}}",
{{end}}
},
}, []ast.Node{(*ast.CompositeLit)(nil)}
}
`))
204 changes: 204 additions & 0 deletions cmd/tlsconfig/tlsconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package main

import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"flag"
"fmt"
"go/format"
"io/ioutil"
"log"
"net/http"
"path/filepath"
"sort"
"strings"

"github.com/mozilla/tls-observatory/constants"
)

var (
pkg = flag.String("pkg", "rules", "package name to be added to the output file")
outputFile = flag.String("outputFile", "tls_config.go", "name of the output file")
)

// TLSConfURL url where Mozilla publishes the TLS ciphers recommendations
const TLSConfURL = "https://statics.tls.security.mozilla.org/server-side-tls-conf.json"

// ServerSideTLSJson contains all the available configurations and the version of the current document.
type ServerSideTLSJson struct {
Configurations map[string]Configuration `json:"configurations"`
Version float64 `json:"version"`
}

// Configuration represents configurations levels declared by the Mozilla server-side-tls
// see https://wiki.mozilla.org/Security/Server_Side_TLS
type Configuration struct {
OpenSSLCiphersuites string `json:"openssl_ciphersuites"`
Ciphersuites []string `json:"ciphersuites"`
TLSVersions []string `json:"tls_versions"`
TLSCurves []string `json:"tls_curves"`
CertificateTypes []string `json:"certificate_types"`
CertificateCurves []string `json:"certificate_curves"`
CertificateSignatures []string `json:"certificate_signatures"`
RsaKeySize float64 `json:"rsa_key_size"`
DHParamSize float64 `json:"dh_param_size"`
ECDHParamSize float64 `json:"ecdh_param_size"`
HstsMinAge float64 `json:"hsts_min_age"`
OldestClients []string `json:"oldest_clients"`
}

type goCipherConfiguration struct {
Name string
Ciphers []string
MinVersion string
MaxVersion string
}

type goTLSConfiguration struct {
cipherConfigs []goCipherConfiguration
}

// getTLSConfFromURL retrieves the json containing the TLS configurations from the specified URL.
func getTLSConfFromURL(url string) (*ServerSideTLSJson, error) {
r, err := http.Get(url)
if err != nil {
return nil, err
}
defer r.Body.Close()

var sstls ServerSideTLSJson
err = json.NewDecoder(r.Body).Decode(&sstls)
if err != nil {
return nil, err
}

return &sstls, nil
}

func getGoCipherConfig(name string, sstls ServerSideTLSJson) (goCipherConfiguration, error) {
cipherConf := goCipherConfiguration{Name: strings.Title(name)}
conf, ok := sstls.Configurations[name]
if !ok {
return cipherConf, fmt.Errorf("TLS configuration '%s' not found", name)
}

for _, cipherName := range conf.Ciphersuites {
cipherSuite, ok := constants.CipherSuites[cipherName]
if !ok {
log.Printf("Warning: cannot map cipher '%s'\n", cipherName)
}
if len(cipherSuite.IANAName) > 0 {
cipherConf.Ciphers = append(cipherConf.Ciphers, cipherSuite.IANAName)
}
}

versions := mapTLSVersions(conf.TLSVersions)
if len(versions) > 0 {
cipherConf.MinVersion = fmt.Sprintf("0x%04x", versions[0])
cipherConf.MaxVersion = fmt.Sprintf("0x%04x", versions[len(versions)-1])
} else {
return cipherConf, fmt.Errorf("No TLS versions found for configuration '%s'", name)
}
return cipherConf, nil
}

func mapTLSVersions(tlsVersions []string) []int {
var versions []int
for _, tlsVersion := range tlsVersions {
switch tlsVersion {
case "TLSv1.2":
versions = append(versions, tls.VersionTLS12)
case "TLSv1.1":
versions = append(versions, tls.VersionTLS11)
case "TLSv1":
versions = append(versions, tls.VersionTLS10)
case "SSLv3":
versions = append(versions, tls.VersionSSL30)
default:
continue
}
}
sort.Ints(versions)
return versions
}

func getGoTLSConf() (goTLSConfiguration, error) {
sstls, err := getTLSConfFromURL(TLSConfURL)
if err != nil || sstls == nil {
msg := fmt.Sprintf("Could not load the Server Side TLS configuration from Mozilla's website. Check the URL: %s. Error: %v\n",
TLSConfURL, err)
panic(msg)
}

tlsConfg := goTLSConfiguration{}

modern, err := getGoCipherConfig("modern", *sstls)
if err != nil {
return tlsConfg, err
}
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, modern)

intermediate, err := getGoCipherConfig("intermediate", *sstls)
if err != nil {
return tlsConfg, err
}
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, intermediate)

old, err := getGoCipherConfig("old", *sstls)
if err != nil {
return tlsConfg, err
}
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, old)

return tlsConfg, nil
}

func getCurrentDir() (string, error) {
dir := "."
if args := flag.Args(); len(args) == 1 {
dir = args[0]
} else if len(args) > 1 {
return "", errors.New("only one directory at a time")
}
dir, err := filepath.Abs(dir)
if err != nil {
return "", err
}
return dir, nil
}

func main() {
dir, err := getCurrentDir()
if err != nil {
log.Fatalln(err)
}
tlsConfig, err := getGoTLSConf()
if err != nil {
log.Fatalln(err)
}

var buf bytes.Buffer
err = generatedHeaderTmpl.Execute(&buf, *pkg)
if err != nil {
log.Fatalf("Failed to generate the header: %v", err)
}
for _, cipherConfig := range tlsConfig.cipherConfigs {
err := generatedRuleTmpl.Execute(&buf, cipherConfig)
if err != nil {
log.Fatalf("Failed to generated the cipher config: %v", err)
}
}

src, err := format.Source(buf.Bytes())
if err != nil {
log.Printf("warnings: Failed to format the code: %v", err)
src = buf.Bytes()
}

outputPath := filepath.Join(dir, *outputFile)
if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
log.Fatalf("Writing output: %s", err)
}
}
73 changes: 2 additions & 71 deletions rules/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//go:generate tlsconfig

package rules

import (
Expand Down Expand Up @@ -118,74 +120,3 @@ func (t *insecureConfigTLS) Match(n ast.Node, c *gas.Context) (*gas.Issue, error
}
return nil, nil
}

// NewModernTLSCheck see: https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
func NewModernTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
return &insecureConfigTLS{
requiredType: "crypto/tls.Config",
MinVersion: 0x0303, // TLS 1.2 only
MaxVersion: 0x0303,
goodCiphers: []string{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
},
}, []ast.Node{(*ast.CompositeLit)(nil)}
}

// NewIntermediateTLSCheck see: https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
func NewIntermediateTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
return &insecureConfigTLS{
requiredType: "crypto/tls.Config",
MinVersion: 0x0301, // TLS 1.2, 1.1, 1.0
MaxVersion: 0x0303,
goodCiphers: []string{
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
},
}, []ast.Node{(*ast.CompositeLit)(nil)}
}

// NewCompatTLSCheck see: https://wiki.mozilla.org/Security/Server_Side_TLS#Old_compatibility_.28default.29
func NewCompatTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
return &insecureConfigTLS{
requiredType: "crypto/tls.Config",
MinVersion: 0x0301, // TLS 1.2, 1.1, 1.0
MaxVersion: 0x0303,
goodCiphers: []string{
"TLS_RSA_WITH_RC4_128_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
},
}, []ast.Node{(*ast.CompositeLit)(nil)}
}
Loading