Skip to content

Commit

Permalink
Add CORS support to HTTP endpoint (#670)
Browse files Browse the repository at this point in the history
* add cors support to HTTP endpoint

Signed-off-by: Bob Callaway <bcallaway@google.com>
  • Loading branch information
bobcallaway committed Jul 1, 2022
1 parent 21b6f72 commit 5a9480e
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 1 deletion.
13 changes: 12 additions & 1 deletion cmd/app/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/cors"
gw "github.com/sigstore/fulcio/pkg/generated/protobuf"
legacy_gw "github.com/sigstore/fulcio/pkg/generated/protobuf/legacy"
"github.com/sigstore/fulcio/pkg/log"
Expand Down Expand Up @@ -67,6 +68,10 @@ func createHTTPServer(ctx context.Context, serverEndpoint string, grpcServer, le
handler = promhttp.InstrumentHandlerDuration(server.MetricLatency, handler)
handler = promhttp.InstrumentHandlerCounter(server.RequestsCount, handler)

// enable CORS
// cors.Default() configures to accept requests for all domains
handler = cors.Default().Handler(handler)

api := http.Server{
Addr: serverEndpoint,
Handler: handler,
Expand Down Expand Up @@ -102,6 +107,13 @@ func setResponseCodeModifier(ctx context.Context, w http.ResponseWriter, _ proto
w.Header().Set("SCT", vals[0])
}

// strip all GRPC response headers
for headerKey := range w.Header() {
if strings.HasPrefix(headerKey, "Grpc-") {
delete(w.Header(), headerKey)
}
}

// set http status code
if vals := md.HeaderMD.Get(server.HTTPResponseCodeMetadataKey); len(vals) > 0 {
code, err := strconv.Atoi(vals[0])
Expand All @@ -110,7 +122,6 @@ func setResponseCodeModifier(ctx context.Context, w http.ResponseWriter, _ proto
}
// delete the headers to not expose any grpc-metadata in http response
delete(md.HeaderMD, server.HTTPResponseCodeMetadataKey)
delete(w.Header(), "Grpc-Metadata-X-Http-Code")
w.WriteHeader(code)
}

Expand Down
117 changes: 117 additions & 0 deletions cmd/app/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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 app

import (
"context"
"crypto"
"errors"
"fmt"
"net"
"net/http"
"net/url"
"strings"
"testing"
"time"

"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/spf13/viper"
)

func setupHTTPServer(t *testing.T) (httpServer, string) {
t.Helper()
httpListen, _ := net.Listen("tcp", ":0")

viper.Set("grpc-host", "")
viper.Set("grpc-port", 0)
grpcServer, err := createGRPCServer(nil, nil, &TrivialCertificateAuthority{})
if err != nil {
t.Error(err)
}
grpcServer.startTCPListener()
// loop until server starts listening in separate goroutine
start := time.Now()
for {
if grpcServer.grpcServerEndpoint != ":0" {
break
}
if time.Since(start) > 3*time.Second {
t.Errorf("timeout waiting for grpcServer to start")
}
}
// set the correct listener value before creating the wrapping http server
grpcServer.grpcServerEndpoint = strings.Replace(grpcServer.grpcServerEndpoint, "::", "localhost", 1)

httpHost := fmt.Sprintf("localhost:%d", httpListen.Addr().(*net.TCPAddr).Port)
httpServer := createHTTPServer(context.Background(), httpHost, grpcServer, nil)
go func() {
_ = httpServer.Serve(httpListen)
}()

return httpServer, fmt.Sprintf("http://%s", httpHost)

}

func TestHTTPCORSSupport(t *testing.T) {
httpServer, host := setupHTTPServer(t)
defer httpServer.Close()

url, _ := url.Parse(host + "/api/v2/trustBundle")
req := http.Request{
Method: "GET",
URL: url,
Header: map[string][]string{"Origin": {"http://example.com"}},
}

resp, err := http.DefaultClient.Do(&req)
if err != nil || resp.StatusCode != http.StatusOK {
t.Error(err)
}
defer resp.Body.Close()

if resp.Header.Get("Access-Control-Allow-Origin") == "" {
t.Errorf("missing CORS header")
}
}

func TestHTTPDoesntLeakGRPCHeaders(t *testing.T) {
httpServer, host := setupHTTPServer(t)
defer httpServer.Close()

resp, err := http.Get(host + "/api/v2/trustBundle")
if err != nil || resp.StatusCode != http.StatusOK {
t.Error(err)
}
defer resp.Body.Close()

for headerKey := range resp.Header {
if strings.HasPrefix(headerKey, "Grpc-") {
t.Errorf("found leaked Grpc response header %s", headerKey)
}
}
}

// Trivial CA service that returns junk
type TrivialCertificateAuthority struct {
}

func (tca *TrivialCertificateAuthority) CreateCertificate(context.Context, identity.Principal, crypto.PublicKey) (*ca.CodeSigningCertificate, error) {
return nil, errors.New("CreateCertificate always fails for testing")
}
func (tca *TrivialCertificateAuthority) Root(ctx context.Context) ([]byte, error) {
return []byte("not a certificate"), nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
github.com/prometheus/client_golang v1.12.2
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.35.0
github.com/rs/cors v1.8.2
github.com/sigstore/sigstore v1.3.1-0.20220630102118-77b1712d5cfd
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand Down

0 comments on commit 5a9480e

Please sign in to comment.