Skip to content

Commit

Permalink
new http-ping-only flag for 'timestamp-server serve' (#474)
Browse files Browse the repository at this point in the history
* add httpPingOnly middleware

Add a small custom middlerware httpPingOnly to allow only
/ping entrypoint for the http (non-https) server.

Related to issue #420.

Signed-off-by: Dmitry S <dsavints@gmail.com>

* Add command-line flag --http-ping-only

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 <dsavints@gmail.com>

* use 404 for anything other than /ping

Signed-off-by: Dmitry Savintsev <dsavints@gmail.com>

* minor changes based on PR feedback

* remove parameter for httpPingOnly
* remove debug message on httpPingOnly
* use const 'pingPath' consistently

Signed-off-by: Dmitry Savintsev <dsavints@gmail.com>

* 2023 as copyright year for new files

Signed-off-by: Dmitry Savintsev <dsavints@gmail.com>

---------

Signed-off-by: Dmitry S <dsavints@gmail.com>
Signed-off-by: Dmitry Savintsev <dsavints@gmail.com>
  • Loading branch information
dmitris authored Sep 29, 2023
1 parent 20cf4f8 commit 5db959c
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 11 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
3 changes: 1 addition & 2 deletions cmd/timestamp-server/app/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ 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)
server := server.NewRestAPIServer(host, port, scheme, httpPingOnly, readTimeout, writeTimeout)
defer func() {
if err := server.Shutdown(); err != nil {
log.Logger.Error(err)
Expand Down
27 changes: 26 additions & 1 deletion pkg/generated/restapi/configure_timestamp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/tls"
"net/http"
"strconv"
"strings"
"time"

"github.com/go-chi/chi/middleware"
Expand All @@ -33,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 @@ -101,14 +103,37 @@ func (l *logAdapter) Print(v ...interface{}) {
log.Logger.Info(v...)
}

const pingPath = "/ping"

// httpPingOnly custom middleware prohibits all entrypoints except
// "/ping" on the http (non-HTTPS) server.
func httpPingOnly() 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, pingPath) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("http server supports only the " + pingPath + " entrypoint")) //nolint:errcheck
return
}
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
return f
}

// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics.
func setupGlobalMiddleware(handler http.Handler) http.Handler {
middleware.DefaultLogger = middleware.RequestLogger(
&middleware.DefaultLogFormatter{Logger: &logAdapter{}})
returnHandler := middleware.Logger(handler)
returnHandler = middleware.Recoverer(returnHandler)
returnHandler = middleware.Heartbeat("/ping")(returnHandler)
returnHandler = middleware.Heartbeat(pingPath)(returnHandler)
if cmdparams.IsHTTPPingOnly {
returnHandler = httpPingOnly()(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 2023 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 2023 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 5db959c

Please sign in to comment.