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 conf: m.TLSConfig(), 76 } 77 ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") 78 return ln 79} 80 81type listener struct { 82 conf *tls.Config 83 84 tcpListener net.Listener 85 tcpListenErr error 86} 87 88func (ln *listener) Accept() (net.Conn, error) { 89 if ln.tcpListenErr != nil { 90 return nil, ln.tcpListenErr 91 } 92 conn, err := ln.tcpListener.Accept() 93 if err != nil { 94 return nil, err 95 } 96 tcpConn := conn.(*net.TCPConn) 97 98 // Because Listener is a convenience function, help out with 99 // this too. This is not possible for the caller to set once 100 // we return a *tcp.Conn wrapping an inaccessible net.Conn. 101 // If callers don't want this, they can do things the manual 102 // way and tweak as needed. But this is what net/http does 103 // itself, so copy that. If net/http changes, we can change 104 // here too. 105 tcpConn.SetKeepAlive(true) 106 tcpConn.SetKeepAlivePeriod(3 * time.Minute) 107 108 return tls.Server(tcpConn, ln.conf), nil 109} 110 111func (ln *listener) Addr() net.Addr { 112 if ln.tcpListener != nil { 113 return ln.tcpListener.Addr() 114 } 115 // net.Listen failed. Return something non-nil in case callers 116 // call Addr before Accept: 117 return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443} 118} 119 120func (ln *listener) Close() error { 121 if ln.tcpListenErr != nil { 122 return ln.tcpListenErr 123 } 124 return ln.tcpListener.Close() 125} 126 127func homeDir() string { 128 if runtime.GOOS == "windows" { 129 return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 130 } 131 if h := os.Getenv("HOME"); h != "" { 132 return h 133 } 134 return "/" 135} 136 137func cacheDir() string { 138 const base = "golang-autocert" 139 switch runtime.GOOS { 140 case "darwin": 141 return filepath.Join(homeDir(), "Library", "Caches", base) 142 case "windows": 143 for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { 144 if v := os.Getenv(ev); v != "" { 145 return filepath.Join(v, base) 146 } 147 } 148 // Worst case: 149 return filepath.Join(homeDir(), base) 150 } 151 if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { 152 return filepath.Join(xdg, base) 153 } 154 return filepath.Join(homeDir(), ".cache", base) 155} 156