|
@@ -4,11 +4,118 @@ import (
|
|
|
"crypto/tls"
|
|
|
"os"
|
|
|
|
|
|
+ "github.com/sagernet/sing-box/adapter"
|
|
|
+ "github.com/sagernet/sing-box/log"
|
|
|
"github.com/sagernet/sing-box/option"
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
+
|
|
|
+ "github.com/fsnotify/fsnotify"
|
|
|
)
|
|
|
|
|
|
-func NewTLSConfig(options option.InboundTLSOptions) (*tls.Config, error) {
|
|
|
+var _ adapter.Service = (*TLSConfig)(nil)
|
|
|
+
|
|
|
+type TLSConfig struct {
|
|
|
+ config *tls.Config
|
|
|
+ logger log.Logger
|
|
|
+ certificate []byte
|
|
|
+ key []byte
|
|
|
+ certificatePath string
|
|
|
+ keyPath string
|
|
|
+ watcher *fsnotify.Watcher
|
|
|
+}
|
|
|
+
|
|
|
+func (c *TLSConfig) Config() *tls.Config {
|
|
|
+ return c.config
|
|
|
+}
|
|
|
+
|
|
|
+func (c *TLSConfig) Start() error {
|
|
|
+ if c.certificatePath == "" && c.keyPath == "" {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ err := c.startWatcher()
|
|
|
+ if err != nil {
|
|
|
+ c.logger.Warn("create fsnotify watcher: ", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (c *TLSConfig) startWatcher() error {
|
|
|
+ watcher, err := fsnotify.NewWatcher()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if c.certificatePath != "" {
|
|
|
+ err = watcher.Add(c.certificatePath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if c.keyPath != "" {
|
|
|
+ err = watcher.Add(c.keyPath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ c.watcher = watcher
|
|
|
+ go c.loopUpdate()
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (c *TLSConfig) loopUpdate() {
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case event, ok := <-c.watcher.Events:
|
|
|
+ if !ok {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if event.Op&fsnotify.Write != fsnotify.Write {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ err := c.reloadKeyPair()
|
|
|
+ if err != nil {
|
|
|
+ c.logger.Error(E.Cause(err, "reload TLS key pair"))
|
|
|
+ }
|
|
|
+ case err, ok := <-c.watcher.Errors:
|
|
|
+ if !ok {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ c.logger.Error(E.Cause(err, "fsnotify error"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (c *TLSConfig) reloadKeyPair() error {
|
|
|
+ if c.certificatePath != "" {
|
|
|
+ certificate, err := os.ReadFile(c.certificatePath)
|
|
|
+ if err != nil {
|
|
|
+ return E.Cause(err, "reload certificate from ", c.certificatePath)
|
|
|
+ }
|
|
|
+ c.certificate = certificate
|
|
|
+ }
|
|
|
+ if c.keyPath != "" {
|
|
|
+ key, err := os.ReadFile(c.keyPath)
|
|
|
+ if err != nil {
|
|
|
+ return E.Cause(err, "reload key from ", c.keyPath)
|
|
|
+ }
|
|
|
+ c.key = key
|
|
|
+ }
|
|
|
+ keyPair, err := tls.X509KeyPair(c.certificate, c.key)
|
|
|
+ if err != nil {
|
|
|
+ return E.Cause(err, "reload key pair")
|
|
|
+ }
|
|
|
+ c.config.Certificates = []tls.Certificate{keyPair}
|
|
|
+ c.logger.Info("reloaded TLS certificate")
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (c *TLSConfig) Close() error {
|
|
|
+ if c.watcher != nil {
|
|
|
+ return c.watcher.Close()
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func NewTLSConfig(logger log.Logger, options option.InboundTLSOptions) (*TLSConfig, error) {
|
|
|
if !options.Enabled {
|
|
|
return nil, nil
|
|
|
}
|
|
@@ -76,5 +183,12 @@ func NewTLSConfig(options option.InboundTLSOptions) (*tls.Config, error) {
|
|
|
return nil, E.Cause(err, "parse x509 key pair")
|
|
|
}
|
|
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
|
|
- return &tlsConfig, nil
|
|
|
+ return &TLSConfig{
|
|
|
+ config: &tlsConfig,
|
|
|
+ logger: logger,
|
|
|
+ certificate: certificate,
|
|
|
+ key: key,
|
|
|
+ certificatePath: options.CertificatePath,
|
|
|
+ keyPath: options.KeyPath,
|
|
|
+ }, nil
|
|
|
}
|