forked from securego/gosec
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a tool to generate the TLS configuration form Mozilla's ciphers r…
…ecommendation (securego#178) * Add a tool which generates the TLS rule configuration from Mozilla server side TLS configuration * Update README * Remove trailing space in README * Update dependencies * Fix the commends of the generated functions
- Loading branch information
Showing
7 changed files
with
406 additions
and
72 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,13 @@ | ||
package main | ||
|
||
import "text/template" | ||
|
||
var generatedHeaderTmpl = template.Must(template.New("generated").Parse(` | ||
package {{.}} | ||
import ( | ||
"go/ast" | ||
"github.com/GoASTScanner/gas" | ||
) | ||
`)) |
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,19 @@ | ||
package main | ||
|
||
import "text/template" | ||
|
||
var generatedRuleTmpl = template.Must(template.New("generated").Parse(` | ||
// New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers | ||
// DO NOT EDIT - generated by tlsconfig tool | ||
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)} | ||
} | ||
`)) |
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,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) | ||
} | ||
} |
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
Oops, something went wrong.