Skip to content

Commit edb362f

Browse files
cosmincojocargcmurphy
authored andcommitted
Add a tool to generate the TLS configuration form Mozilla's ciphers recommendation (#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
1 parent 1c58cbd commit edb362f

File tree

7 files changed

+406
-72
lines changed

7 files changed

+406
-72
lines changed

Godeps/Godeps.json

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,21 @@ file. The output format is controlled by the '-fmt' flag, and the output file is
114114
$ gas -fmt=json -out=results.json *.go
115115
```
116116

117+
### Generate TLS rule
118+
119+
The configuration of TLS rule can be generated from [Mozilla's TLS ciphers recommendation](https://statics.tls.security.mozilla.org/server-side-tls-conf.json).
120+
121+
122+
First you need to install the generator tool:
123+
124+
```
125+
go get github.com/GoASTScanner/gas/cmd/tlsconfig/...
126+
```
127+
128+
You can invoke now the `go generate` in the root of the project:
129+
130+
```
131+
go generate ./...
132+
```
133+
134+
This will generate the `rules/tls_config.go` file with will contain the current ciphers recommendation from Mozilla.

cmd/tlsconfig/header_template.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
import "text/template"
4+
5+
var generatedHeaderTmpl = template.Must(template.New("generated").Parse(`
6+
package {{.}}
7+
8+
import (
9+
"go/ast"
10+
11+
"github.com/GoASTScanner/gas"
12+
)
13+
`))

cmd/tlsconfig/rule_template.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package main
2+
3+
import "text/template"
4+
5+
var generatedRuleTmpl = template.Must(template.New("generated").Parse(`
6+
// New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers
7+
// DO NOT EDIT - generated by tlsconfig tool
8+
func New{{.Name}}TLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
9+
return &insecureConfigTLS{
10+
requiredType: "crypto/tls.Config",
11+
MinVersion: {{ .MinVersion }},
12+
MaxVersion: {{ .MaxVersion }},
13+
goodCiphers: []string{
14+
{{range $cipherName := .Ciphers }} "{{$cipherName}}",
15+
{{end}}
16+
},
17+
}, []ast.Node{(*ast.CompositeLit)(nil)}
18+
}
19+
`))

cmd/tlsconfig/tlsconfig.go

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"crypto/tls"
6+
"encoding/json"
7+
"errors"
8+
"flag"
9+
"fmt"
10+
"go/format"
11+
"io/ioutil"
12+
"log"
13+
"net/http"
14+
"path/filepath"
15+
"sort"
16+
"strings"
17+
18+
"github.com/mozilla/tls-observatory/constants"
19+
)
20+
21+
var (
22+
pkg = flag.String("pkg", "rules", "package name to be added to the output file")
23+
outputFile = flag.String("outputFile", "tls_config.go", "name of the output file")
24+
)
25+
26+
// TLSConfURL url where Mozilla publishes the TLS ciphers recommendations
27+
const TLSConfURL = "https://statics.tls.security.mozilla.org/server-side-tls-conf.json"
28+
29+
// ServerSideTLSJson contains all the available configurations and the version of the current document.
30+
type ServerSideTLSJson struct {
31+
Configurations map[string]Configuration `json:"configurations"`
32+
Version float64 `json:"version"`
33+
}
34+
35+
// Configuration represents configurations levels declared by the Mozilla server-side-tls
36+
// see https://wiki.mozilla.org/Security/Server_Side_TLS
37+
type Configuration struct {
38+
OpenSSLCiphersuites string `json:"openssl_ciphersuites"`
39+
Ciphersuites []string `json:"ciphersuites"`
40+
TLSVersions []string `json:"tls_versions"`
41+
TLSCurves []string `json:"tls_curves"`
42+
CertificateTypes []string `json:"certificate_types"`
43+
CertificateCurves []string `json:"certificate_curves"`
44+
CertificateSignatures []string `json:"certificate_signatures"`
45+
RsaKeySize float64 `json:"rsa_key_size"`
46+
DHParamSize float64 `json:"dh_param_size"`
47+
ECDHParamSize float64 `json:"ecdh_param_size"`
48+
HstsMinAge float64 `json:"hsts_min_age"`
49+
OldestClients []string `json:"oldest_clients"`
50+
}
51+
52+
type goCipherConfiguration struct {
53+
Name string
54+
Ciphers []string
55+
MinVersion string
56+
MaxVersion string
57+
}
58+
59+
type goTLSConfiguration struct {
60+
cipherConfigs []goCipherConfiguration
61+
}
62+
63+
// getTLSConfFromURL retrieves the json containing the TLS configurations from the specified URL.
64+
func getTLSConfFromURL(url string) (*ServerSideTLSJson, error) {
65+
r, err := http.Get(url)
66+
if err != nil {
67+
return nil, err
68+
}
69+
defer r.Body.Close()
70+
71+
var sstls ServerSideTLSJson
72+
err = json.NewDecoder(r.Body).Decode(&sstls)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
return &sstls, nil
78+
}
79+
80+
func getGoCipherConfig(name string, sstls ServerSideTLSJson) (goCipherConfiguration, error) {
81+
cipherConf := goCipherConfiguration{Name: strings.Title(name)}
82+
conf, ok := sstls.Configurations[name]
83+
if !ok {
84+
return cipherConf, fmt.Errorf("TLS configuration '%s' not found", name)
85+
}
86+
87+
for _, cipherName := range conf.Ciphersuites {
88+
cipherSuite, ok := constants.CipherSuites[cipherName]
89+
if !ok {
90+
log.Printf("Warning: cannot map cipher '%s'\n", cipherName)
91+
}
92+
if len(cipherSuite.IANAName) > 0 {
93+
cipherConf.Ciphers = append(cipherConf.Ciphers, cipherSuite.IANAName)
94+
}
95+
}
96+
97+
versions := mapTLSVersions(conf.TLSVersions)
98+
if len(versions) > 0 {
99+
cipherConf.MinVersion = fmt.Sprintf("0x%04x", versions[0])
100+
cipherConf.MaxVersion = fmt.Sprintf("0x%04x", versions[len(versions)-1])
101+
} else {
102+
return cipherConf, fmt.Errorf("No TLS versions found for configuration '%s'", name)
103+
}
104+
return cipherConf, nil
105+
}
106+
107+
func mapTLSVersions(tlsVersions []string) []int {
108+
var versions []int
109+
for _, tlsVersion := range tlsVersions {
110+
switch tlsVersion {
111+
case "TLSv1.2":
112+
versions = append(versions, tls.VersionTLS12)
113+
case "TLSv1.1":
114+
versions = append(versions, tls.VersionTLS11)
115+
case "TLSv1":
116+
versions = append(versions, tls.VersionTLS10)
117+
case "SSLv3":
118+
versions = append(versions, tls.VersionSSL30)
119+
default:
120+
continue
121+
}
122+
}
123+
sort.Ints(versions)
124+
return versions
125+
}
126+
127+
func getGoTLSConf() (goTLSConfiguration, error) {
128+
sstls, err := getTLSConfFromURL(TLSConfURL)
129+
if err != nil || sstls == nil {
130+
msg := fmt.Sprintf("Could not load the Server Side TLS configuration from Mozilla's website. Check the URL: %s. Error: %v\n",
131+
TLSConfURL, err)
132+
panic(msg)
133+
}
134+
135+
tlsConfg := goTLSConfiguration{}
136+
137+
modern, err := getGoCipherConfig("modern", *sstls)
138+
if err != nil {
139+
return tlsConfg, err
140+
}
141+
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, modern)
142+
143+
intermediate, err := getGoCipherConfig("intermediate", *sstls)
144+
if err != nil {
145+
return tlsConfg, err
146+
}
147+
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, intermediate)
148+
149+
old, err := getGoCipherConfig("old", *sstls)
150+
if err != nil {
151+
return tlsConfg, err
152+
}
153+
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, old)
154+
155+
return tlsConfg, nil
156+
}
157+
158+
func getCurrentDir() (string, error) {
159+
dir := "."
160+
if args := flag.Args(); len(args) == 1 {
161+
dir = args[0]
162+
} else if len(args) > 1 {
163+
return "", errors.New("only one directory at a time")
164+
}
165+
dir, err := filepath.Abs(dir)
166+
if err != nil {
167+
return "", err
168+
}
169+
return dir, nil
170+
}
171+
172+
func main() {
173+
dir, err := getCurrentDir()
174+
if err != nil {
175+
log.Fatalln(err)
176+
}
177+
tlsConfig, err := getGoTLSConf()
178+
if err != nil {
179+
log.Fatalln(err)
180+
}
181+
182+
var buf bytes.Buffer
183+
err = generatedHeaderTmpl.Execute(&buf, *pkg)
184+
if err != nil {
185+
log.Fatalf("Failed to generate the header: %v", err)
186+
}
187+
for _, cipherConfig := range tlsConfig.cipherConfigs {
188+
err := generatedRuleTmpl.Execute(&buf, cipherConfig)
189+
if err != nil {
190+
log.Fatalf("Failed to generated the cipher config: %v", err)
191+
}
192+
}
193+
194+
src, err := format.Source(buf.Bytes())
195+
if err != nil {
196+
log.Printf("warnings: Failed to format the code: %v", err)
197+
src = buf.Bytes()
198+
}
199+
200+
outputPath := filepath.Join(dir, *outputFile)
201+
if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
202+
log.Fatalf("Writing output: %s", err)
203+
}
204+
}

rules/tls.go

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
//go:generate tlsconfig
16+
1517
package rules
1618

1719
import (
@@ -118,74 +120,3 @@ func (t *insecureConfigTLS) Match(n ast.Node, c *gas.Context) (*gas.Issue, error
118120
}
119121
return nil, nil
120122
}
121-
122-
// NewModernTLSCheck see: https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
123-
func NewModernTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
124-
return &insecureConfigTLS{
125-
requiredType: "crypto/tls.Config",
126-
MinVersion: 0x0303, // TLS 1.2 only
127-
MaxVersion: 0x0303,
128-
goodCiphers: []string{
129-
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
130-
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
131-
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
132-
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
133-
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
134-
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
135-
},
136-
}, []ast.Node{(*ast.CompositeLit)(nil)}
137-
}
138-
139-
// NewIntermediateTLSCheck see: https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
140-
func NewIntermediateTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
141-
return &insecureConfigTLS{
142-
requiredType: "crypto/tls.Config",
143-
MinVersion: 0x0301, // TLS 1.2, 1.1, 1.0
144-
MaxVersion: 0x0303,
145-
goodCiphers: []string{
146-
"TLS_RSA_WITH_AES_128_CBC_SHA",
147-
"TLS_RSA_WITH_AES_256_CBC_SHA",
148-
"TLS_RSA_WITH_AES_128_GCM_SHA256",
149-
"TLS_RSA_WITH_AES_256_GCM_SHA384",
150-
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
151-
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
152-
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
153-
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
154-
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
155-
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
156-
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
157-
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
158-
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
159-
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
160-
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
161-
},
162-
}, []ast.Node{(*ast.CompositeLit)(nil)}
163-
}
164-
165-
// NewCompatTLSCheck see: https://wiki.mozilla.org/Security/Server_Side_TLS#Old_compatibility_.28default.29
166-
func NewCompatTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
167-
return &insecureConfigTLS{
168-
requiredType: "crypto/tls.Config",
169-
MinVersion: 0x0301, // TLS 1.2, 1.1, 1.0
170-
MaxVersion: 0x0303,
171-
goodCiphers: []string{
172-
"TLS_RSA_WITH_RC4_128_SHA",
173-
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
174-
"TLS_RSA_WITH_AES_128_CBC_SHA",
175-
"TLS_RSA_WITH_AES_256_CBC_SHA",
176-
"TLS_RSA_WITH_AES_128_GCM_SHA256",
177-
"TLS_RSA_WITH_AES_256_GCM_SHA384",
178-
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
179-
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
180-
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
181-
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
182-
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
183-
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
184-
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
185-
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
186-
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
187-
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
188-
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
189-
},
190-
}, []ast.Node{(*ast.CompositeLit)(nil)}
191-
}

0 commit comments

Comments
 (0)