gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/securego/gosec/cmd/tlsconfig/tlsconfig.go (about) 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) // #nosec G107 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("'%s' cipher is not available in crypto/tls package\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 }