mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-30 22:34:15 -05:00
1201492222
* Updated lucas-clemente/quic-go for QUIC 39+ support * Update quic-go to latest
226 lines
5.6 KiB
Go
226 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/bifurcation/mint"
|
|
"golang.org/x/net/http2"
|
|
)
|
|
|
|
var (
|
|
port string
|
|
serverName string
|
|
certFile string
|
|
keyFile string
|
|
responseFile string
|
|
h2 bool
|
|
sendTickets bool
|
|
)
|
|
|
|
type responder []byte
|
|
|
|
func (rsp responder) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
w.Write(rsp)
|
|
}
|
|
|
|
// ParsePrivateKeyDER parses a PKCS #1, PKCS #8, or elliptic curve
|
|
// PEM-encoded private key.
|
|
// XXX: Inlined from github.com/cloudflare/cfssl because of build issues with that module
|
|
func ParsePrivateKeyPEM(keyPEM []byte) (key crypto.Signer, err error) {
|
|
keyDER, _ := pem.Decode(keyPEM)
|
|
if keyDER == nil {
|
|
return nil, err
|
|
}
|
|
|
|
generalKey, err := x509.ParsePKCS8PrivateKey(keyDER.Bytes)
|
|
if err != nil {
|
|
generalKey, err = x509.ParsePKCS1PrivateKey(keyDER.Bytes)
|
|
if err != nil {
|
|
generalKey, err = x509.ParseECPrivateKey(keyDER.Bytes)
|
|
if err != nil {
|
|
// We don't include the actual error into
|
|
// the final error. The reason might be
|
|
// we don't want to leak any info about
|
|
// the private key.
|
|
return nil, fmt.Errorf("No successful private key decoder")
|
|
}
|
|
}
|
|
}
|
|
|
|
switch generalKey.(type) {
|
|
case *rsa.PrivateKey:
|
|
return generalKey.(*rsa.PrivateKey), nil
|
|
case *ecdsa.PrivateKey:
|
|
return generalKey.(*ecdsa.PrivateKey), nil
|
|
}
|
|
|
|
// should never reach here
|
|
return nil, fmt.Errorf("Should be unreachable")
|
|
}
|
|
|
|
// ParseOneCertificateFromPEM attempts to parse one PEM encoded certificate object,
|
|
// either a raw x509 certificate or a PKCS #7 structure possibly containing
|
|
// multiple certificates, from the top of certsPEM, which itself may
|
|
// contain multiple PEM encoded certificate objects.
|
|
// XXX: Inlined from github.com/cloudflare/cfssl because of build issues with that module
|
|
func ParseOneCertificateFromPEM(certsPEM []byte) ([]*x509.Certificate, []byte, error) {
|
|
block, rest := pem.Decode(certsPEM)
|
|
if block == nil {
|
|
return nil, rest, nil
|
|
}
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
var certs = []*x509.Certificate{cert}
|
|
return certs, rest, err
|
|
}
|
|
|
|
// ParseCertificatesPEM parses a sequence of PEM-encoded certificate and returns them,
|
|
// can handle PEM encoded PKCS #7 structures.
|
|
// XXX: Inlined from github.com/cloudflare/cfssl because of build issues with that module
|
|
func ParseCertificatesPEM(certsPEM []byte) ([]*x509.Certificate, error) {
|
|
var certs []*x509.Certificate
|
|
var err error
|
|
certsPEM = bytes.TrimSpace(certsPEM)
|
|
for len(certsPEM) > 0 {
|
|
var cert []*x509.Certificate
|
|
cert, certsPEM, err = ParseOneCertificateFromPEM(certsPEM)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if cert == nil {
|
|
break
|
|
}
|
|
|
|
certs = append(certs, cert...)
|
|
}
|
|
if len(certsPEM) > 0 {
|
|
return nil, fmt.Errorf("Trailing PEM data")
|
|
}
|
|
return certs, nil
|
|
}
|
|
|
|
func main() {
|
|
flag.StringVar(&port, "port", "4430", "port")
|
|
flag.StringVar(&serverName, "host", "example.com", "hostname")
|
|
flag.StringVar(&certFile, "cert", "", "certificate chain in PEM or DER")
|
|
flag.StringVar(&keyFile, "key", "", "private key in PEM format")
|
|
flag.StringVar(&responseFile, "response", "", "file to serve")
|
|
flag.BoolVar(&h2, "h2", false, "whether to use HTTP/2 (exclusively)")
|
|
flag.BoolVar(&sendTickets, "tickets", true, "whether to send session tickets")
|
|
flag.Parse()
|
|
|
|
var certChain []*x509.Certificate
|
|
var priv crypto.Signer
|
|
var response []byte
|
|
var err error
|
|
|
|
// Load the key and certificate chain
|
|
if certFile != "" {
|
|
certs, err := ioutil.ReadFile(certFile)
|
|
if err != nil {
|
|
log.Fatalf("Error: %v", err)
|
|
} else {
|
|
certChain, err = ParseCertificatesPEM(certs)
|
|
if err != nil {
|
|
certChain, err = x509.ParseCertificates(certs)
|
|
if err != nil {
|
|
log.Fatalf("Error parsing certificates: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if keyFile != "" {
|
|
keyPEM, err := ioutil.ReadFile(keyFile)
|
|
if err != nil {
|
|
log.Fatalf("Error: %v", err)
|
|
} else {
|
|
priv, err = ParsePrivateKeyPEM(keyPEM)
|
|
if priv == nil || err != nil {
|
|
log.Fatalf("Error parsing private key: %v", err)
|
|
}
|
|
}
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("Error: %v", err)
|
|
}
|
|
|
|
// Load response file
|
|
if responseFile != "" {
|
|
log.Printf("Loading response file: %v", responseFile)
|
|
response, err = ioutil.ReadFile(responseFile)
|
|
if err != nil {
|
|
log.Fatalf("Error: %v", err)
|
|
}
|
|
} else {
|
|
response = []byte("Welcome to the TLS 1.3 zone!")
|
|
}
|
|
handler := responder(response)
|
|
|
|
config := mint.Config{
|
|
SendSessionTickets: true,
|
|
ServerName: serverName,
|
|
NextProtos: []string{"http/1.1"},
|
|
}
|
|
|
|
if h2 {
|
|
config.NextProtos = []string{"h2"}
|
|
}
|
|
|
|
config.SendSessionTickets = sendTickets
|
|
|
|
if certChain != nil && priv != nil {
|
|
log.Printf("Loading cert: %v key: %v", certFile, keyFile)
|
|
config.Certificates = []*mint.Certificate{
|
|
{
|
|
Chain: certChain,
|
|
PrivateKey: priv,
|
|
},
|
|
}
|
|
}
|
|
config.Init(false)
|
|
|
|
service := "0.0.0.0:" + port
|
|
srv := &http.Server{Handler: handler}
|
|
|
|
log.Printf("Listening on port %v", port)
|
|
// Need the inner loop here because the h1 server errors on a dropped connection
|
|
// Need the outer loop here because the h2 server is per-connection
|
|
for {
|
|
listener, err := mint.Listen("tcp", service, &config)
|
|
if err != nil {
|
|
log.Printf("Listen Error: %v", err)
|
|
continue
|
|
}
|
|
|
|
if !h2 {
|
|
alert := srv.Serve(listener)
|
|
if alert != mint.AlertNoAlert {
|
|
log.Printf("Serve Error: %v", err)
|
|
}
|
|
} else {
|
|
srv2 := new(http2.Server)
|
|
opts := &http2.ServeConnOpts{
|
|
Handler: handler,
|
|
BaseConfig: srv,
|
|
}
|
|
|
|
for {
|
|
conn, err := listener.Accept()
|
|
if err != nil {
|
|
log.Printf("Accept error: %v", err)
|
|
continue
|
|
}
|
|
go srv2.ServeConn(conn, opts)
|
|
}
|
|
}
|
|
}
|
|
}
|