1// Copyright 2017 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package autocert 6 7import ( 8 "crypto/tls" 9 "log" 10 "net" 11 "os" 12 "path/filepath" 13 "runtime" 14 "time" 15) 16 17// NewListener returns a net.Listener that listens on the standard TLS 18// port (443) on all interfaces and returns *tls.Conn connections with 19// LetsEncrypt certificates for the provided domain or domains. 20// 21// It enables one-line HTTPS servers: 22// 23// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler)) 24// 25// NewListener is a convenience function for a common configuration. 26// More complex or custom configurations can use the autocert.Manager 27// type instead. 28// 29// Use of this function implies acceptance of the LetsEncrypt Terms of 30// Service. If domains is not empty, the provided domains are passed 31// to HostWhitelist. If domains is empty, the listener will do 32// LetsEncrypt challenges for any requested domain, which is not 33// recommended. 34// 35// Certificates are cached in a "golang-autocert" directory under an 36// operating system-specific cache or temp directory. This may not 37// be suitable for servers spanning multiple machines. 38// 39// The returned listener uses a *tls.Config that enables HTTP/2, and 40// should only be used with servers that support HTTP/2. 41// 42// The returned Listener also enables TCP keep-alives on the accepted 43// connections. The returned *tls.Conn are returned before their TLS 44// handshake has completed. 45func NewListener(domains ...string) net.Listener { 46 m := &Manager{ 47 Prompt: AcceptTOS, 48 } 49 if len(domains) > 0 { 50 m.HostPolicy = HostWhitelist(domains...) 51 } 52 dir := cacheDir() 53 if err := os.MkdirAll(dir, 0700); err != nil { 54 log.Printf("warning: autocert.NewListener not using a cache: %v", err) 55 } else { 56 m.Cache = DirCache(dir) 57 } 58 return m.Listener() 59} 60 61// Listener listens on the standard TLS port (443) on all interfaces 62// and returns a net.Listener returning *tls.Conn connections. 63// 64// The returned listener uses a *tls.Config that enables HTTP/2, and 65// should only be used with servers that support HTTP/2. 66// 67// The returned Listener also enables TCP keep-alives on the accepted 68// connections. The returned *tls.Conn are returned before their TLS 69// handshake has completed. 70// 71// Unlike NewListener, it is the caller's responsibility to initialize 72// the Manager m's Prompt, Cache, HostPolicy, and other desired options. 73func (m *Manager) Listener() net.Listener { 74 ln := &listener{ 75 m: m, 76 conf: m.TLSConfig(), 77 } 78 ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") 79 return ln 80} 81 82type listener struct { 83 m *Manager 84 conf *tls.Config 85 86 tcpListener net.Listener 87 tcpListenErr error 88} 89 90func (ln *listener) Accept() (net.Conn, error) { 91 if ln.tcpListenErr != nil { 92 return nil, ln.tcpListenErr 93 } 94 conn, err := ln.tcpListener.Accept() 95 if err != nil { 96 return nil, err 97 } 98 tcpConn := conn.(*net.TCPConn) 99 100 // Because Listener is a convenience function, help out with 101 // this too. This is not possible for the caller to set once 102 // we return a *tcp.Conn wrapping an inaccessible net.Conn. 103 // If callers don't want this, they can do things the manual 104 // way and tweak as needed. But this is what net/http does 105 // itself, so copy that. If net/http changes, we can change 106 // here too. 107 tcpConn.SetKeepAlive(true) 108 tcpConn.SetKeepAlivePeriod(3 * time.Minute) 109 110 return tls.Server(tcpConn, ln.conf), nil 111} 112 113func (ln *listener) Addr() net.Addr { 114 if ln.tcpListener != nil { 115 return ln.tcpListener.Addr() 116 } 117 // net.Listen failed. Return something non-nil in case callers 118 // call Addr before Accept: 119 return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443} 120} 121 122func (ln *listener) Close() error { 123 if ln.tcpListenErr != nil { 124 return ln.tcpListenErr 125 } 126 return ln.tcpListener.Close() 127} 128 129func homeDir() string { 130 if runtime.GOOS == "windows" { 131 return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 132 } 133 if h := os.Getenv("HOME"); h != "" { 134 return h 135 } 136 return "/" 137} 138 139func cacheDir() string { 140 const base = "golang-autocert" 141 switch runtime.GOOS { 142 case "darwin": 143 return filepath.Join(homeDir(), "Library", "Caches", base) 144 case "windows": 145 for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { 146 if v := os.Getenv(ev); v != "" { 147 return filepath.Join(v, base) 148 } 149 } 150 // Worst case: 151 return filepath.Join(homeDir(), base) 152 } 153 if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { 154 return filepath.Join(xdg, base) 155 } 156 return filepath.Join(homeDir(), ".cache", base) 157} 158