Created
December 23, 2016 17:32
-
-
Save jaksi/8f1cd0af858aa08aa51a280610722742 to your computer and use it in GitHub Desktop.
Go web server
This file contains hidden or 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
package main | |
import ( | |
"bytes" | |
"crypto/tls" | |
"crypto/x509" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"time" | |
"golang.org/x/crypto/acme/autocert" | |
"golang.org/x/crypto/ocsp" | |
) | |
func main() { | |
// HTTP server, redirects to HTTPS | |
srvHTTP := &http.Server{ | |
ReadTimeout: 5 * time.Second, | |
WriteTimeout: 5 * time.Second, | |
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | |
w.Header().Set("Connection", "close") | |
url := "https://" + req.Host + req.URL.String() | |
http.Redirect(w, req, url, http.StatusMovedPermanently) | |
}), | |
} | |
// Automatically fetch certificates from Let's Encrypt | |
m := autocert.Manager{ | |
Prompt: autocert.AcceptTOS, | |
HostPolicy: autocert.HostWhitelist("jaksi.io", "www.jaksi.io"), | |
Cache: autocert.DirCache("/tmp/autocert"), | |
ForceRSA: true, | |
} | |
getCertificate := func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { | |
// Set a default server name if SNI was not sent | |
if hello.ServerName == "" { | |
hello.ServerName = "jaksi.io" | |
} | |
// Get a certificate from Let's Encrypt (or the cache) | |
cert, err := m.GetCertificate(hello) | |
if err != nil { | |
return nil, err | |
} | |
// Fetch and staple OCSP | |
// TODO: should cache OCSP responses | |
x509Cert := cert.Leaf | |
ocspServer := x509Cert.OCSPServer[0] | |
// TODO: what if there are no OCSP servers | |
x509Issuer, err := x509.ParseCertificate(cert.Certificate[1]) | |
// TODO: what if there's only one cert in the chain | |
if err != nil { | |
log.Println(err) | |
return cert, nil | |
} | |
ocspRequest, err := ocsp.CreateRequest(x509Cert, x509Issuer, nil) | |
if err != nil { | |
log.Println(err) | |
return cert, nil | |
} | |
ocspRequestReader := bytes.NewReader(ocspRequest) | |
httpResponse, err := http.Post(ocspServer, "application/ocsp-request", ocspRequestReader) | |
if err != nil { | |
log.Println(err) | |
return cert, nil | |
} | |
defer httpResponse.Body.Close() | |
ocspResponseBytes, err := ioutil.ReadAll(httpResponse.Body) | |
if err != nil { | |
log.Println(err) | |
return cert, nil | |
} | |
//ocspResponse, err := ocsp.ParseResponse(ocspResponseBytes, x509Issuer) | |
//if err != nil { | |
// log.Println(err) | |
// return cert, nil | |
//} | |
// TODO: should maybe fail if the status was invalid or revoked | |
cert.OCSPStaple = ocspResponseBytes | |
return cert, nil | |
} | |
// HTTPS Server, serves a simple string | |
srvHTTPS := &http.Server{ | |
WriteTimeout: 10 * time.Second, | |
TLSConfig: &tls.Config{ | |
PreferServerCipherSuites: true, | |
CurvePreferences: []tls.CurveID{ | |
tls.CurveP256, | |
}, | |
// Optional, for requesting certificates on the fly from Let's Encrypt | |
// and stpling OCSP | |
GetCertificate: getCertificate, | |
}, | |
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | |
w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload") | |
fmt.Fprintf(w, "Hello World!") | |
}), | |
} | |
go func() { log.Fatal(srvHTTP.ListenAndServe()) }() | |
// For the automatic cert request method using Let's Encrypt | |
log.Fatal(srvHTTPS.ListenAndServeTLS("", "")) | |
// Static certificate | |
//log.Fatal(srvHTTPS.ListenAndServeTLS("cert.pem", "key.pem")) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment