From 7de1cf7cc78f66de41ca7fa94799c8907db96d0d Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 10 Oct 2023 19:26:36 +0100 Subject: [PATCH] Add read only mode to block all the queries with mutations. --- README.md | 8 +++++++- graphql.go | 9 +++++++++ main.go | 1 + struct_config.go | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dcca27b..677c79c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ I wanted to monitor the queries and responses of our graphql endpoint, but we di * MONITORING: Extracting user id from JWT token and adding it as a label to the metrics * MONITORING: Extracting the query name and type and adding it as a label to the metrics * MONITORING: Calculating the query duration and adding it to the metrics -* SPEED: Caching the queries +* SPEED: Caching the queries, together with per-query cache and TTL * SECURITY: Blocking schema introspection * SECURITY: Rate limiting queries based on user role @@ -41,6 +41,7 @@ I wanted to monitor the queries and responses of our graphql endpoint, but we di * `LOG_LEVEL` - the log level (default: `info`) * `BLOCK_SCHEMA_INTROSPECTION` - blocks the schema introspection (default: `false`) * `ENABLE_ACCESS_LOG` - enable the access log (default: `false`) +* `READ_ONLY_MODE` - enable the read only mode (default: `false`) ### Caching @@ -85,6 +86,11 @@ If you'd like to change it - mount your configmap as `/app/ratelimit.json` file. Remember to include the `-` role, which is used for unauthenticated users or when claim can't be found for any reason. If rate limit has been reached - the proxy will return `429 Too Many Requests` error. + +### Read only mode + +You can enable the read only mode by setting the `READ_ONLY_MODE` environment variable to `true` - which will block all the `mutation` queries. + ### Monitoring endpoint Example metrics produced by the proxy: diff --git a/graphql.go b/graphql.go index c7e0dbc..5b7c359 100644 --- a/graphql.go +++ b/graphql.go @@ -2,6 +2,7 @@ package main import ( "strconv" + "strings" fiber "github.com/gofiber/fiber/v2" "github.com/graphql-go/graphql/language/ast" @@ -62,6 +63,14 @@ func parseGraphQLQuery(c *fiber.Ctx) (operationType, operationName string, cache for _, d := range p.Definitions { if oper, ok := d.(*ast.OperationDefinition); ok { operationType = oper.Operation + if strings.ToLower(operationType) == "mutation" && cfg.Server.ReadOnlyMode { + cfg.Logger.Warning("Mutation blocked", m) + cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil) + c.Status(403).SendString("The server is in read-only mode") + should_block = true + return + } + if oper.Name != nil { operationName = oper.Name.Value } else { diff --git a/main.go b/main.go index a08201e..3d3c3ee 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ func parseConfig() { c.Client.GQLClient = graphql.NewConnection() c.Client.GQLClient.SetEndpoint(c.Server.HostGraphQL) c.Server.AccessLog = envutil.GetBool("ENABLE_ACCESS_LOG", false) + c.Server.ReadOnlyMode = envutil.GetBool("READ_ONLY_MODE", false) cfg = &c enableCache() // takes close to no resources, but can be used with dynamic query cache loadRatelimitConfig() diff --git a/struct_config.go b/struct_config.go index 8db8784..d15f47e 100644 --- a/struct_config.go +++ b/struct_config.go @@ -18,6 +18,7 @@ type config struct { PortMonitoring int HostGraphQL string AccessLog bool + ReadOnlyMode bool } Client struct {