Skip to content

Commit

Permalink
Merge pull request #14 from maelvls/readonly-for-guestbook
Browse files Browse the repository at this point in the history
Readonly access for the guestbook
  • Loading branch information
jetstack-bot authored Mar 19, 2024
2 parents fcdaef1 + 173e194 commit e3b4353
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 23 deletions.
10 changes: 8 additions & 2 deletions guestbook/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ module github.com/cert-manager/print-your-cert/guestbook

go 1.22

require modernc.org/sqlite v1.29.1
require (
golang.org/x/crypto v0.21.0
modernc.org/sqlite v1.29.1
)

require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/inconshreveable/go-vhost v1.0.0
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.41.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
Expand Down
12 changes: 10 additions & 2 deletions guestbook/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/inconshreveable/go-vhost v1.0.0 h1:IK4VZTlXL4l9vz2IZoiSFbYaaqUW7dXJAiPriUN5Ur8=
github.com/inconshreveable/go-vhost v1.0.0/go.mod h1:aA6DnFhALT3zH0y+A39we+zbrdMC2N0X/q21e6FI0LU=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
Expand All @@ -16,11 +18,17 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
Expand Down
116 changes: 97 additions & 19 deletions guestbook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"text/tabwriter"
"time"

"github.com/inconshreveable/go-vhost"
"golang.org/x/crypto/acme/autocert"
_ "modernc.org/sqlite"
)

Expand All @@ -27,6 +29,12 @@ var (
caCertPath = flag.String("ca-cert", "", "Path to CA certs to trust for client certs")
chainPath = flag.String("tls-chain", "", "Path to TLS cert chain")
privateKeyPath = flag.String("tls-key", "", "Path to TLS private key")
mainDomain = flag.String("domain", "guestbook.print-your-cert.cert-manager.io", "Domain used to access the guestbook. Used for SNI routing.")

readOnlyDomain = flag.String("readonly-domain", "readonly-guestbook.print-your-cert.cert-manager.io", "Domain used to access the guestbook in read-only mode")
readOnlyListenInsecure = flag.String("readonly-listen-insecure", "0.0.0.0:8080", "Address and port to listen on. Must be 80 if -prod is set.")
readOnlyProd = flag.Bool("prod", false, "If true, enables HTTPS for the readonly domain using Let's Encrypt.")
readOnlyAutocertDir = flag.String("autocert-dir", ".", "The directory used to cache the certificate and temporary files to work with Let's Encrypt.")

dbPath = flag.String("db-path", "guestbook.sqlite", "Path to sqlite database")
initDB = flag.Bool("init-db", false, "If set, initialise a fresh database at db-path")
Expand Down Expand Up @@ -224,8 +232,15 @@ func run(ctx context.Context) error {
return fmt.Errorf("missing required path to CA cert")
}

if *readOnlyProd && !strings.HasSuffix(*readOnlyListenInsecure, ":80") {
return fmt.Errorf("with -prod, use -readonly-listen-insecure=:80 so that Let's Encrypt can verify the domain")
}

logger := LoggerFromContext(ctx)

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT)

db, err := sql.Open("sqlite", *dbPath)
if err != nil {
return err
Expand All @@ -241,50 +256,113 @@ func run(ctx context.Context) error {
return err
}

serveMux := http.NewServeMux()
listener, err := net.Listen("tcp", *listen)
if err != nil {
return fmt.Errorf("failed to create TCP listener: %s", err)
}
tlsMux, err := vhost.NewTLSMuxer(listener, time.Second*5)
if err != nil {
return fmt.Errorf("failed to create vhost muxer: %s", err)
}

serveMux := http.NewServeMux()
serveMux.Handle("GET /", certExtractMiddleware(indexPage(db)))
serveMux.Handle("POST /write", certExtractMiddleware(writePage(db)))

server := &http.Server{
Handler: serveMux,
BaseContext: func(_ net.Listener) context.Context { return ctx },
ErrorLog: slog.NewLogLogger(logger.With("handler", "http.Server").Handler(), slog.LevelError),
BaseContext: func(l net.Listener) context.Context { return ctx },
ErrorLog: slog.NewLogLogger(logger.With("handler", "http.Server", "server", "main-server").Handler(), slog.LevelError),
}

listener, err := net.Listen("tcp", *listen)
serverListener, err := tlsMux.Listen(*mainDomain)
if err != nil {
return fmt.Errorf("failed to create TCP listener: %s", err)
return fmt.Errorf("failed to create vhost listener: %s", err)
}
go func() {
logger.Info("main-server listening", "address", *listen, "sni", *mainDomain)
err := server.Serve(tls.NewListener(serverListener, &tls.Config{
Certificates: []tls.Certificate{cert},
ClientCAs: certPool,
ClientAuth: tls.RequireAndVerifyClientCert,
}))
if err != nil && err != http.ErrServerClosed {
logger.Info("failed to listen", "error", err)
}
}()

tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientCAs: certPool,
ClientAuth: tls.RequireAndVerifyClientCert,
readOnlyMux := http.NewServeMux()
readOnlyMux.Handle("GET /", indexPage(db))
readOnlySrv := &http.Server{
Handler: readOnlyMux,
BaseContext: func(_ net.Listener) context.Context { return ctx },
ErrorLog: slog.NewLogLogger(logger.With("handler", "http.Server", "server", "readonly-server-https").Handler(), slog.LevelError),
}
mgr := &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: func(ctx context.Context, host string) error {
allowedHost := *readOnlyDomain
if host == allowedHost {
return nil
}
return fmt.Errorf("acme/autocert: only %s host is allowed", allowedHost)
},
Cache: autocert.DirCache(*readOnlyAutocertDir),
}
readOnlyListener, err := tlsMux.Listen(*readOnlyDomain)
if err != nil {
return fmt.Errorf("failed to create vhost listener: %s", err)
}

listener = tls.NewListener(listener, tlsConfig)

logger.Info("listening", "address", *listen)

sigs := make(chan os.Signal, 1)

signal.Notify(sigs, syscall.SIGINT)
go func() {
if !*readOnlyProd {
logger.Info("readonly-server not enabled, use -prod to turn it on")
return
}
logger.Info("readonly-server-https listening", "address", *listen, "sni", *readOnlyDomain)
err := readOnlySrv.Serve(tls.NewListener(readOnlyListener, &tls.Config{
GetCertificate: mgr.GetCertificate,
}))
if err != nil && err != http.ErrServerClosed {
logger.Info("failed to listen", "error", err)
}
}()

readOnlySrvHTTP := &http.Server{
Handler: readOnlyMux,
BaseContext: func(_ net.Listener) context.Context { return ctx },
ErrorLog: slog.NewLogLogger(logger.With("handler", "http.Server", "server", "readonly-server-http").Handler(), slog.LevelError),
Addr: *readOnlyListenInsecure,
}
if mgr != nil {
// Allows autocert handle Let's Encrypt HTTP-01 callbacks.
readOnlySrv.Handler = mgr.HTTPHandler(readOnlySrvHTTP.Handler)
}
go func() {
err := server.Serve(listener)
logger.Info("readonly-server-http listening", "address", *readOnlyListenInsecure)
err := readOnlySrv.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
logger.Info("failed to listen", "error", err)
}
}()

go tlsMux.HandleErrors()

<-sigs
logger.Info("shutting down")

err = server.Shutdown(context.Background())
if err != nil {
return err
}
err = readOnlySrv.Shutdown(context.Background())
if err != nil {
return err
}
if readOnlySrvHTTP != nil {
err = readOnlySrvHTTP.Shutdown(context.Background())
if err != nil {
return err
}
}

return nil
}
Expand Down

0 comments on commit e3b4353

Please sign in to comment.