Skip to content

Instantly share code, notes, and snippets.

@jaksi
Created December 23, 2016 17:32
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jaksi/8f1cd0af858aa08aa51a280610722742 to your computer and use it in GitHub Desktop.
Save jaksi/8f1cd0af858aa08aa51a280610722742 to your computer and use it in GitHub Desktop.
Go web server
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