Skip to content

Commit

Permalink
fix: return CORS headers in ingress responses (#563)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
alecthomas and github-actions[bot] authored Nov 8, 2023
1 parent e353e38 commit b9cd12e
Show file tree
Hide file tree
Showing 13 changed files with 51 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Dockerfile.controller
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ EXPOSE 8892

ENV FTL_CONTROLLER_BIND="http://0.0.0.0:8892"
ENV FTL_CONTROLLER_ADVERTISE="http://127.0.0.1:8892"
ENV FTL_CONTROLLER_ALLOW_ORIGIN="*"
ENV FTL_CONTROLLER_CONSOLE_URL="*"
ENV FTL_CONTROLLER_DSN="postgres://host.docker.internal/ftl?sslmode=disable&user=postgres&password=secret"

CMD ["/root/ftl-controller"]
12 changes: 12 additions & 0 deletions backend/common/cors/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cors

import (
"net/http"

"github.com/rs/cors"
)

func Middleware(allowOrigins []string, next http.Handler) http.Handler {
c := cors.New(cors.Options{AllowedOrigins: allowOrigins})
return c.Handler(next)
}
18 changes: 1 addition & 17 deletions backend/common/rpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"connectrpc.com/grpcreflect"
"github.com/alecthomas/concurrency"
"github.com/alecthomas/errors"
"github.com/rs/cors"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"

Expand Down Expand Up @@ -82,22 +81,7 @@ func NewServer(ctx context.Context, listen *url.URL, options ...Option) (*Server
reflector := grpcreflect.NewStaticReflector(opts.reflectionPaths...)
opts.mux.Handle(grpcreflect.NewHandlerV1(reflector))
opts.mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector))

// TODO: Is this a good idea? Who knows!
crs := cors.New(cors.Options{
AllowedOrigins: []string{listen.String(), "http://ftl.localtest.me"},
AllowedMethods: []string{
http.MethodHead,
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
},
AllowedHeaders: []string{"*"},
AllowCredentials: false,
})
root := crs.Handler(ContextValuesMiddleware(ctx, opts.mux))
root := ContextValuesMiddleware(ctx, opts.mux)

http1Server := &http.Server{
Handler: h2c.NewHandler(root, &http2.Server{}),
Expand Down
13 changes: 10 additions & 3 deletions backend/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/TBD54566975/ftl/backend/common/cors"
"github.com/TBD54566975/ftl/backend/common/log"
"github.com/TBD54566975/ftl/backend/common/model"
"github.com/TBD54566975/ftl/backend/common/rpc"
Expand All @@ -48,7 +49,8 @@ import (
type Config struct {
Bind *url.URL `help:"Socket to bind to." default:"http://localhost:8892" env:"FTL_CONTROLLER_BIND"`
Advertise *url.URL `help:"Endpoint the Controller should advertise (must be unique across the cluster, defaults to --bind if omitted)." env:"FTL_CONTROLLER_ADVERTISE"`
AllowOrigin string `help:"Allow CORS requests from this origin." default:"*" env:"FTL_CONTROLLER_ALLOW_ORIGIN"`
ConsoleURL *url.URL `help:"The public URL of the console (for CORS)." env:"FTL_CONTROLLER_CONSOLE_URL"`
AllowOrigins []*url.URL `help:"Allow CORS requests to ingress endpoints from these origins." env:"FTL_CONTROLLER_ALLOW_ORIGIN"`
ContentTime time.Time `help:"Time to use for console resource timestamps." default:"${timestamp=1970-01-01T00:00:00Z}"`
Key model.ControllerKey `help:"Controller key (auto)." placeholder:"C<ULID>" default:"C00000000000000000000000000"`
DSN string `help:"DAL DSN." default:"postgres://localhost/ftl?sslmode=disable&user=postgres&password=secret" env:"FTL_CONTROLLER_DSN"`
Expand All @@ -73,7 +75,7 @@ func Start(ctx context.Context, config Config, runnerScaling scaling.RunnerScali
logger := log.FromContext(ctx)
logger.Infof("Starting FTL controller")

c, err := frontend.Server(ctx, config.ContentTime, config.AllowOrigin)
c, err := frontend.Server(ctx, config.ContentTime, config.ConsoleURL)
if err != nil {
return errors.WithStack(err)
}
Expand All @@ -96,11 +98,16 @@ func Start(ctx context.Context, config Config, runnerScaling scaling.RunnerScali

console := NewConsoleService(dal)

ingressHandler := http.StripPrefix("/ingress", svc)
if len(config.AllowOrigins) > 0 {
ingressHandler = cors.Middleware(slices.Map(config.AllowOrigins, func(u *url.URL) string { return u.String() }), ingressHandler)
}

return rpc.Serve(ctx, config.Bind,
rpc.GRPC(ftlv1connect.NewVerbServiceHandler, svc),
rpc.GRPC(ftlv1connect.NewControllerServiceHandler, svc),
rpc.GRPC(pbconsoleconnect.NewConsoleServiceHandler, console),
rpc.HTTP("/ingress/", http.StripPrefix("/ingress", svc)),
rpc.HTTP("/ingress/", ingressHandler),
rpc.HTTP("/", c),
)
}
Expand Down
16 changes: 9 additions & 7 deletions cmd/ftl/cmd_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ import (
)

type serveCmd struct {
Bind *url.URL `help:"Starting endpoint to bind to and advertise to. Each controller and runner will increment the port by 1" default:"http://localhost:8892"`
DBPort int `help:"Port to use for the database." default:"5433"`
Recreate bool `help:"Recreate the database even if it already exists." default:"false"`
Controllers int `short:"c" help:"Number of controllers to start." default:"1"`
Runners int `short:"r" help:"Number of runners to start." default:"0"`
Bind *url.URL `help:"Starting endpoint to bind to and advertise to. Each controller and runner will increment the port by 1" default:"http://localhost:8892"`
AllowOrigins []*url.URL `help:"Allow CORS requests to ingress endpoints from these origins." env:"FTL_CONTROLLER_ALLOW_ORIGIN"`
DBPort int `help:"Port to use for the database." default:"5433"`
Recreate bool `help:"Recreate the database even if it already exists." default:"false"`
Controllers int `short:"c" help:"Number of controllers to start." default:"1"`
Runners int `short:"r" help:"Number of runners to start." default:"0"`
}

const ftlContainerName = "ftl-db"
Expand Down Expand Up @@ -59,8 +60,9 @@ func (s *serveCmd) Run(ctx context.Context) error {
for i := 0; i < s.Controllers; i++ {
i := i
config := controller.Config{
Bind: controllerAddresses[i],
DSN: dsn,
Bind: controllerAddresses[i],
DSN: dsn,
AllowOrigins: s.AllowOrigins,
}
if err := kong.ApplyDefaults(&config); err != nil {
return errors.WithStack(err)
Expand Down
1 change: 0 additions & 1 deletion examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/swaggest/jsonschema-go v0.3.62 // indirect
github.com/swaggest/refl v1.3.0 // indirect
github.com/zalando/go-keyring v0.2.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions examples/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion examples/online-boutique/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/swaggest/jsonschema-go v0.3.62 // indirect
github.com/swaggest/refl v1.3.0 // indirect
github.com/zalando/go-keyring v0.2.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions examples/online-boutique/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/online-boutique/services/ad/ad.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ type Ad struct {
}

type AdResponse struct {
Ads []Ad
Name string
Ads []Ad
}

//ftl:verb
Expand Down
11 changes: 0 additions & 11 deletions frontend/cors.go

This file was deleted.

18 changes: 6 additions & 12 deletions frontend/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (

"github.com/alecthomas/errors"

"github.com/TBD54566975/ftl/backend/common/cors"
"github.com/TBD54566975/ftl/backend/common/exec"
"github.com/TBD54566975/ftl/backend/common/log"
)

var consoleURL, _ = url.Parse("http://localhost:5173")
var proxy = httputil.NewSingleHostReverseProxy(consoleURL)

func Server(ctx context.Context, timestamp time.Time, allowOrigin string) (http.Handler, error) {
func Server(ctx context.Context, timestamp time.Time, allowOrigin *url.URL) (http.Handler, error) {
logger := log.FromContext(ctx)
logger.Infof("Building console...")

Expand All @@ -33,16 +34,9 @@ func Server(ctx context.Context, timestamp time.Time, allowOrigin string) (http.
}
logger.Infof("Console started")

return http.HandlerFunc(handler(allowOrigin)), nil
}

func handler(allowOrigin string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
writeCORSHeaders(w, allowOrigin)
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
proxy.ServeHTTP(w, r)
if allowOrigin == nil {
return proxy, nil
}

return cors.Middleware([]string{allowOrigin.String()}, proxy), nil
}
14 changes: 10 additions & 4 deletions frontend/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,26 @@ import (
"io"
"io/fs"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"

"github.com/alecthomas/errors"

"github.com/TBD54566975/ftl/backend/common/cors"
)

//go:embed all:dist
var build embed.FS

func Server(ctx context.Context, timestamp time.Time, allowOrigin string) (http.Handler, error) {
func Server(ctx context.Context, timestamp time.Time, allowOrigin *url.URL) (http.Handler, error) {
dir, err := fs.Sub(build, "dist")
if err != nil {
return nil, errors.WithStack(err)
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
writeCORSHeaders(w, allowOrigin)
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var f fs.File
var err error
filePath := strings.TrimPrefix(r.URL.Path, "/")
Expand All @@ -43,5 +45,9 @@ func Server(ctx context.Context, timestamp time.Time, allowOrigin string) (http.
return
}
http.ServeContent(w, r, filePath, timestamp, f.(io.ReadSeeker))
}), nil
})
if allowOrigin != nil {
handler = cors.Middleware([]string{allowOrigin.String()}, handler)
}
return handler, nil
}

0 comments on commit b9cd12e

Please sign in to comment.