diff --git a/config-sample.yaml b/config-sample.yaml index 989a3254d8..16db9e91c1 100644 --- a/config-sample.yaml +++ b/config-sample.yaml @@ -210,3 +210,7 @@ keys: # num_tracks: -1 # # defaults to 1 GB/s, or just under 10 Gbps # bytes_per_sec: 1_000_000_000 + +# autocert: +# enabled: true +# cache_dir: /tmp/certs diff --git a/go.mod b/go.mod index 7f3b4e6656..4b43a951ed 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( github.com/urfave/negroni v1.0.0 go.uber.org/atomic v1.9.0 go.uber.org/zap v1.22.0 + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 @@ -84,7 +85,6 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect diff --git a/pkg/config/config.go b/pkg/config/config.go index f263d4dc60..8732e6bf7e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -44,6 +44,7 @@ type Config struct { KeyFile string `yaml:"key_file,omitempty"` Keys map[string]string `yaml:"keys,omitempty"` Region string `yaml:"region,omitempty"` + Autocert AutocertConfig `yaml:"autocert,omitempty"` // LogLevel is deprecated LogLevel string `yaml:"log_level,omitempty"` Logging LoggingConfig `yaml:"logging,omitempty"` @@ -374,3 +375,9 @@ func (conf *Config) unmarshalKeys(keys string) error { } return nil } + +type AutocertConfig struct { + Domain string `yaml:"domain,omitempty"` + Enabled bool `yaml:"enabled,omitempty"` + CacheDir string `yaml:"cache_dir,omitempty"` +} diff --git a/pkg/service/server.go b/pkg/service/server.go index 7d398a2f09..45ce3f6158 100644 --- a/pkg/service/server.go +++ b/pkg/service/server.go @@ -2,6 +2,7 @@ package service import ( "context" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -16,6 +17,8 @@ import ( "github.com/rs/cors" "github.com/urfave/negroni" "go.uber.org/atomic" + "golang.org/x/crypto/acme" + "golang.org/x/crypto/acme/autocert" "golang.org/x/sync/errgroup" "github.com/livekit/livekit-server/pkg/config" @@ -166,9 +169,18 @@ func (s *LivekitServer) Start() error { listeners := make([]net.Listener, 0) promListeners := make([]net.Listener, 0) for _, addr := range addresses { - ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, s.config.Port)) - if err != nil { - return err + var ln net.Listener + var err error + if s.config.Autocert.Enabled && s.config.Autocert.Domain != "" { + ln, err = asTlsListener(s.config.Autocert.CacheDir, fmt.Sprintf("0.0.0.0:%d", s.config.Port), s.config.Autocert.Domain) + if err != nil { + return err + } + } else { + ln, err = net.Listen("tcp", fmt.Sprintf("%s:%d", addr, s.config.Port)) + if err != nil { + return err + } } listeners = append(listeners, ln) @@ -336,3 +348,26 @@ func configureMiddlewares(handler http.Handler, middlewares ...negroni.Handler) n.UseHandler(handler) return n } + +func asTlsListener(cache string, addr string, hosts ...string) (lnTls net.Listener, err error) { + m := &autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist(hosts...), + Cache: autocert.DirCache(cache), + } + + cfg := &tls.Config{ + GetCertificate: m.GetCertificate, + NextProtos: []string{ + "http/1.1", acme.ALPNProto, + }, + } + + ln, err := net.Listen("tcp4", addr) + if err != nil { + return + } + + lnTls = tls.NewListener(ln, cfg) + return +} diff --git a/pkg/service/turn.go b/pkg/service/turn.go index a11bcdb20f..ee82b3fc4e 100644 --- a/pkg/service/turn.go +++ b/pkg/service/turn.go @@ -61,25 +61,38 @@ func NewTurnServer(conf *config.Config, authHandler turn.AuthHandler) (*turn.Ser } if !turnConf.ExternalTLS { - cert, err := tls.LoadX509KeyPair(turnConf.CertFile, turnConf.KeyFile) - if err != nil { - return nil, errors.Wrap(err, "TURN tls cert required") - } - - tlsListener, err := tls.Listen("tcp4", "0.0.0.0:"+strconv.Itoa(turnConf.TLSPort), - &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{cert}, - }) - if err != nil { - return nil, errors.Wrap(err, "could not listen on TURN TCP port") + if conf.Autocert.Enabled { + tlsListener, err := asTlsListener(conf.Autocert.CacheDir, "0.0.0.0:"+strconv.Itoa(turnConf.TLSPort), turnConf.Domain) + if err != nil { + return nil, errors.Wrap(err, "could not run autocert on TURN TCP port") + } + + listenerConfig := turn.ListenerConfig{ + Listener: tlsListener, + RelayAddressGenerator: relayAddrGen, + } + serverConfig.ListenerConfigs = append(serverConfig.ListenerConfigs, listenerConfig) + } else { + cert, err := tls.LoadX509KeyPair(turnConf.CertFile, turnConf.KeyFile) + if err != nil { + return nil, errors.Wrap(err, "TURN tls cert required") + } + + tlsListener, err := tls.Listen("tcp4", "0.0.0.0:"+strconv.Itoa(turnConf.TLSPort), + &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{cert}, + }) + if err != nil { + return nil, errors.Wrap(err, "could not listen on TURN TCP port") + } + + listenerConfig := turn.ListenerConfig{ + Listener: tlsListener, + RelayAddressGenerator: relayAddrGen, + } + serverConfig.ListenerConfigs = append(serverConfig.ListenerConfigs, listenerConfig) } - - listenerConfig := turn.ListenerConfig{ - Listener: tlsListener, - RelayAddressGenerator: relayAddrGen, - } - serverConfig.ListenerConfigs = append(serverConfig.ListenerConfigs, listenerConfig) } else { tcpListener, err := net.Listen("tcp4", "0.0.0.0:"+strconv.Itoa(turnConf.TLSPort)) if err != nil {