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  }