reload certificate on SIGHUP
This commit is contained in:
parent
2e196465b6
commit
802bf1eb6d
4 changed files with 56 additions and 33 deletions
11
README.md
11
README.md
|
@ -6,13 +6,12 @@ protocol](https://gemini.circumlunar.space/docs/specification.gmi). Why built ye
|
|||
server? Because it's educational and that's the spirit of the protocol.
|
||||
|
||||
Features
|
||||
- **zero conf**, if no certificate is available, gmifs can generates self-signed certs
|
||||
- **zero conf**, if no certificate is available, gmifs generates a self-signed cert
|
||||
- **zero dependencies**, Go standard library only
|
||||
- directory listing support
|
||||
- only modern tls ciphers (from [Mozilla's TLS ciphers recommendations](https://statics.tls.security.mozilla.org/server-side-tls-conf.json))
|
||||
- concurrent requests limiter
|
||||
- reloads ssl certs and flushes/reopens log files on SIGHUP
|
||||
- single file gemini implementation, focus on simplicity, no bells and whistles
|
||||
- directory listing support `-autoindex`
|
||||
- reloads ssl certs and reopens log files on SIGHUP, e.g. after Let's Encrypt renewal
|
||||
- KISS, single file gemini implementation, handler func in main
|
||||
- modern tls ciphers (from [Mozilla's TLS ciphers recommendations](https://statics.tls.security.mozilla.org/server-side-tls-conf.json))
|
||||
|
||||
This tool is used alongside the markdown to gemtext converter
|
||||
[md2gmi](https://github.com/n0x1m/md2gmi).
|
||||
|
|
|
@ -91,6 +91,8 @@ type Server struct {
|
|||
Logger *log.Logger
|
||||
|
||||
TLSConfig *tls.Config
|
||||
TLSConfigLoader func() (*tls.Config, error)
|
||||
|
||||
Handler Handler // handler to invoke
|
||||
ReadTimeout time.Duration
|
||||
MaxOpenConns int
|
||||
|
@ -115,14 +117,30 @@ func (s *Server) logf(format string, v ...interface{}) {
|
|||
s.log(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (s *Server) loadTLS() (err error) {
|
||||
s.TLSConfig, err = s.TLSConfigLoader()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() error {
|
||||
err := s.loadTLS()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hup := make(chan os.Signal, 1)
|
||||
signal.Notify(hup, syscall.SIGHUP)
|
||||
go func() {
|
||||
for {
|
||||
<-hup
|
||||
s.log("reloading certificate")
|
||||
if s.listener != nil {
|
||||
// TODO: reload TLSConfig
|
||||
err := s.loadTLS()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "critical: failed to load tls certs: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
s.listener.Close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// GenX509KeyPair generates a TLS keypair with one week validity.
|
||||
func GenX509KeyPair(host string) (tls.Certificate, error) {
|
||||
func GenX509KeyPair(host string, daysvalid int) (tls.Certificate, error) {
|
||||
now := time.Now()
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(now.Unix()),
|
||||
|
@ -20,7 +20,7 @@ func GenX509KeyPair(host string) (tls.Certificate, error) {
|
|||
Organization: []string{host},
|
||||
},
|
||||
NotBefore: now,
|
||||
NotAfter: now.AddDate(0, 0, 7),
|
||||
NotAfter: now.AddDate(0, 0, daysvalid),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
|
|
38
main.go
38
main.go
|
@ -27,6 +27,7 @@ const (
|
|||
defaultCertPath = ""
|
||||
defaultKeyPath = ""
|
||||
|
||||
autoCertDaysValid = 7
|
||||
shutdownTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
|
@ -61,20 +62,6 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
var cert tls.Certificate
|
||||
if crt != "" && key != "" {
|
||||
log.Println("loading certificate from", crt)
|
||||
cert, err = tls.LoadX509KeyPair(crt, key)
|
||||
if err != nil {
|
||||
log.Fatalf("server: loadkeys: %s", err)
|
||||
}
|
||||
} else if host != "" {
|
||||
log.Println("generating self-signed temporary certificate")
|
||||
cert, err = gemini.GenX509KeyPair(host)
|
||||
if err != nil {
|
||||
log.Fatalf("server: loadkeys: %s", err)
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
fmt.Fprintf(os.Stderr, "a keypair with cert and key or at least a common name (hostname) is required for sni\n")
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
|
@ -84,12 +71,12 @@ func main() {
|
|||
|
||||
mux := gemini.NewMux()
|
||||
mux.Use(middleware.Logger(flogger))
|
||||
mux.Handle(gemini.HandlerFunc(fileserver.Serve(root, true)))
|
||||
mux.Handle(gemini.HandlerFunc(fileserver.Serve(root, autoindex)))
|
||||
|
||||
server := &gemini.Server{
|
||||
Addr: addr,
|
||||
Hostname: host,
|
||||
TLSConfig: gemini.TLSConfig(host, cert),
|
||||
TLSConfigLoader: setupCertificate(crt, key, host),
|
||||
Handler: mux,
|
||||
MaxOpenConns: maxconns,
|
||||
ReadTimeout: time.Duration(timeout) * time.Second,
|
||||
|
@ -118,6 +105,25 @@ func main() {
|
|||
cancel()
|
||||
}
|
||||
|
||||
func setupCertificate(crt, key, host string) func() (*tls.Config, error) {
|
||||
return func() (*tls.Config, error) {
|
||||
if crt != "" && key != "" {
|
||||
cert, err := tls.LoadX509KeyPair(crt, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gemini.TLSConfig(host, cert), nil
|
||||
}
|
||||
|
||||
log.Println("generating self-signed temporary certificate")
|
||||
cert, err := gemini.GenX509KeyPair(host, autoCertDaysValid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gemini.TLSConfig(host, cert), nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupLogger(dir, filename string) (*log.Logger, error) {
|
||||
logger := log.New(os.Stdout, "", log.LUTC|log.Ldate|log.Ltime)
|
||||
|
||||
|
|
Loading…
Reference in a new issue