2023-01-20 13:17:40 +00:00
|
|
|
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
2021-12-15 20:48:55 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
2023-01-20 13:17:40 +00:00
|
|
|
"sync/atomic"
|
2021-12-15 20:48:55 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
|
|
|
|
|
|
"go.pinniped.dev/internal/certauthority"
|
2023-09-08 16:22:10 +00:00
|
|
|
"go.pinniped.dev/internal/httputil/requestutil"
|
|
|
|
"go.pinniped.dev/internal/plog"
|
2021-12-15 20:48:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// contextKey type is unexported to prevent collisions.
|
|
|
|
type contextKey int
|
|
|
|
|
|
|
|
const bootstrapKey contextKey = iota
|
|
|
|
|
|
|
|
func withBootstrapConnCtx(ctx context.Context, _ net.Conn) context.Context {
|
2023-01-20 13:17:40 +00:00
|
|
|
isBootstrap := atomic.Bool{} // safe for concurrent access
|
|
|
|
return context.WithValue(ctx, bootstrapKey, &isBootstrap)
|
2021-12-15 20:48:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func setIsBootstrapConn(ctx context.Context) {
|
|
|
|
isBootstrap, _ := ctx.Value(bootstrapKey).(*atomic.Bool)
|
|
|
|
if isBootstrap == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
isBootstrap.Store(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func withBootstrapPaths(handler http.Handler, paths ...string) http.Handler {
|
|
|
|
bootstrapPaths := sets.NewString(paths...)
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
isBootstrap, _ := req.Context().Value(bootstrapKey).(*atomic.Bool)
|
|
|
|
|
|
|
|
if isBootstrap != nil && isBootstrap.Load() && !bootstrapPaths.Has(req.URL.Path) {
|
2023-09-08 16:22:10 +00:00
|
|
|
// When a user-provided cert was not found for a request path which requires it,
|
|
|
|
// then emit a log statement visible at the default log level.
|
|
|
|
plog.Warning("error finding user-provided TLS cert to use for for incoming request",
|
|
|
|
"proto", req.Proto,
|
|
|
|
"method", req.Method,
|
|
|
|
"host", req.Host,
|
|
|
|
"requestSNIServerName", requestutil.SNIServerName(req),
|
|
|
|
"path", req.URL.Path,
|
|
|
|
"remoteAddr", req.RemoteAddr,
|
|
|
|
)
|
|
|
|
|
2021-12-15 20:48:55 +00:00
|
|
|
http.Error(w, "pinniped supervisor has invalid TLS serving certificate configuration", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
handler.ServeHTTP(w, req)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBootstrapCert() (*tls.Certificate, error) {
|
|
|
|
const forever = 10 * 365 * 24 * time.Hour
|
|
|
|
|
|
|
|
bootstrapCA, err := certauthority.New("pinniped-supervisor-bootstrap-ca", forever)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to create bootstrap CA: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bootstrapCert, err := bootstrapCA.IssueServerCert([]string{"pinniped-supervisor-bootstrap-cert"}, nil, forever)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to create bootstrap cert: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return bootstrapCert, nil // this is just enough to complete a TLS handshake, trust distribution does not matter
|
|
|
|
}
|