forked from golang/crypto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Now users can do 1-line LetsEncrypt HTTPS servers: log.Fatal(http.Serve(autocert.NewListener("example.com"), handler)) Updates golang/go#17053 Change-Id: I13fcd3985ebf6bc97a7524cceeb7641cf1b66b22 Reviewed-on: https://go-review.googlesource.com/39207 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Alex Vaghin <ddos@google.com>
- Loading branch information
Showing
2 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package autocert | ||
|
||
import ( | ||
"crypto/tls" | ||
"log" | ||
"net" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"time" | ||
) | ||
|
||
// NewListener returns a net.Listener that listens on the standard TLS | ||
// port (443) on all interfaces and returns *tls.Conn connections with | ||
// LetsEncrypt certificates for the provided domain or domains. | ||
// | ||
// It enables one-line HTTPS servers: | ||
// | ||
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler)) | ||
// | ||
// NewListener is a convenience function for a common configuration. | ||
// More complex or custom configurations can use the autocert.Manager | ||
// type instead. | ||
// | ||
// Use of this function implies acceptance of the LetsEncrypt Terms of | ||
// Service. If domains is not empty, the provided domains are passed | ||
// to HostWhitelist. If domains is empty, the listener will do | ||
// LetsEncrypt challenges for any requested domain, which is not | ||
// recommended. | ||
// | ||
// Certificates are cached in a "golang-autocert" directory under an | ||
// operating system-specific cache or temp directory. This may not | ||
// be suitable for servers spanning multiple machines. | ||
// | ||
// The returned Listener also enables TCP keep-alives on the accepted | ||
// connections. The returned *tls.Conn are returned before their TLS | ||
// handshake has completed. | ||
func NewListener(domains ...string) net.Listener { | ||
m := &Manager{ | ||
Prompt: AcceptTOS, | ||
} | ||
if len(domains) > 0 { | ||
m.HostPolicy = HostWhitelist(domains...) | ||
} | ||
dir := cacheDir() | ||
if err := os.MkdirAll(dir, 0700); err != nil { | ||
log.Printf("warning: autocert.NewListener not using a cache: %v", err) | ||
} else { | ||
m.Cache = DirCache(dir) | ||
} | ||
return m.Listener() | ||
} | ||
|
||
// Listener listens on the standard TLS port (443) on all interfaces | ||
// and returns a net.Listener returning *tls.Conn connections. | ||
// | ||
// The returned Listener also enables TCP keep-alives on the accepted | ||
// connections. The returned *tls.Conn are returned before their TLS | ||
// handshake has completed. | ||
// | ||
// Unlike NewListener, it is the caller's responsibility to initialize | ||
// the Manager m's Prompt, Cache, HostPolicy, and other desired options. | ||
func (m *Manager) Listener() net.Listener { | ||
ln := &listener{ | ||
m: m, | ||
conf: &tls.Config{ | ||
GetCertificate: m.GetCertificate, // bonus: panic on nil m | ||
}, | ||
} | ||
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") | ||
return ln | ||
} | ||
|
||
type listener struct { | ||
m *Manager | ||
conf *tls.Config | ||
|
||
tcpListener net.Listener | ||
tcpListenErr error | ||
} | ||
|
||
func (ln *listener) Accept() (net.Conn, error) { | ||
if ln.tcpListenErr != nil { | ||
return nil, ln.tcpListenErr | ||
} | ||
conn, err := ln.tcpListener.Accept() | ||
if err != nil { | ||
return nil, err | ||
} | ||
tcpConn := conn.(*net.TCPConn) | ||
|
||
// Because Listener is a convenience function, help out with | ||
// this too. This is not possible for the caller to set once | ||
// we return a *tcp.Conn wrapping an inaccessible net.Conn. | ||
// If callers don't want this, they can do things the manual | ||
// way and tweak as needed. But this is what net/http does | ||
// itself, so copy that. If net/http changes, we can change | ||
// here too. | ||
tcpConn.SetKeepAlive(true) | ||
tcpConn.SetKeepAlivePeriod(3 * time.Minute) | ||
|
||
return tls.Server(tcpConn, ln.conf), nil | ||
} | ||
|
||
func (ln *listener) Addr() net.Addr { | ||
if ln.tcpListener != nil { | ||
return ln.tcpListener.Addr() | ||
} | ||
// net.Listen failed. Return something non-nil in case callers | ||
// call Addr before Accept: | ||
return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443} | ||
} | ||
|
||
func (ln *listener) Close() error { | ||
if ln.tcpListenErr != nil { | ||
return ln.tcpListenErr | ||
} | ||
return ln.tcpListener.Close() | ||
} | ||
|
||
func homeDir() string { | ||
if runtime.GOOS == "windows" { | ||
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") | ||
} | ||
if h := os.Getenv("HOME"); h != "" { | ||
return h | ||
} | ||
return "/" | ||
} | ||
|
||
func cacheDir() string { | ||
const base = "golang-autocert" | ||
switch runtime.GOOS { | ||
case "darwin": | ||
return filepath.Join(homeDir(), "Library", "Caches", base) | ||
case "windows": | ||
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { | ||
if v := os.Getenv(ev); v != "" { | ||
return filepath.Join(v, base) | ||
} | ||
} | ||
// Worst case: | ||
return filepath.Join(homeDir(), base) | ||
} | ||
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { | ||
return filepath.Join(xdg, base) | ||
} | ||
return filepath.Join(homeDir(), ".cache", base) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package autocert_test | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
|
||
"golang.org/x/crypto/acme/autocert" | ||
) | ||
|
||
func ExampleNewListener() { | ||
mux := http.NewServeMux() | ||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS) | ||
}) | ||
log.Fatal(http.Serve(autocert.NewListener("example.com"), mux)) | ||
} |