package server import ( "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" ) // TLSInfo holds the SSL certificates paths. type TLSInfo struct { CertFile string `json:"CertFile"` KeyFile string `json:"KeyFile"` CAFile string `json:"CAFile"` } // Generates a TLS configuration from the given files. func (info TLSInfo) Config() (TLSConfig, error) { var t TLSConfig t.Scheme = "http" // If the user do not specify key file, cert file and CA file, the type will be HTTP if info.KeyFile == "" && info.CertFile == "" && info.CAFile == "" { return t, nil } // Both the key and cert must be present. if info.KeyFile == "" || info.CertFile == "" { return t, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile) } tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile) if err != nil { return t, err } t.Scheme = "https" t.Server.ClientAuth, t.Server.ClientCAs, err = newCertPool(info.CAFile) if err != nil { return t, err } // The client should trust the RootCA that the Server uses since // everyone is a peer in the network. t.Client.Certificates = []tls.Certificate{tlsCert} t.Client.RootCAs = t.Server.ClientCAs return t, nil } // newCertPool creates x509 certPool and corresponding Auth Type. // If the given CAfile is valid, add the cert into the pool and verify the clients' // certs against the cert in the pool. // If the given CAfile is empty, do not verify the clients' cert. // If the given CAfile is not valid, fatal. func newCertPool(CAFile string) (tls.ClientAuthType, *x509.CertPool, error) { if CAFile == "" { return tls.NoClientCert, nil, nil } pemByte, err := ioutil.ReadFile(CAFile) if err != nil { return 0, nil, err } block, pemByte := pem.Decode(pemByte) cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return 0, nil, err } certPool := x509.NewCertPool() certPool.AddCert(cert) return tls.RequireAndVerifyClientCert, certPool, nil }