github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/app_listenAndServeAutoTLS.go (about) 1 // Copyright 2017-present Kirill Danshin and Gramework contributors 2 // Copyright 2019-present Highload LTD (UK CN: 11893420) 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 11 package gramework 12 13 import ( 14 "crypto/tls" 15 "fmt" 16 "math/rand" 17 "net" 18 "runtime" 19 "time" 20 21 "golang.org/x/crypto/acme/autocert" 22 ) 23 24 func getDefaultTLSConfig() *tls.Config { 25 return &tls.Config{ 26 DynamicRecordSizingDisabled: true, 27 ClientSessionCache: tls.NewLRUClientSessionCache(1024 * runtime.GOMAXPROCS(0)), 28 } 29 } 30 31 func getCachePath(dev ...bool) string { 32 p := "./tls-gramecache" 33 if len(dev) > 0 && dev[0] { 34 p += ".dev" 35 } 36 37 return p 38 } 39 40 // ListenAndServeAutoTLS serves TLS requests 41 func (app *App) ListenAndServeAutoTLS(addr string, cachePath ...string) error { 42 if len(app.TLSEmails) == 0 { 43 return ErrTLSNoEmails 44 } 45 46 addr, err := normalizeTLSAddr(addr) 47 if err != nil { 48 app.internalLog.Errorf("Bad address %q: %s", addr, err) 49 } 50 51 ln, err := net.Listen("tcp", addr) 52 if err != nil { 53 app.internalLog.Errorf("Can't serve %q: %s", addr, err) 54 return err 55 } 56 57 letscache := getCachePath() 58 if len(cachePath) > 0 { 59 letscache = cachePath[0] 60 } 61 62 s := rand.NewSource(time.Now().Unix()) 63 r := rand.New(s) 64 m := autocert.Manager{ 65 Prompt: autocert.AcceptTOS, 66 Email: app.TLSEmails[r.Intn(len(app.TLSEmails))], 67 } 68 69 if letscache != "" { 70 m.Cache = autocert.DirCache(letscache) 71 } 72 73 tlsConfig := getDefaultTLSConfig() 74 tlsConfig.GetCertificate = func(hello *tls.ClientHelloInfo) (cert *tls.Certificate, err error) { 75 if len(hello.ServerName) == 0 || hello.ServerName == localhost { 76 hello.ServerName = localhost 77 cert, err = selfSignedCertificate(hello) 78 } else { 79 cert, err = m.GetCertificate(hello) 80 } 81 82 if err != nil { 83 app.internalLog.Errorf("Can't get cert for %q: %s", hello.ServerName, err) 84 } 85 86 return cert, err 87 } 88 89 tlsLn := tls.NewListener(ln, tlsConfig) 90 checks() 91 92 l := app.internalLog.WithField("bind", addr) 93 l.Info("Starting HTTPS") 94 95 srv := app.copyServer() 96 app.runningServersMu.Lock() 97 app.runningServers = append(app.runningServers, runningServerInfo{ 98 bind: addr, 99 srv: srv, 100 }) 101 app.runningServersMu.Unlock() 102 if err = srv.Serve(tlsLn); err != nil { 103 app.internalLog.Errorf("Can't serve: %s", err) 104 } 105 106 return err 107 } 108 109 // ListenAndServeAutoTLSDev serves non-production grade TLS requests. Supports localhost.localdomain. 110 // Deprecated: use ListenAndServeAutoTLS() instead 111 func (app *App) ListenAndServeAutoTLSDev(addr string, cachePath ...string) error { 112 return app.ListenAndServeAutoTLS(addr, cachePath...) 113 } 114 115 func normalizeTLSAddr(addr string) (string, error) { 116 host, port, err := net.SplitHostPort(addr) 117 118 if err != nil && net.ParseIP(host) == nil { 119 return addr, fmt.Errorf("invalid address %q: %s", addr, err) 120 } 121 122 if len(port) == 0 { 123 addr = net.JoinHostPort(host, "443") 124 } 125 126 return addr, nil 127 }