Cleanup pt 1 (#4)

* Disable startup headers.

* Add banning / unbanning of specific user.
This commit is contained in:
2023-10-19 14:36:16 +01:00
committed by GitHub
parent 2be4f17ea3
commit 92359c1114
9 changed files with 223 additions and 12 deletions
+59 -5
View File
@@ -8,6 +8,23 @@ This project is in active use by [telegram-bot.app](https://telegram-bot.app), a
You can find the example of the Kubernetes manifest in the [example deployment](static/kubernetes-deployment.yaml) file. You can find the example of the Kubernetes manifest in the [example deployment](static/kubernetes-deployment.yaml) file.
- [graphql monitoring proxy](#graphql-monitoring-proxy)
- [Why this project exists](#why-this-project-exists)
- [Endpoints](#endpoints)
- [Features](#features)
- [Configuration](#configuration)
- [Speed](#speed)
- [Caching](#caching)
- [Security](#security)
- [Role-based rate limiting](#role-based-rate-limiting)
- [Read-only mode](#read-only-mode)
- [Allowing access to listed URLs](#allowing-access-to-listed-urls)
- [Blocking introspection](#blocking-introspection)
- [API endpoints](#api-endpoints)
- [Ban or unban the user](#ban-or-unban-the-user)
- [Monitoring endpoint](#monitoring-endpoint)
### Why this project exists ### Why this project exists
I wanted to monitor the queries and responses of our graphql endpoint. Still, we didn't want to pay the price of the graphql server itself ( and I will not point fingers at a particular well-known project), as monitoring and basic security features should be a standard, free functionality. I wanted to monitor the queries and responses of our graphql endpoint. Still, we didn't want to pay the price of the graphql server itself ( and I will not point fingers at a particular well-known project), as monitoring and basic security features should be a standard, free functionality.
@@ -18,6 +35,7 @@ I wanted to monitor the queries and responses of our graphql endpoint. Still, we
* `:9393/metrics` - the prometheus metrics endpoint * `:9393/metrics` - the prometheus metrics endpoint
* `:8080/healthz` - the healthcheck endpoint * `:8080/healthz` - the healthcheck endpoint
* `:8080/livez` - the liveness probe endpoint * `:8080/livez` - the liveness probe endpoint
* `:9090/api/*` - the monitoring proxy API endpoint
### Features ### Features
@@ -32,6 +50,7 @@ I wanted to monitor the queries and responses of our graphql endpoint. Still, we
| security | Rate limiting queries based on user role | | security | Rate limiting queries based on user role |
| security | Blocking mutations in read-only mode | | security | Blocking mutations in read-only mode |
| security | Allow access only to listed URLs | | security | Allow access only to listed URLs |
| security | Ban / unban specific user from accessing the application |
### Configuration ### Configuration
@@ -53,9 +72,13 @@ I wanted to monitor the queries and responses of our graphql endpoint. Still, we
| `ENABLE_ACCESS_LOG` | Enable the access log | `false` | | `ENABLE_ACCESS_LOG` | Enable the access log | `false` |
| `READ_ONLY_MODE` | Enable the read only mode | `false` | | `READ_ONLY_MODE` | Enable the read only mode | `false` |
| `ALLOWED_URLS` | Allow access only to certain URLs | `/v1/graphql,/v1/version` | | `ALLOWED_URLS` | Allow access only to certain URLs | `/v1/graphql,/v1/version` |
| `ENABLE_API` | Enable the monitoring API | `false` |
| `API_PORT` | The port to expose the monitoring API | `9090` |
| `BANNED_USERS_FILE` | The path to the file with banned users | `/go/src/app/banned_users.json` |
### Speed
### Caching #### Caching
The cache engine is enabled in the background by default, using no additional resources. The cache engine is enabled in the background by default, using no additional resources.
You can then start using the cache by setting the `ENABLE_GLOBAL_CACHE` environment variable to `true` - which will enable the cache for all queries without introspection. You can leave the global cache disabled and enable the cache for specific queries by adding the `@cached` directive to the query. You can then start using the cache by setting the `ENABLE_GLOBAL_CACHE` environment variable to `true` - which will enable the cache for all queries without introspection. You can leave the global cache disabled and enable the cache for specific queries by adding the `@cached` directive to the query.
@@ -63,7 +86,9 @@ You can then start using the cache by setting the `ENABLE_GLOBAL_CACHE` environm
In the case of the `@cached` you can add additional parameters to the directive which will set the cache for specific queries to the provided time. In the case of the `@cached` you can add additional parameters to the directive which will set the cache for specific queries to the provided time.
For example, `query MyCachedQuery @cached(ttl: 90) ....` will set the cache for the query to 90 seconds. For example, `query MyCachedQuery @cached(ttl: 90) ....` will set the cache for the query to 90 seconds.
### Role-based rate limiting ### Security
#### Role-based rate limiting
You can rate limit requests using the `ROLE_RATE_LIMIT` environment variable. If enabled, the proxy will rate limit the requests based on the role claim in the JWT token. You can then provide the JSON file in the following format to specify the limits. You can rate limit requests using the `ROLE_RATE_LIMIT` environment variable. If enabled, the proxy will rate limit the requests based on the role claim in the JWT token. You can then provide the JSON file in the following format to specify the limits.
The default interval is `second`, but you can use other values as well. If you want to disable the rate limiting for a specific role, you can set the `req` to `0`. The default interval is `second`, but you can use other values as well. If you want to disable the rate limiting for a specific role, you can set the `req` to `0`.
@@ -101,15 +126,15 @@ Remember to include the `-` role, which is used for unauthenticated users or whe
If rate limit has been reached - the proxy will return `429 Too Many Requests` error. If rate limit has been reached - the proxy will return `429 Too Many Requests` error.
### Read-only mode #### 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. You can enable the read-only mode by setting the `READ_ONLY_MODE` environment variable to `true` - which will block all the `mutation` queries.
### Allowing access to listed URLs #### Allowing access to listed URLs
You can allow access only to certain URLs by setting the `ALLOWED_URLS` environment variable to a comma-separated list of URLs. If enabled - other URLs will return `403 Forbidden` error and request will **not** reach the proxied service. You can allow access only to certain URLs by setting the `ALLOWED_URLS` environment variable to a comma-separated list of URLs. If enabled - other URLs will return `403 Forbidden` error and request will **not** reach the proxied service.
### Blocking introspection #### Blocking introspection
You can block the schema introspection by setting the `BLOCK_SCHEMA_INTROSPECTION` environment variable to `true` - which will block all the queries with introspection parts, like: You can block the schema introspection by setting the `BLOCK_SCHEMA_INTROSPECTION` environment variable to `true` - which will block all the queries with introspection parts, like:
@@ -119,6 +144,35 @@ If you'd like to keep blocking of the schema introspection on but allow one or m
`ALLOWED_INTROSPECTION="__typename,__type"` `ALLOWED_INTROSPECTION="__typename,__type"`
### API endpoints
#### Ban or unban the user
Your monitoring system can detect user misbehaving, for example trying to extract / scrap the data. To prevent user from doing so you can use the simple API to ban the user from accessing the application.
To do so - you need to enable the api by setting env variable `ENABLE_API=true` which will expose the API on the port `API_PORT=9090`. Nedless to say - keep it secure and don't expose it outside of your cluster.
Then you can use the following endpoints:
* `POST /api/user-ban` - ban the user from accessing the application
* `POST /api/user-unban` - unban the user from accessing the application
Both endpoints require the `user_id` parameter to be present in the request body and allow you to provide the reason for the ban.
Example request:
```bash
curl -X POST \
http://localhost:9090/api/user-ban \
-H 'Content-Type: application/json' \
-d '{
"user_id": "1337",
"reason": "Scraping data"
}'
```
Ban details will be stored in the `banned_users.json` file, which you can mount as a file or configmap to the `/go/src/app/banned_users.json` path ( or use `BANNED_USERS_FILE` environment variable to specify the path to the file). The file operation is important if you have multiple instances of the proxy running, as it will allow you to ban the user from accessing the application on all instances.
### Monitoring endpoint ### Monitoring endpoint
Example metrics produced by the proxy: Example metrics produced by the proxy:
+133
View File
@@ -0,0 +1,133 @@
package main
import (
"fmt"
"os"
"time"
fiber "github.com/gofiber/fiber/v2"
"github.com/gofrs/flock"
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
)
var bannedUsersIDs map[string]string = make(map[string]string)
func enableApi() {
if cfg.Server.EnableApi {
apiserver := fiber.New(fiber.Config{
DisableStartupMessage: true,
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
})
api := apiserver.Group("/api")
api.Post("/user-ban", apiBanUser)
api.Post("/user-unban", apiUnbanUser)
go periodicallyReloadBannedUsers()
err := apiserver.Listen(fmt.Sprintf(":%d", cfg.Server.ApiPort))
if err != nil {
cfg.Logger.Critical("Can't start the service", map[string]interface{}{"error": err.Error()})
}
}
}
func periodicallyReloadBannedUsers() {
for {
loadBannedUsers()
cfg.Logger.Debug("Banned users reloaded", map[string]interface{}{"users": bannedUsersIDs})
<-time.After(10 * time.Second)
}
}
func checkIfUserIsBanned(c *fiber.Ctx, userID string) bool {
_, found := bannedUsersIDs[userID]
cfg.Logger.Debug("Checking if user is banned", map[string]interface{}{"user_id": userID, "found": found})
if found {
cfg.Logger.Info("User is banned", map[string]interface{}{"user_id": userID})
c.Status(403).SendString("User is banned")
}
return found
}
type apiBanUserRequest struct {
UserID string `json:"user_id"`
Reason string `json:"reason"`
}
func apiBanUser(c *fiber.Ctx) error {
var req apiBanUserRequest
err := c.BodyParser(&req)
if err != nil {
cfg.Logger.Error("Can't parse the ban user request", map[string]interface{}{"error": err.Error()})
return err
}
bannedUsersIDs[req.UserID] = req.Reason
cfg.Logger.Info("Banned user", map[string]interface{}{"user_id": req.UserID, "reason": req.Reason})
storeBannedUsers()
c.Status(200).SendString("OK: user banned")
return nil
}
func apiUnbanUser(c *fiber.Ctx) error {
var req apiBanUserRequest
err := c.BodyParser(&req)
if err != nil {
cfg.Logger.Error("Can't parse the unban user request", map[string]interface{}{"error": err.Error()})
return err
}
delete(bannedUsersIDs, req.UserID)
cfg.Logger.Info("Unbanned user", map[string]interface{}{"user_id": req.UserID})
storeBannedUsers()
c.Status(200).SendString("OK: user unbanned")
return nil
}
func storeBannedUsers() {
fileLock := flock.New(fmt.Sprintf("%s.lock", cfg.Api.BannedUsersFile))
err := fileLock.Lock()
if err != nil {
cfg.Logger.Error("Can't lock the file", map[string]interface{}{"error": err.Error()})
return
}
defer fileLock.Unlock()
data, err := json.Marshal(bannedUsersIDs)
if err != nil {
cfg.Logger.Error("Can't marshal banned users", map[string]interface{}{"error": err.Error()})
return
}
err = os.WriteFile(cfg.Api.BannedUsersFile, data, 0644)
if err != nil {
cfg.Logger.Error("Can't write banned users to file", map[string]interface{}{"error": err.Error()})
return
}
}
func loadBannedUsers() {
if _, err := os.Stat(cfg.Api.BannedUsersFile); os.IsNotExist(err) {
cfg.Logger.Info("Banned users file doesn't exist - creating it", map[string]interface{}{"file": cfg.Api.BannedUsersFile})
_, err := os.Create(cfg.Api.BannedUsersFile)
if err != nil {
cfg.Logger.Error("Can't create the file", map[string]interface{}{"error": err.Error()})
return
}
}
fileLock := flock.New(fmt.Sprintf("%s.lock", cfg.Api.BannedUsersFile))
err := fileLock.RLock() // Use RLock for read lock
if err != nil {
cfg.Logger.Error("Can't lock the file [load]", map[string]interface{}{"error": err.Error()})
return
}
defer fileLock.Unlock()
data, err := os.ReadFile(cfg.Api.BannedUsersFile)
if err != nil {
cfg.Logger.Error("Can't read banned users from file", map[string]interface{}{"error": err.Error()})
return
}
err = json.Unmarshal(data, &bannedUsersIDs)
if err != nil {
cfg.Logger.Error("Can't unmarshal banned users", map[string]interface{}{"error": err.Error()})
return
}
}
+1 -2
View File
@@ -1,7 +1,6 @@
package main package main
import ( import (
"fmt"
"time" "time"
fiber "github.com/gofiber/fiber/v2" fiber "github.com/gofiber/fiber/v2"
@@ -10,7 +9,7 @@ import (
) )
func calculateHash(c *fiber.Ctx) string { func calculateHash(c *fiber.Ctx) string {
return strutil.Md5(fmt.Sprintf("%s", c.Body())) return strutil.Md5(c.Body())
} }
func enableCache() { func enableCache() {
+2 -1
View File
@@ -6,6 +6,7 @@ require (
github.com/VictoriaMetrics/metrics v1.24.0 github.com/VictoriaMetrics/metrics v1.24.0
github.com/buger/jsonparser v1.1.1 github.com/buger/jsonparser v1.1.1
github.com/gofiber/fiber/v2 v2.50.0 github.com/gofiber/fiber/v2 v2.50.0
github.com/gofrs/flock v0.8.1
github.com/gookit/goutil v0.6.14 github.com/gookit/goutil v0.6.14
github.com/graphql-go/graphql v0.8.1 github.com/graphql-go/graphql v0.8.1
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
@@ -26,7 +27,7 @@ require (
github.com/klauspost/compress v1.17.1 // indirect github.com/klauspost/compress v1.17.1 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
+4 -1
View File
@@ -14,6 +14,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -43,8 +45,9 @@ github.com/lukaszraczylo/go-simple-graphql v1.1.34/go.mod h1:LywSrNo3Otp69z1pTZ7
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+4
View File
@@ -50,9 +50,13 @@ func parseConfig() {
return strings.Split(urls, ",") return strings.Split(urls, ",")
}() }()
c.Client.FastProxyClient = createFasthttpClient() c.Client.FastProxyClient = createFasthttpClient()
c.Server.EnableApi = envutil.GetBool("ENABLE_API", false)
c.Server.ApiPort = envutil.GetInt("API_PORT", 9090)
c.Api.BannedUsersFile = envutil.Getenv("BANNED_USERS_FILE", "/go/src/app/banned_users.json")
cfg = &c cfg = &c
enableCache() // takes close to no resources, but can be used with dynamic query cache enableCache() // takes close to no resources, but can be used with dynamic query cache
loadRatelimitConfig() loadRatelimitConfig()
enableApi()
} }
func main() { func main() {
+5 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/VictoriaMetrics/metrics" "github.com/VictoriaMetrics/metrics"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gookit/goutil/envutil" "github.com/gookit/goutil/envutil"
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
logging "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" logging "github.com/lukaszraczylo/graphql-monitoring-proxy/logging"
) )
@@ -31,7 +32,10 @@ func NewMonitoring() *MetricsSetup {
} }
func (ms *MetricsSetup) startPrometheusEndpoint() { func (ms *MetricsSetup) startPrometheusEndpoint() {
app := fiber.New() app := fiber.New(fiber.Config{
DisableStartupMessage: true,
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
})
app.Get("/metrics", ms.metricsEndpoint) app.Get("/metrics", ms.metricsEndpoint)
err := app.Listen(fmt.Sprintf(":%d", envutil.GetInt("MONITORING_PORT", 9393))) err := app.Listen(fmt.Sprintf(":%d", envutil.GetInt("MONITORING_PORT", 9393)))
if err != nil { if err != nil {
@@ -46,7 +50,6 @@ func (ms *MetricsSetup) metricsEndpoint(c *fiber.Ctx) error {
func (ms *MetricsSetup) AddMetricsPrefix(prefix string) { func (ms *MetricsSetup) AddMetricsPrefix(prefix string) {
ms.metrics_prefix = prefix ms.metrics_prefix = prefix
return
} }
func (ms *MetricsSetup) ListActiveMetrics() []string { func (ms *MetricsSetup) ListActiveMetrics() []string {
+9 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/cors"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring" libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring"
) )
@@ -15,7 +16,10 @@ var json = jsoniter.ConfigCompatibleWithStandardLibrary
// StartHTTPProxy starts the HTTP and points it to the GraphQL server. // StartHTTPProxy starts the HTTP and points it to the GraphQL server.
func StartHTTPProxy() { func StartHTTPProxy() {
server := fiber.New() server := fiber.New(fiber.Config{
DisableStartupMessage: true,
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
})
server.Use(cors.New(cors.Config{ server.Use(cors.New(cors.Config{
AllowOrigins: "*", AllowOrigins: "*",
@@ -69,6 +73,10 @@ func processGraphQLRequest(c *fiber.Ctx) error {
extractedUserID, extractedRoleName = extractClaimsFromJWTHeader(string(authorization)) extractedUserID, extractedRoleName = extractClaimsFromJWTHeader(string(authorization))
} }
if checkIfUserIsBanned(c, extractedUserID) {
return nil
}
if len(cfg.Client.RoleFromHeader) > 0 { if len(cfg.Client.RoleFromHeader) > 0 {
extractedRoleName = string(c.Request().Header.Peek(cfg.Client.RoleFromHeader)) extractedRoleName = string(c.Request().Header.Peek(cfg.Client.RoleFromHeader))
if extractedRoleName == "" { if extractedRoleName == "" {
+6
View File
@@ -21,6 +21,8 @@ type config struct {
AccessLog bool AccessLog bool
ReadOnlyMode bool ReadOnlyMode bool
AllowURLs []string AllowURLs []string
EnableApi bool
ApiPort int
} }
Client struct { Client struct {
@@ -39,6 +41,10 @@ type config struct {
CacheClient *libpack_cache.Cache CacheClient *libpack_cache.Cache
} }
Api struct {
BannedUsersFile string
}
Security struct { Security struct {
BlockIntrospection bool BlockIntrospection bool
IntrospectionAllowed []string IntrospectionAllowed []string