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

Add support for TLS

parent ea7c2d88
...@@ -2,6 +2,7 @@ package irmaserver ...@@ -2,6 +2,7 @@ package irmaserver
import ( import (
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
...@@ -26,11 +27,21 @@ type Configuration struct { ...@@ -26,11 +27,21 @@ type Configuration struct {
ListenAddress string `json:"listenaddr" mapstructure:"listenaddr"` ListenAddress string `json:"listenaddr" mapstructure:"listenaddr"`
// Port to listen at // Port to listen at
Port int `json:"port" mapstructure:"port"` 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 // If specified, start a separate server for the IRMA app at his port
ClientPort int `json:"clientport" mapstructure:"clientport"` ClientPort int `json:"clientport" mapstructure:"clientport"`
// If clientport is specified, the server for the IRMA app listens at this address // If clientport is specified, the server for the IRMA app listens at this address
ClientListenAddress string `json:"clientlistenaddr" mapstructure:"clientlistenaddr"` 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 // Requestor-specific permission and authentication configuration
RequestorsString string `json:"-" mapstructure:"requestors"` RequestorsString string `json:"-" mapstructure:"requestors"`
...@@ -172,6 +183,15 @@ func (conf *Configuration) initialize() error { ...@@ -172,6 +183,15 @@ func (conf *Configuration) initialize() error {
return errors.New("clientlistenaddr must be combined with a nonzero clientport") 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 { if err := conf.validatePermissions(); err != nil {
return err return err
} }
...@@ -188,6 +208,12 @@ func (conf *Configuration) initialize() error { ...@@ -188,6 +208,12 @@ func (conf *Configuration) initialize() error {
} }
replace := "$1:" + strconv.Itoa(port) replace := "$1:" + strconv.Itoa(port)
conf.URL = string(regexp.MustCompile("(https?://[^/]*):port").ReplaceAll([]byte(conf.URL), []byte(replace))) 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 return nil
...@@ -262,6 +288,48 @@ func (conf *Configuration) validatePermissionSet(requestor string, requestorperm ...@@ -262,6 +288,48 @@ func (conf *Configuration) validatePermissionSet(requestor string, requestorperm
return errs 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 { func (conf *Configuration) readPrivateKey() error {
if conf.JwtPrivateKey == "" && conf.JwtPrivateKeyFile == "" { if conf.JwtPrivateKey == "" && conf.JwtPrivateKeyFile == "" {
return nil return nil
......
...@@ -119,6 +119,16 @@ func setFlags(cmd *cobra.Command) error { ...@@ -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("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.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.CountP("verbose", "v", "verbose (repeatable)")
flags.BoolP("quiet", "q", false, "quiet") flags.BoolP("quiet", "q", false, "quiet")
...@@ -187,6 +197,15 @@ func configure(cmd *cobra.Command) error { ...@@ -187,6 +197,15 @@ func configure(cmd *cobra.Command) error {
MaxRequestAge: viper.GetInt("maxrequestage"), MaxRequestAge: viper.GetInt("maxrequestage"),
Verbose: viper.GetInt("verbose"), Verbose: viper.GetInt("verbose"),
Quiet: viper.GetBool("quiet"), 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 // Handle global permissions
...@@ -212,8 +231,6 @@ func configure(cmd *cobra.Command) error { ...@@ -212,8 +231,6 @@ func configure(cmd *cobra.Command) error {
} }
} }
bts, _ := json.MarshalIndent(conf, "", " ")
logger.Debug("Configuration: ", string(bts), "\n")
logger.Debug("Done configuring") logger.Debug("Done configuring")
return nil return nil
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
package irmaserver package irmaserver
import ( import (
"crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
...@@ -39,20 +41,30 @@ func Start(config *Configuration) error { ...@@ -39,20 +41,30 @@ func Start(config *Configuration) error {
func startRequestorServer() { func startRequestorServer() {
serv = &http.Server{} 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() { func startClientServer() {
clientserv = &http.Server{} 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) fulladdr := fmt.Sprintf("%s:%d", addr, port)
conf.Logger.Info(name, " listening at ", fulladdr) conf.Logger.Info(name, " listening at ", fulladdr)
s.Addr = fulladdr s.Addr = fulladdr
s.Handler = handler 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) _ = server.LogFatal(err)
} }
} }
...@@ -84,6 +96,8 @@ func Initialize(config *Configuration) error { ...@@ -84,6 +96,8 @@ func Initialize(config *Configuration) error {
if err := conf.initialize(); err != nil { if err := conf.initialize(); err != nil {
return err return err
} }
bts, _ := json.MarshalIndent(config, "", " ")
config.Logger.Debug("Configuration: ", string(bts), "\n")
return nil 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