Add support for TLS listeners
Support for -tls-cert and -tls-key to point to a certificate and key to encrypt communications between the user and hydroxide. Support for -tls-client-ca to ask client to present a certificate signed by the provided CA. Closes: https://github.com/emersion/hydroxide/issues/13
This commit is contained in:
parent
b812444ff8
commit
eaed359829
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -21,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/emersion/hydroxide/auth"
|
"github.com/emersion/hydroxide/auth"
|
||||||
"github.com/emersion/hydroxide/carddav"
|
"github.com/emersion/hydroxide/carddav"
|
||||||
|
"github.com/emersion/hydroxide/config"
|
||||||
"github.com/emersion/hydroxide/events"
|
"github.com/emersion/hydroxide/events"
|
||||||
"github.com/emersion/hydroxide/exports"
|
"github.com/emersion/hydroxide/exports"
|
||||||
imapbackend "github.com/emersion/hydroxide/imap"
|
imapbackend "github.com/emersion/hydroxide/imap"
|
||||||
|
@ -39,25 +41,32 @@ func newClient() *protonmail.Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenAndServeSMTP(addr string, debug bool, authManager *auth.Manager) error {
|
func listenAndServeSMTP(addr string, debug bool, authManager *auth.Manager, tlsConfig *tls.Config) error {
|
||||||
be := smtpbackend.New(authManager)
|
be := smtpbackend.New(authManager)
|
||||||
s := smtp.NewServer(be)
|
s := smtp.NewServer(be)
|
||||||
s.Addr = addr
|
s.Addr = addr
|
||||||
s.Domain = "localhost" // TODO: make this configurable
|
s.Domain = "localhost" // TODO: make this configurable
|
||||||
s.AllowInsecureAuth = true // TODO: remove this
|
s.AllowInsecureAuth = tlsConfig == nil
|
||||||
|
s.TLSConfig = tlsConfig
|
||||||
if debug {
|
if debug {
|
||||||
s.Debug = os.Stdout
|
s.Debug = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.TLSConfig != nil {
|
||||||
|
log.Println("SMTP server listening with TLS on", s.Addr)
|
||||||
|
return s.ListenAndServeTLS()
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("SMTP server listening on", s.Addr)
|
log.Println("SMTP server listening on", s.Addr)
|
||||||
return s.ListenAndServe()
|
return s.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenAndServeIMAP(addr string, debug bool, authManager *auth.Manager, eventsManager *events.Manager) error {
|
func listenAndServeIMAP(addr string, debug bool, authManager *auth.Manager, eventsManager *events.Manager, tlsConfig *tls.Config) error {
|
||||||
be := imapbackend.New(authManager, eventsManager)
|
be := imapbackend.New(authManager, eventsManager)
|
||||||
s := imapserver.New(be)
|
s := imapserver.New(be)
|
||||||
s.Addr = addr
|
s.Addr = addr
|
||||||
s.AllowInsecureAuth = true // TODO: remove this
|
s.AllowInsecureAuth = tlsConfig == nil
|
||||||
|
s.TLSConfig = tlsConfig
|
||||||
if debug {
|
if debug {
|
||||||
s.Debug = os.Stdout
|
s.Debug = os.Stdout
|
||||||
}
|
}
|
||||||
|
@ -65,15 +74,21 @@ func listenAndServeIMAP(addr string, debug bool, authManager *auth.Manager, even
|
||||||
s.Enable(imapspacialuse.NewExtension())
|
s.Enable(imapspacialuse.NewExtension())
|
||||||
s.Enable(imapmove.NewExtension())
|
s.Enable(imapmove.NewExtension())
|
||||||
|
|
||||||
|
if s.TLSConfig != nil {
|
||||||
|
log.Println("IMAP server listening with TLS on", s.Addr)
|
||||||
|
return s.ListenAndServeTLS()
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("IMAP server listening on", s.Addr)
|
log.Println("IMAP server listening on", s.Addr)
|
||||||
return s.ListenAndServe()
|
return s.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenAndServeCardDAV(addr string, authManager *auth.Manager, eventsManager *events.Manager) error {
|
func listenAndServeCardDAV(addr string, authManager *auth.Manager, eventsManager *events.Manager, tlsConfig *tls.Config) error {
|
||||||
handlers := make(map[string]http.Handler)
|
handlers := make(map[string]http.Handler)
|
||||||
|
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
resp.Header().Set("WWW-Authenticate", "Basic")
|
resp.Header().Set("WWW-Authenticate", "Basic")
|
||||||
|
|
||||||
|
@ -108,6 +123,11 @@ func listenAndServeCardDAV(addr string, authManager *auth.Manager, eventsManager
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.TLSConfig != nil {
|
||||||
|
log.Println("CardDAV server listening with TLS on", s.Addr)
|
||||||
|
return s.ListenAndServeTLS("", "")
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("CardDAV server listening on", s.Addr)
|
log.Println("CardDAV server listening on", s.Addr)
|
||||||
return s.ListenAndServe()
|
return s.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
@ -147,7 +167,13 @@ Global options:
|
||||||
-imap-port example.com
|
-imap-port example.com
|
||||||
IMAP port on which hydroxide listens, defaults to 1143
|
IMAP port on which hydroxide listens, defaults to 1143
|
||||||
-carddav-port example.com
|
-carddav-port example.com
|
||||||
CardDAV port on which hydroxide listens, defaults to 8080`
|
CardDAV port on which hydroxide listens, defaults to 8080
|
||||||
|
-tls-cert /path/to/cert.pem
|
||||||
|
Path to the certificate to use for incoming connections (Optional)
|
||||||
|
-tls-key /path/to/key.pem
|
||||||
|
Path to the certificate key to use for incoming connections (Optional)
|
||||||
|
-tls-client-ca /path/to/ca.pem
|
||||||
|
If set, clients must provide a certificate signed by the given CA (Optional)`
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.BoolVar(&debug, "debug", false, "Enable debug logs")
|
flag.BoolVar(&debug, "debug", false, "Enable debug logs")
|
||||||
|
@ -161,6 +187,10 @@ func main() {
|
||||||
carddavHost := flag.String("carddav-host", "127.0.0.1", "Allowed CardDAV email hostname on which hydroxide listens, defaults to 127.0.0.1")
|
carddavHost := flag.String("carddav-host", "127.0.0.1", "Allowed CardDAV email hostname on which hydroxide listens, defaults to 127.0.0.1")
|
||||||
carddavPort := flag.String("carddav-port", "8080", "CardDAV port on which hydroxide listens, defaults to 8080")
|
carddavPort := flag.String("carddav-port", "8080", "CardDAV port on which hydroxide listens, defaults to 8080")
|
||||||
|
|
||||||
|
tlsCert := flag.String("tls-cert", "", "Path to the certificate to use for incoming connections")
|
||||||
|
tlsCertKey := flag.String("tls-key", "", "Path to the certificate key to use for incoming connections")
|
||||||
|
tlsClientCA := flag.String("tls-client-ca", "", "If set, clients must provide a certificate signed by the given CA")
|
||||||
|
|
||||||
authCmd := flag.NewFlagSet("auth", flag.ExitOnError)
|
authCmd := flag.NewFlagSet("auth", flag.ExitOnError)
|
||||||
exportSecretKeysCmd := flag.NewFlagSet("export-secret-keys", flag.ExitOnError)
|
exportSecretKeysCmd := flag.NewFlagSet("export-secret-keys", flag.ExitOnError)
|
||||||
importMessagesCmd := flag.NewFlagSet("import-messages", flag.ExitOnError)
|
importMessagesCmd := flag.NewFlagSet("import-messages", flag.ExitOnError)
|
||||||
|
@ -172,6 +202,11 @@ func main() {
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
tlsConfig, err := config.TLS(*tlsCert, *tlsCertKey, *tlsClientCA)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
cmd := flag.Arg(0)
|
cmd := flag.Arg(0)
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "auth":
|
case "auth":
|
||||||
|
@ -412,17 +447,17 @@ func main() {
|
||||||
case "smtp":
|
case "smtp":
|
||||||
addr := *smtpHost + ":" + *smtpPort
|
addr := *smtpHost + ":" + *smtpPort
|
||||||
authManager := auth.NewManager(newClient)
|
authManager := auth.NewManager(newClient)
|
||||||
log.Fatal(listenAndServeSMTP(addr, debug, authManager))
|
log.Fatal(listenAndServeSMTP(addr, debug, authManager, tlsConfig))
|
||||||
case "imap":
|
case "imap":
|
||||||
addr := *imapHost + ":" + *imapPort
|
addr := *imapHost + ":" + *imapPort
|
||||||
authManager := auth.NewManager(newClient)
|
authManager := auth.NewManager(newClient)
|
||||||
eventsManager := events.NewManager()
|
eventsManager := events.NewManager()
|
||||||
log.Fatal(listenAndServeIMAP(addr, debug, authManager, eventsManager))
|
log.Fatal(listenAndServeIMAP(addr, debug, authManager, eventsManager, tlsConfig))
|
||||||
case "carddav":
|
case "carddav":
|
||||||
addr := *carddavHost + ":" + *carddavPort
|
addr := *carddavHost + ":" + *carddavPort
|
||||||
authManager := auth.NewManager(newClient)
|
authManager := auth.NewManager(newClient)
|
||||||
eventsManager := events.NewManager()
|
eventsManager := events.NewManager()
|
||||||
log.Fatal(listenAndServeCardDAV(addr, authManager, eventsManager))
|
log.Fatal(listenAndServeCardDAV(addr, authManager, eventsManager, tlsConfig))
|
||||||
case "serve":
|
case "serve":
|
||||||
smtpAddr := *smtpHost + ":" + *smtpPort
|
smtpAddr := *smtpHost + ":" + *smtpPort
|
||||||
imapAddr := *imapHost + ":" + *imapPort
|
imapAddr := *imapHost + ":" + *imapPort
|
||||||
|
@ -433,13 +468,13 @@ func main() {
|
||||||
|
|
||||||
done := make(chan error, 3)
|
done := make(chan error, 3)
|
||||||
go func() {
|
go func() {
|
||||||
done <- listenAndServeSMTP(smtpAddr, debug, authManager)
|
done <- listenAndServeSMTP(smtpAddr, debug, authManager, tlsConfig)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
done <- listenAndServeIMAP(imapAddr, debug, authManager, eventsManager)
|
done <- listenAndServeIMAP(imapAddr, debug, authManager, eventsManager, tlsConfig)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
done <- listenAndServeCardDAV(carddavAddr, authManager, eventsManager)
|
done <- listenAndServeCardDAV(carddavAddr, authManager, eventsManager, tlsConfig)
|
||||||
}()
|
}()
|
||||||
log.Fatal(<-done)
|
log.Fatal(<-done)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TLS(certPath string, keyPath string, clientCAPath string) (*tls.Config, error) {
|
||||||
|
tlsConfig := &tls.Config{}
|
||||||
|
|
||||||
|
if certPath != "" && keyPath != "" {
|
||||||
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error: unable load key pair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientCAPath != "" {
|
||||||
|
data, err := ioutil.ReadFile(clientCAPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error: unable read CA file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
pool.AppendCertsFromPEM(data)
|
||||||
|
|
||||||
|
tlsConfig.ClientCAs = pool
|
||||||
|
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsConfig, nil
|
||||||
|
}
|
Loading…
Reference in New Issue