Commit 87d664ac authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Add support for TLS

parent ea7c2d88
......@@ -2,6 +2,7 @@ package irmaserver
import (
"crypto/rsa"
"crypto/tls"
"fmt"
"regexp"
"strconv"
......@@ -26,11 +27,21 @@ type Configuration struct {
ListenAddress string `json:"listenaddr" mapstructure:"listenaddr"`
// Port to listen at
Port int `json:"port" mapstructure:"port"`
// TLS configuration
TlsCertificate string `json:"tlscertificate" mapstructure:"tlscertificate"`
TlsCertificateFile string `json:"tlscertificatefile" mapstructure:"tlscertificatefile"`
TlsPrivateKey string `json:"tlsprivatekey" mapstructure:"tlsprivatekey"`
TlsPrivateKeyFile string `json:"tlsprivatekeyfile" mapstructure:"tlsprivatekeyfile"`
// If specified, start a separate server for the IRMA app at his port
ClientPort int `json:"clientport" mapstructure:"clientport"`
// If clientport is specified, the server for the IRMA app listens at this address
ClientListenAddress string `json:"clientlistenaddr" mapstructure:"clientlistenaddr"`
// TLS configuration for irmaclient HTTP API
ClientTlsCertificate string `json:"clienttlscertificate" mapstructure:"clienttlscertificate"`
ClientTlsCertificateFile string `json:"clienttlscertificatefile" mapstructure:"clienttlscertificatefile"`
ClientTlsPrivateKey string `json:"clienttlsprivatekey" mapstructure:"clienttlsprivatekey"`
ClientTlsPrivateKeyFile string `json:"clienttlsprivatekeyfile" mapstructure:"clienttlsprivatekeyfile"`
// Requestor-specific permission and authentication configuration
RequestorsString string `json:"-" mapstructure:"requestors"`
......@@ -172,6 +183,15 @@ func (conf *Configuration) initialize() error {
return errors.New("clientlistenaddr must be combined with a nonzero clientport")
}
tlsConf, err := conf.tlsConfig()
if err != nil {
return errors.WrapPrefix(err, "Failed to read TLS configuration", 0)
}
clientTlsConf, err := conf.clientTlsConfig()
if err != nil {
return errors.WrapPrefix(err, "Failed to read client TLS configuration", 0)
}
if err := conf.validatePermissions(); err != nil {
return err
}
......@@ -188,6 +208,12 @@ func (conf *Configuration) initialize() error {
}
replace := "$1:" + strconv.Itoa(port)
conf.URL = string(regexp.MustCompile("(https?://[^/]*):port").ReplaceAll([]byte(conf.URL), []byte(replace)))
separateClientServer := conf.separateClientServer()
if (separateClientServer && clientTlsConf != nil) || (!separateClientServer && tlsConf != nil) {
if strings.HasPrefix(conf.URL, "http://") {
conf.URL = "https://" + conf.URL[len("http://"):]
}
}
}
return nil
......@@ -262,6 +288,48 @@ func (conf *Configuration) validatePermissionSet(requestor string, requestorperm
return errs
}
func (conf *Configuration) clientTlsConfig() (*tls.Config, error) {
return conf.readTlsConf(conf.ClientTlsCertificate, conf.ClientTlsCertificateFile, conf.ClientTlsPrivateKey, conf.ClientTlsPrivateKeyFile)
}
func (conf *Configuration) tlsConfig() (*tls.Config, error) {
return conf.readTlsConf(conf.TlsCertificate, conf.TlsCertificateFile, conf.TlsPrivateKey, conf.TlsPrivateKeyFile)
}
func (conf *Configuration) readTlsConf(cert, certfile, key, keyfile string) (*tls.Config, error) {
if cert == "" && certfile == "" && key == "" && keyfile == "" {
return nil, nil
}
var certbts, keybts []byte
var err error
if certbts, err = fs.ReadKey(cert, certfile); err != nil {
return nil, err
}
if keybts, err = fs.ReadKey(key, keyfile); err != nil {
return nil, err
}
cer, err := tls.X509KeyPair(certbts, keybts)
if err != nil {
return nil, err
}
return &tls.Config{
Certificates: []tls.Certificate{cer},
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}, nil
}
func (conf *Configuration) readPrivateKey() error {
if conf.JwtPrivateKey == "" && conf.JwtPrivateKeyFile == "" {
return nil
......
......@@ -119,6 +119,16 @@ func setFlags(cmd *cobra.Command) error {
flags.StringSlice("sign", nil, "Comma-separated list of attributes that all requestors may request in signatures")
flags.StringSlice("issue", nil, "Comma-separated list of attributes that all requestors may issue")
flags.String("tlscertificate", "", "TLS certificate ")
flags.String("tlscertificatefile", "", "Path to TLS certificate ")
flags.String("tlsprivatekey", "", "TLS private key")
flags.String("tlsprivatekeyfile", "", "Path to TLS private key")
flags.String("clienttlscertificate", "", "TLS certificate for IRMA app server")
flags.String("clienttlscertificatefile", "", "Path to TLS certificate for IRMA app server")
flags.String("clienttlsprivatekey", "", "TLS private key for IRMA app server")
flags.String("clienttlsprivatekeyfile", "", "Path to TLS private key for IRMA app server")
flags.CountP("verbose", "v", "verbose (repeatable)")
flags.BoolP("quiet", "q", false, "quiet")
......@@ -187,6 +197,15 @@ func configure(cmd *cobra.Command) error {
MaxRequestAge: viper.GetInt("maxrequestage"),
Verbose: viper.GetInt("verbose"),
Quiet: viper.GetBool("quiet"),
TlsCertificate: viper.GetString("tlscertificate"),
TlsCertificateFile: viper.GetString("tlscertificatefile"),
TlsPrivateKey: viper.GetString("tlsprivatekey"),
TlsPrivateKeyFile: viper.GetString("tlsprivatekeyfile"),
ClientTlsCertificate: viper.GetString("clienttlscertificate"),
ClientTlsCertificateFile: viper.GetString("clienttlscertificatefile"),
ClientTlsPrivateKey: viper.GetString("clienttlsprivatekey"),
ClientTlsPrivateKeyFile: viper.GetString("clienttlsprivatekeyfile"),
}
// Handle global permissions
......@@ -212,8 +231,6 @@ func configure(cmd *cobra.Command) error {
}
}
bts, _ := json.MarshalIndent(conf, "", " ")
logger.Debug("Configuration: ", string(bts), "\n")
logger.Debug("Done configuring")
return nil
......
......@@ -2,7 +2,9 @@
package irmaserver
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
......@@ -39,20 +41,30 @@ func Start(config *Configuration) error {
func startRequestorServer() {
serv = &http.Server{}
startServer(serv, Handler(), "Server", conf.ListenAddress, conf.Port)
tlsConf, _ := conf.tlsConfig()
startServer(serv, Handler(), "Server", conf.ListenAddress, conf.Port, tlsConf)
}
func startClientServer() {
clientserv = &http.Server{}
startServer(clientserv, ClientHandler(), "Client server", conf.ClientListenAddress, conf.ClientPort)
tlsConf, _ := conf.clientTlsConfig()
startServer(clientserv, ClientHandler(), "Client server", conf.ClientListenAddress, conf.ClientPort, tlsConf)
}
func startServer(s *http.Server, handler http.Handler, name, addr string, port int) {
func startServer(s *http.Server, handler http.Handler, name, addr string, port int, tlsConf *tls.Config) {
fulladdr := fmt.Sprintf("%s:%d", addr, port)
conf.Logger.Info(name, " listening at ", fulladdr)
s.Addr = fulladdr
s.Handler = handler
if err := s.ListenAndServe(); err != http.ErrServerClosed {
var err error
if tlsConf != nil {
conf.Logger.Info(name, " TLS enabled")
s.TLSConfig = tlsConf
err = s.ListenAndServeTLS("", "")
} else {
err = s.ListenAndServe()
}
if err != http.ErrServerClosed {
_ = server.LogFatal(err)
}
}
......@@ -84,6 +96,8 @@ func Initialize(config *Configuration) error {
if err := conf.initialize(); err != nil {
return err
}
bts, _ := json.MarshalIndent(config, "", " ")
config.Logger.Debug("Configuration: ", string(bts), "\n")
return nil
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment