diff --git a/README.md b/README.md index a67889f..236f9b7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ This is the server program of [rtty](https://github.com/zhaojh329/rtty) statik -src=frontend/dist ## Authorization +### Token Generate a token $ rttys token @@ -40,6 +41,11 @@ Use token $ rttys run -t 34762d07637276694b938d23f10d7164 +### mTLS +You can enable mTLS by specifying device CA storage (valid file) in config file or from CLI (variable ssl-devs). +Appending to CA storage is possible on-the-fly, you can reload CA certs by sendig SIGUSR1 signal. +Device(s) without valid CA in storage will be disconnected in TLS handshake. + ## Running as a Linux service Move the rttys binary into /usr/local/bin/ diff --git a/broker.go b/broker.go index 0a72f3b..411b5ac 100644 --- a/broker.go +++ b/broker.go @@ -2,6 +2,7 @@ package main import ( "encoding/binary" + "crypto/x509" "github.com/gorilla/websocket" jsoniter "github.com/json-iterator/go" @@ -32,6 +33,7 @@ type broker struct { userMessage chan *usrMessage cmdMessage chan []byte webMessage chan *webResp + devCertPool *x509.CertPool } func newBroker(cfg *config.Config) *broker { diff --git a/build.sh b/build.sh index 65b9d20..e03ba75 100755 --- a/build.sh +++ b/build.sh @@ -5,7 +5,7 @@ GitCommit=$(git log --pretty=format:"%h" -1) BuildTime=$(date +%FT%T%z) [ $# -lt 2 ] && { - echo "Usage: $0 linux amb64" + echo "Usage: $0 linux amd64" exit 1 } diff --git a/config/config.go b/config/config.go index 3e74ebf..441a1a4 100644 --- a/config/config.go +++ b/config/config.go @@ -19,6 +19,7 @@ type Config struct { WebPort int SslCert string SslKey string + SslDevices string HTTPUsername string HTTPPassword string Token string @@ -49,6 +50,7 @@ func Parse(c *cli.Context) *Config { WebRedirURL: c.String("web-redir-url"), SslCert: c.String("ssl-cert"), SslKey: c.String("ssl-key"), + SslDevices: c.String("ssl-devs"), HTTPUsername: c.String("http-username"), HTTPPassword: c.String("http-password"), Token: c.String("token"), @@ -74,6 +76,7 @@ func Parse(c *cli.Context) *Config { getConfigOpt(yamlCfg, "web-redir-url", &cfg.WebRedirURL) getConfigOpt(yamlCfg, "ssl-cert", &cfg.SslCert) getConfigOpt(yamlCfg, "ssl-key", &cfg.SslKey) + getConfigOpt(yamlCfg, "ssl-devs", &cfg.SslDevices) getConfigOpt(yamlCfg, "http-username", &cfg.HTTPUsername) getConfigOpt(yamlCfg, "http-password", &cfg.HTTPPassword) getConfigOpt(yamlCfg, "token", &cfg.Token) diff --git a/device.go b/device.go index c3788a2..d5da12f 100644 --- a/device.go +++ b/device.go @@ -6,8 +6,10 @@ import ( "context" "crypto/rand" "crypto/tls" + "crypto/x509" "encoding/binary" "io" + "io/ioutil" "net" "strings" "sync" @@ -272,6 +274,18 @@ func listenDevice(br *broker) { tlsConfig.Time = time.Now tlsConfig.Rand = rand.Reader + caCert, err := ioutil.ReadFile(cfg.SslDevices) + if err != nil { + log.Warn().Msgf("mTLS not used: %s", err.Error()) + } else { + br.devCertPool = x509.NewCertPool() + br.devCertPool.AppendCertsFromPEM(caCert) + + // Create the TLS Config with the CA pool and enable Client certificate validation + tlsConfig.ClientCAs = br.devCertPool + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert + } + ln = tls.NewListener(ln, tlsConfig) log.Info().Msgf("Listen device on: %s SSL on", cfg.AddrDev) } else { diff --git a/main.go b/main.go index 2ceb812..daa7010 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,10 @@ package main import ( "fmt" "os" + "os/signal" + "syscall" "runtime" + "io/ioutil" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" @@ -17,6 +20,8 @@ func runRttys(c *cli.Context) { rlog.SetPath(c.String("log")) cfg := config.Parse(c) + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGUSR1) if cfg.HTTPUsername == "" { fmt.Println("You must configure the http username by commandline or config file") @@ -46,6 +51,26 @@ func runRttys(c *cli.Context) { listenDeviceWeb(br) httpStart(br) + go func() { + for { + s := <-sigs + switch s { + case syscall.SIGUSR1: + if br.devCertPool != nil { + log.Info().Msg("Reload certs for mTLS") + caCert, err := ioutil.ReadFile(cfg.SslDevices) + if err != nil { + log.Info().Msgf("mTLS update faled: %s", err.Error()) + } else { + br.devCertPool.AppendCertsFromPEM(caCert) + } + } else { + log.Warn().Msg("Reload certs failed: mTLS not used") + } + } + } + }() + select {} } @@ -105,6 +130,11 @@ func main() { Value: "", Usage: "ssl key file Path", }, + &cli.StringFlag{ + Name: "ssl-devs", + Value: "", + Usage: "mtls CA storage in PEM file Path", + }, &cli.StringFlag{ Name: "http-username", Value: "",