From 01dc1762d6be40d84829476ee154834d88490155 Mon Sep 17 00:00:00 2001 From: tamirms Date: Fri, 28 Jun 2024 06:16:20 +0100 Subject: [PATCH] sevices/friendbot: Allow friendbot to include cloudflare derived IP address in request logs (#5359) --- services/friendbot/main.go | 17 ++++++++++--- services/friendbot/router_test.go | 33 +++++++++++++++++++++++++ support/http/logging_middleware.go | 2 ++ support/http/logging_middleware_test.go | 9 ++++--- support/http/mux.go | 1 + 5 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 services/friendbot/router_test.go diff --git a/services/friendbot/main.go b/services/friendbot/main.go index 22a04b0bed..22e7d4c44d 100644 --- a/services/friendbot/main.go +++ b/services/friendbot/main.go @@ -8,6 +8,7 @@ import ( "github.com/go-chi/chi" "github.com/spf13/cobra" + "github.com/stellar/go/services/friendbot/internal" "github.com/stellar/go/support/app" "github.com/stellar/go/support/config" @@ -29,6 +30,7 @@ type Config struct { BaseFee int64 `toml:"base_fee" valid:"optional"` MinionBatchSize int `toml:"minion_batch_size" valid:"optional"` SubmitTxRetriesAllowed int `toml:"submit_tx_retries_allowed" valid:"optional"` + UseCloudflareIP bool `toml:"use_cloudflare_ip" valid:"optional"` } func main() { @@ -68,7 +70,7 @@ func run(cmd *cobra.Command, args []string) { log.Error(err) os.Exit(1) } - router := initRouter(fb) + router := initRouter(cfg, fb) registerProblems() addr := fmt.Sprintf("0.0.0.0:%d", cfg.Port) @@ -84,8 +86,8 @@ func run(cmd *cobra.Command, args []string) { }) } -func initRouter(fb *internal.Bot) *chi.Mux { - mux := http.NewAPIMux(log.DefaultLogger) +func initRouter(cfg Config, fb *internal.Bot) *chi.Mux { + mux := newMux(cfg) handler := &internal.FriendbotHandler{Friendbot: fb} mux.Get("/", handler.Handle) @@ -97,6 +99,15 @@ func initRouter(fb *internal.Bot) *chi.Mux { return mux } +func newMux(cfg Config) *chi.Mux { + mux := chi.NewRouter() + // first apply XFFMiddleware so we can have the real ip in the subsequent + // middlewares + mux.Use(http.XFFMiddleware(http.XFFMiddlewareConfig{BehindCloudflare: cfg.UseCloudflareIP})) + mux.Use(http.NewAPIMux(log.DefaultLogger).Middlewares()...) + return mux +} + func registerProblems() { problem.RegisterError(sql.ErrNoRows, problem.NotFound) diff --git a/services/friendbot/router_test.go b/services/friendbot/router_test.go new file mode 100644 index 0000000000..292a3253ca --- /dev/null +++ b/services/friendbot/router_test.go @@ -0,0 +1,33 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/stellar/go/support/log" +) + +func TestIPLogging(t *testing.T) { + done := log.DefaultLogger.StartTest(log.InfoLevel) + + mux := newMux(Config{UseCloudflareIP: true}) + mux.Get("/", func(w http.ResponseWriter, request *http.Request) { + w.WriteHeader(http.StatusOK) + }) + recorder := httptest.NewRecorder() + request := httptest.NewRequest("GET", "/", nil) + ipAddress := "255.128.255.128" + request.Header.Set("CF-Connecting-IP", ipAddress) + mux.ServeHTTP(recorder, request) + require.Equal(t, http.StatusOK, recorder.Code) + + logged := done() + require.Len(t, logged, 2) + require.Equal(t, "starting request", logged[0].Message) + require.Equal(t, ipAddress, logged[0].Data["ip"]) + require.Equal(t, "finished request", logged[1].Message) + require.Equal(t, ipAddress, logged[1].Data["ip"]) +} diff --git a/support/http/logging_middleware.go b/support/http/logging_middleware.go index 2cc957ac68..540dbd1243 100644 --- a/support/http/logging_middleware.go +++ b/support/http/logging_middleware.go @@ -8,6 +8,7 @@ import ( "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" + "github.com/stellar/go/support/http/mutil" "github.com/stellar/go/support/log" ) @@ -136,6 +137,7 @@ func logEndOfRequest( "subsys": "http", "path": r.URL.String(), "method": r.Method, + "ip": r.RemoteAddr, "status": mw.Status(), "bytes": mw.BytesWritten(), "duration": duration, diff --git a/support/http/logging_middleware_test.go b/support/http/logging_middleware_test.go index 0e2eb45bb2..3ba4d651db 100644 --- a/support/http/logging_middleware_test.go +++ b/support/http/logging_middleware_test.go @@ -6,9 +6,10 @@ import ( "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" + "github.com/stretchr/testify/assert" + "github.com/stellar/go/support/http/httptest" "github.com/stellar/go/support/log" - "github.com/stretchr/testify/assert" ) // setXFFMiddleware sets "X-Forwarded-For" header to test LoggingMiddlewareWithOptions. @@ -143,7 +144,7 @@ func TestHTTPMiddlewareWithOptions(t *testing.T) { assert.Equal(t, req1, logged[2].Data["req"]) assert.Equal(t, "/path/1234", logged[2].Data["path"]) assert.Equal(t, "/path/{value}", logged[2].Data["route"]) - assert.Equal(t, 9, len(logged[2].Data)) + assert.Equal(t, 10, len(logged[2].Data)) assert.Equal(t, "starting request", logged[3].Message) assert.Equal(t, "http", logged[3].Data["subsys"]) @@ -162,7 +163,7 @@ func TestHTTPMiddlewareWithOptions(t *testing.T) { assert.Equal(t, req2, logged[4].Data["req"]) assert.Equal(t, "/not_found", logged[4].Data["path"]) assert.Equal(t, "/not_found", logged[4].Data["route"]) - assert.Equal(t, 9, len(logged[4].Data)) + assert.Equal(t, 10, len(logged[4].Data)) assert.Equal(t, "starting request", logged[5].Message) assert.Equal(t, "http", logged[5].Data["subsys"]) @@ -181,7 +182,7 @@ func TestHTTPMiddlewareWithOptions(t *testing.T) { assert.Equal(t, req3, logged[6].Data["req"]) assert.Equal(t, "/really_not_found", logged[6].Data["path"]) assert.Equal(t, "", logged[6].Data["route"]) - assert.Equal(t, 9, len(logged[6].Data)) + assert.Equal(t, 10, len(logged[6].Data)) } } diff --git a/support/http/mux.go b/support/http/mux.go index ca041d3797..1d64f99812 100644 --- a/support/http/mux.go +++ b/support/http/mux.go @@ -4,6 +4,7 @@ import ( "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/rs/cors" + "github.com/stellar/go/support/log" )