Skip to content

Commit

Permalink
Add command-line flag --http-ping-only
Browse files Browse the repository at this point in the history
New optional command-line flag --http-ping-only allows
to enforce that the http server (which doesn't support mTLS)
allows only to access the /ping entrypoint. This would limit
the risk of the timestamp server being accessed without
mTLS in case of the strict mTLS requirement while still
allowing the mTLS-exempt /ping entrypoint to be called
for example by the heartbeat checkers (load balancers etc.).

Fixes #420.

Signed-off-by: Dmitry Savintsev <[email protected]>
  • Loading branch information
dmitris committed Sep 18, 2023
1 parent d388e35 commit 740f769
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 14 deletions.
9 changes: 5 additions & 4 deletions cmd/timestamp-server/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import (
)

var (
cfgFile string
logType string
enablePprof bool
cfgFile string
logType string
enablePprof bool
httpPingOnly bool
)

// rootCmd represents the base command when called without any subcommands
Expand Down Expand Up @@ -56,7 +57,7 @@ func init() {
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.timestamp-server.yaml)")
rootCmd.PersistentFlags().StringVar(&logType, "log-type", "dev", "logger type to use (dev/prod)")
rootCmd.PersistentFlags().BoolVar(&enablePprof, "enable-pprof", false, "enable pprof for profiling on port 6060")

rootCmd.PersistentFlags().BoolVar(&httpPingOnly, "http-ping-only", false, "serve only /ping in the http server")
rootCmd.PersistentFlags().String("timestamp-signer", "memory", "Timestamping authority signer. Valid options include: [kms, tink, memory, file]. Memory and file-based signers should only be used for testing")
// KMS flags
rootCmd.PersistentFlags().String("kms-key-resource", "", "KMS key for signing timestamp responses. Valid options include: [gcpkms://resource, azurekms://resource, hashivault://resource, awskms://resource]")
Expand Down
4 changes: 2 additions & 2 deletions cmd/timestamp-server/app/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ var serveCmd = &cobra.Command{
host := viper.GetString("host")
port := int(viper.GetUint("port"))
scheme := viper.GetStringSlice("scheme")

server := server.NewRestAPIServer(host, port, scheme, readTimeout, writeTimeout)
httpPingOnly := viper.GetBool("http-ping-only")
server := server.NewRestAPIServer(host, port, scheme, httpPingOnly, readTimeout, writeTimeout)
defer func() {
if err := server.Shutdown(); err != nil {
log.Logger.Error(err)
Expand Down
11 changes: 7 additions & 4 deletions pkg/generated/restapi/configure_timestamp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
pkgapi "github.com/sigstore/timestamp-authority/pkg/api"
"github.com/sigstore/timestamp-authority/pkg/generated/restapi/operations"
"github.com/sigstore/timestamp-authority/pkg/generated/restapi/operations/timestamp"
"github.com/sigstore/timestamp-authority/pkg/internal/cmdparams"
"github.com/sigstore/timestamp-authority/pkg/log"
)

Expand Down Expand Up @@ -102,15 +103,15 @@ func (l *logAdapter) Print(v ...interface{}) {
log.Logger.Info(v...)
}

// httpPingOnly custom middleware prohibits all entrypoing except
// httpPingOnly custom middleware prohibits all entrypoints except
// "/ping" on the http (non-HTTPS) server.
func httpPingOnly(endpoint string) func(http.Handler) http.Handler {
f := func(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if r.URL.Scheme != "https" && !strings.EqualFold(r.URL.Path, endpoint) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("http server supports only the /ping entrypoint"))
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("http server supports only the /ping entrypoint")) //nolint:errcheck
return
}
h.ServeHTTP(w, r)
Expand All @@ -128,7 +129,9 @@ func setupGlobalMiddleware(handler http.Handler) http.Handler {
returnHandler := middleware.Logger(handler)
returnHandler = middleware.Recoverer(returnHandler)
returnHandler = middleware.Heartbeat("/ping")(returnHandler)
returnHandler = httpPingOnly("/ping")(returnHandler)
if cmdparams.IsHTTPPingOnly {
returnHandler = httpPingOnly("/ping")(returnHandler)
}

handleCORS := cors.Default().Handler
returnHandler = handleCORS(returnHandler)
Expand Down
23 changes: 23 additions & 0 deletions pkg/internal/cmdparams/cmdparams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright 2022 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmdparams

// IsHTTPPingOnly is set off the command-line flag to enforce limiting
// the non-mTLS http server to only serving the /ping entrypoint.
// It should be set only once when processing command-line flags
// and then used only in pkg/generated/restapi/configure_timestamp_server.go
// and as read-only.
var IsHTTPPingOnly bool
19 changes: 19 additions & 0 deletions pkg/internal/cmdparams/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Copyright 2022 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cmdparams contains variables to propagate from command-line
// flags to their handling in
// pkg/generated/restapi/configure_timestamp_server.go.
package cmdparams
1 change: 0 additions & 1 deletion pkg/ntpmonitor/config.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//
// Copyright 2022 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
9 changes: 7 additions & 2 deletions pkg/server/restapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ import (
"github.com/sigstore/timestamp-authority/pkg/api"
"github.com/sigstore/timestamp-authority/pkg/generated/restapi"
"github.com/sigstore/timestamp-authority/pkg/generated/restapi/operations"
"github.com/sigstore/timestamp-authority/pkg/internal/cmdparams"
)

// NewRestAPIServer creates a server for serving the rest API TSA service
func NewRestAPIServer(host string, port int, scheme []string, readTimeout, writeTimeout time.Duration) *restapi.Server {
func NewRestAPIServer(host string,
port int,
scheme []string,
httpReadOnly bool,
readTimeout, writeTimeout time.Duration) *restapi.Server {
doc, _ := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
server := restapi.NewServer(operations.NewTimestampServerAPI(doc))

Expand All @@ -33,7 +38,7 @@ func NewRestAPIServer(host string, port int, scheme []string, readTimeout, write
server.EnabledListeners = scheme
server.ReadTimeout = readTimeout
server.WriteTimeout = writeTimeout

cmdparams.IsHTTPPingOnly = httpReadOnly
api.ConfigureAPI()
server.ConfigureAPI()

Expand Down
2 changes: 1 addition & 1 deletion pkg/tests/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
func createServer(t *testing.T) string {
viper.Set("timestamp-signer", "memory")
// unused port
apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, 10*time.Second, 10*time.Second)
apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second)
server := httptest.NewServer(apiServer.GetHandler())
t.Cleanup(server.Close)

Expand Down

0 comments on commit 740f769

Please sign in to comment.