From bc86abd8f8caab5d3ca6f29a72941a21738c1e62 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Wed, 30 Oct 2024 16:38:09 +0100 Subject: [PATCH 1/5] chore: move blocklist middleware from route blocklist to middlewares --- pkg/config.go | 56 +++++++++--------- pkg/middleware.go | 22 ++++--- pkg/middleware_test.go | 35 +++++++---- pkg/route.go | 129 +++++++++++++++++++++++------------------ pkg/var.go | 8 ++- 5 files changed, 141 insertions(+), 109 deletions(-) diff --git a/pkg/config.go b/pkg/config.go index dc83b70..4ad163e 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -30,7 +30,7 @@ var cfg *Gateway type Config struct { file string } -type BasicRule struct { +type BasicRuleMiddleware struct { Username string `yaml:"username"` Password string `yaml:"password"` } @@ -54,10 +54,10 @@ type Cors struct { Headers map[string]string `yaml:"headers"` } -// JWTRuler authentication using HTTP GET method +// JWTRuleMiddleware authentication using HTTP GET method // -// JWTRuler contains the authentication details -type JWTRuler struct { +// JWTRuleMiddleware contains the authentication details +type JWTRuleMiddleware struct { // URL contains the authentication URL, it supports HTTP GET method only. URL string `yaml:"url"` // RequiredHeaders , contains required before sending request to the backend. @@ -84,16 +84,21 @@ type RateLimiter struct { Rule int `yaml:"rule"` } +type AccessRuleMiddleware struct { + ResponseCode int `yaml:"responseCode"` // HTTP Response code +} + // Middleware defined the route middleware type Middleware struct { //Path contains the name of middleware and must be unique Name string `yaml:"name"` // Type contains authentication types // - // basic, jwt, auth0, rateLimit - Type string `yaml:"type"` + // basic, jwt, auth0, rateLimit, access + Type string `yaml:"type"` // Middleware type [basic, jwt, auth0, rateLimit, access] + Paths []string `yaml:"paths"` // Protected paths // Rule contains rule type of - Rule interface{} `yaml:"rule"` + Rule interface{} `yaml:"rule"` // Middleware rule } type MiddlewareName struct { name string `yaml:"name"` @@ -136,7 +141,7 @@ type Route struct { // Eg: [ 403, 405, 500 ] InterceptErrors []int `yaml:"interceptErrors"` // Middlewares Defines route middleware from Middleware names - Middlewares []RouteMiddleware `yaml:"middlewares"` + Middlewares []string `yaml:"middlewares"` } // Gateway contains Goma Proxy Gateway's configs @@ -287,12 +292,7 @@ func initConfig(configFile string) { "Access-Control-Max-Age": "1728000", }, }, - Middlewares: []RouteMiddleware{ - { - Path: "/user", - Rules: []string{"basic-auth"}, - }, - }, + Middlewares: []string{"basic-auth"}, }, { Name: "Hostname example", @@ -308,14 +308,14 @@ func initConfig(configFile string) { { Name: "basic-auth", Type: "basic", - Rule: BasicRule{ + Rule: BasicRuleMiddleware{ Username: "goma", Password: "goma", }, }, { Name: "jwt", Type: "jwt", - Rule: JWTRuler{ + Rule: JWTRuleMiddleware{ URL: "https://www.googleapis.com/auth/userinfo.email", RequiredHeaders: []string{ "Authorization", @@ -361,40 +361,40 @@ func (Gateway) Setup(conf string) *Gateway { return &Gateway{} } -func (middleware Middleware) name() { -} -func ToJWTRuler(input interface{}) (JWTRuler, error) { - jWTRuler := new(JWTRuler) +// getJWTMiddleware returns JWTRuleMiddleware,error +func getJWTMiddleware(input interface{}) (JWTRuleMiddleware, error) { + jWTRuler := new(JWTRuleMiddleware) var bytes []byte bytes, err := yaml.Marshal(input) if err != nil { - return JWTRuler{}, fmt.Errorf("error parsing yaml: %v", err) + return JWTRuleMiddleware{}, fmt.Errorf("error parsing yaml: %v", err) } err = yaml.Unmarshal(bytes, jWTRuler) if err != nil { - return JWTRuler{}, fmt.Errorf("error parsing yaml: %v", err) + return JWTRuleMiddleware{}, fmt.Errorf("error parsing yaml: %v", err) } if jWTRuler.URL == "" { - return JWTRuler{}, fmt.Errorf("error parsing yaml: empty url in jwt auth middleware") + return JWTRuleMiddleware{}, fmt.Errorf("error parsing yaml: empty url in jwt auth middleware") } return *jWTRuler, nil } -func ToBasicAuth(input interface{}) (BasicRule, error) { - basicAuth := new(BasicRule) +// getBasicAuthMiddleware returns BasicRuleMiddleware,error +func getBasicAuthMiddleware(input interface{}) (BasicRuleMiddleware, error) { + basicAuth := new(BasicRuleMiddleware) var bytes []byte bytes, err := yaml.Marshal(input) if err != nil { - return BasicRule{}, fmt.Errorf("error parsing yaml: %v", err) + return BasicRuleMiddleware{}, fmt.Errorf("error parsing yaml: %v", err) } err = yaml.Unmarshal(bytes, basicAuth) if err != nil { - return BasicRule{}, fmt.Errorf("error parsing yaml: %v", err) + return BasicRuleMiddleware{}, fmt.Errorf("error parsing yaml: %v", err) } if basicAuth.Username == "" || basicAuth.Password == "" { - return BasicRule{}, fmt.Errorf("error parsing yaml: empty username/password in %s middleware", basicAuth) + return BasicRuleMiddleware{}, fmt.Errorf("error parsing yaml: empty username/password in %s middleware", basicAuth) } return *basicAuth, nil diff --git a/pkg/middleware.go b/pkg/middleware.go index 0973d6d..b0a4951 100644 --- a/pkg/middleware.go +++ b/pkg/middleware.go @@ -7,7 +7,7 @@ import ( "strings" ) -func searchMiddleware(rules []string, middlewares []Middleware) (Middleware, error) { +func getMiddleware(rules []string, middlewares []Middleware) (Middleware, error) { for _, m := range middlewares { if slices.Contains(rules, m.Name) { return m, nil @@ -17,17 +17,6 @@ func searchMiddleware(rules []string, middlewares []Middleware) (Middleware, err return Middleware{}, errors.New("middleware not found with name: [" + strings.Join(rules, ";") + "]") } -func getMiddleware(rule string, middlewares []Middleware) (Middleware, error) { - for _, m := range middlewares { - if strings.Contains(rule, m.Name) { - - return m, nil - } - continue - } - - return Middleware{}, errors.New("no middleware found with name " + rule) -} type RoutePath struct { route Route @@ -36,3 +25,12 @@ type RoutePath struct { middlewares []Middleware router *mux.Router } + +func doesExist(tyName string) bool { + middlewareList := []string{BasicAuth, JWTAuth, AccessMiddleware} + if slices.Contains(middlewareList, tyName) { + return true + + } + return false +} diff --git a/pkg/middleware_test.go b/pkg/middleware_test.go index 822bb82..7e8161c 100644 --- a/pkg/middleware_test.go +++ b/pkg/middleware_test.go @@ -31,16 +31,29 @@ func TestMiddleware(t *testing.T) { TestInit(t) middlewares := []Middleware{ { - Name: "basic-auth", - Type: "basic", - Rule: BasicRule{ + Name: "basic-auth", + Type: "basic", + Paths: []string{"/", "/admin"}, + Rule: BasicRuleMiddleware{ Username: "goma", Password: "goma", }, - }, { - Name: MidName, - Type: "jwt", - Rule: JWTRuler{ + }, + { + Name: "forbidden path acces", + Type: "access", + Paths: []string{"/", "/admin"}, + Rule: BasicRuleMiddleware{ + Username: "goma", + Password: "goma", + }, + }, + + { + Name: "jwt", + Type: "jwt", + Paths: []string{"/", "/admin"}, + Rule: JWTRuleMiddleware{ URL: "https://www.googleapis.com/auth/userinfo.email", Headers: map[string]string{}, Params: map[string]string{}, @@ -61,21 +74,21 @@ func TestMiddleware(t *testing.T) { func TestReadMiddleware(t *testing.T) { TestMiddleware(t) middlewares := getMiddlewares(t) - middleware, err := searchMiddleware(rules, middlewares) + middleware, err := getMiddleware(rules, middlewares) if err != nil { t.Fatalf("Error searching middleware %s", err.Error()) } switch middleware.Type { case "basic": log.Println("Basic auth") - basicAuth, err := ToBasicAuth(middleware.Rule) + basicAuth, err := getBasicAuthMiddleware(middleware.Rule) if err != nil { log.Fatalln("error:", err) } log.Printf("Username: %s and password: %s\n", basicAuth.Username, basicAuth.Password) case "jwt": log.Println("JWT auth") - jwt, err := ToJWTRuler(middleware.Rule) + jwt, err := getJWTMiddleware(middleware.Rule) if err != nil { log.Fatalln("error:", err) } @@ -89,7 +102,7 @@ func TestReadMiddleware(t *testing.T) { func TestFoundMiddleware(t *testing.T) { middlewares := getMiddlewares(t) - middleware, err := searchMiddleware(rules, middlewares) + middleware, err := getAuthMiddleware("jwt", middlewares) if err != nil { t.Errorf("Error getting middleware %v", err) } diff --git a/pkg/route.go b/pkg/route.go index 218eaa6..46d330b 100644 --- a/pkg/route.go +++ b/pkg/route.go @@ -42,67 +42,87 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router { } for _, route := range gateway.Routes { if route.Path != "" { - blM := middleware.BlockListMiddleware{ - Path: route.Path, - List: route.Blocklist, - } - // Apply route middlewares + + // Apply middlewares to route for _, mid := range route.Middlewares { - if mid.Path != "" { - secureRouter := r.PathPrefix(util.ParseURLPath(route.Path + mid.Path)).Subrouter() - proxyRoute := ProxyRoute{ - path: route.Path, - rewrite: route.Rewrite, - destination: route.Destination, - disableXForward: route.DisableHeaderXForward, - cors: route.Cors, - } - rMiddleware, err := searchMiddleware(mid.Rules, middlewares) + if mid != "" { + // Get Access middleware if it does exist + accessMiddleware, err := getMiddleware([]string{mid}, middlewares) if err != nil { logger.Error("Error: %v", err.Error()) } else { - //Check Authentication middleware - switch rMiddleware.Type { - case "basic": - basicAuth, err := ToBasicAuth(rMiddleware.Rule) - if err != nil { - logger.Error("Error: %s", err.Error()) - } else { - amw := middleware.AuthBasic{ - Username: basicAuth.Username, - Password: basicAuth.Password, - Headers: nil, - Params: nil, - } - // Apply JWT authentication middleware - secureRouter.Use(amw.AuthMiddleware) - secureRouter.Use(CORSHandler(route.Cors)) - secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler - secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler + // Apply access middleware + if accessMiddleware.Type == AccessMiddleware { + blM := middleware.BlockListMiddleware{ + Path: route.Path, + List: accessMiddleware.Paths, } - case "jwt": - jwt, err := ToJWTRuler(rMiddleware.Rule) - if err != nil { - logger.Error("Error: %s", err.Error()) - } else { - amw := middleware.JwtAuth{ - AuthURL: jwt.URL, - RequiredHeaders: jwt.RequiredHeaders, - Headers: jwt.Headers, - Params: jwt.Params, + r.Use(blM.BlocklistMiddleware) + + } + + } + // Get route authentication middleware if it does exist + rMiddleware, err := getMiddleware([]string{mid}, middlewares) + if err != nil { + //Error: middleware not found + logger.Error("Error: %v", err.Error()) + } else { + for _, midPath := range rMiddleware.Paths { + proxyRoute := ProxyRoute{ + path: route.Path, + rewrite: route.Rewrite, + destination: route.Destination, + disableXForward: route.DisableHeaderXForward, + cors: route.Cors, + } + secureRouter := r.PathPrefix(util.ParseURLPath(route.Path + midPath)).Subrouter() + //Check Authentication middleware + switch rMiddleware.Type { + case BasicAuth: + basicAuth, err := getBasicAuthMiddleware(rMiddleware.Rule) + if err != nil { + logger.Error("Error: %s", err.Error()) + } else { + amw := middleware.AuthBasic{ + Username: basicAuth.Username, + Password: basicAuth.Password, + Headers: nil, + Params: nil, + } + // Apply JWT authentication middleware + secureRouter.Use(amw.AuthMiddleware) + secureRouter.Use(CORSHandler(route.Cors)) + secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler + secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler + } + case JWTAuth: + jwt, err := getJWTMiddleware(rMiddleware.Rule) + if err != nil { + logger.Error("Error: %s", err.Error()) + } else { + amw := middleware.JwtAuth{ + AuthURL: jwt.URL, + RequiredHeaders: jwt.RequiredHeaders, + Headers: jwt.Headers, + Params: jwt.Params, + } + // Apply JWT authentication middleware + secureRouter.Use(amw.AuthMiddleware) + secureRouter.Use(CORSHandler(route.Cors)) + secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler + secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler + + } + case "OAuth": + logger.Error("OAuth is not yet implemented") + logger.Info("Auth middleware ignored") + default: + if !doesExist(rMiddleware.Type) { + logger.Error("Unknown middleware type %s", rMiddleware.Type) } - // Apply JWT authentication middleware - secureRouter.Use(amw.AuthMiddleware) - secureRouter.Use(CORSHandler(route.Cors)) - secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler - secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler } - case "OAuth": - logger.Error("OAuth is not yet implemented") - logger.Info("Auth middleware ignored") - default: - logger.Error("Unknown middleware type %s", rMiddleware.Type) } @@ -122,9 +142,6 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router { router := r.PathPrefix(route.Path).Subrouter() // Apply route Cors router.Use(CORSHandler(route.Cors)) - // Add block access middleware to route, if defined - router.Use(blM.BlocklistMiddleware) - //Domain/host based request routing if route.Host != "" { router.Host(route.Host).PathPrefix("").Handler(proxyRoute.ProxyHandler()) } else { diff --git a/pkg/var.go b/pkg/var.go index 2dd5a1b..c22d2da 100644 --- a/pkg/var.go +++ b/pkg/var.go @@ -1,5 +1,9 @@ package pkg -const ConfigFile = "/config/goma.yml" -const accessControlAllowOrigin = "Access-Control-Allow-Origin" +const ConfigFile = "/config/goma.yml" // Default configuration file +const accessControlAllowOrigin = "Access-Control-Allow-Origin" // Cors const serverName = "Goma" +const AccessMiddleware = "access" // access middleware +const BasicAuth = "basic" // basic authentication middleware +const JWTAuth = "jwt" // JWT authentication middleware +const OAuth = "OAuth" // OAuth authentication middleware From c332309a566a401d6d93301948be4a6ec76fad25 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Wed, 30 Oct 2024 16:45:13 +0100 Subject: [PATCH 2/5] refactor: rename functions --- pkg/middleware/{bloclist.go => access-middleware.go} | 4 ++-- pkg/middleware/middleware.go | 9 +++++---- pkg/route.go | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) rename pkg/middleware/{bloclist.go => access-middleware.go} (93%) diff --git a/pkg/middleware/bloclist.go b/pkg/middleware/access-middleware.go similarity index 93% rename from pkg/middleware/bloclist.go rename to pkg/middleware/access-middleware.go index f389a52..decd703 100644 --- a/pkg/middleware/bloclist.go +++ b/pkg/middleware/access-middleware.go @@ -25,8 +25,8 @@ import ( "time" ) -// BlocklistMiddleware checks if the request path is forbidden and returns 403 Forbidden -func (blockList BlockListMiddleware) BlocklistMiddleware(next http.Handler) http.Handler { +// AccessMiddleware checks if the request path is forbidden and returns 403 Forbidden +func (blockList AccessListMiddleware) AccessMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for _, block := range blockList.List { if isPathBlocked(r.URL.Path, util.ParseURLPath(blockList.Path+block)) { diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index a9707e2..04433fe 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -50,6 +50,7 @@ func NewRateLimiterWindow(requests int, window time.Duration) *RateLimiter { } } +// TokenRateLimiter stores tokenRate limit type TokenRateLimiter struct { tokens int maxTokens int @@ -65,7 +66,7 @@ type ProxyResponseError struct { Message string `json:"message"` } -// JwtAuth Define struct +// JwtAuth stores JWT configuration type JwtAuth struct { AuthURL string RequiredHeaders []string @@ -73,20 +74,20 @@ type JwtAuth struct { Params map[string]string } -// AuthenticationMiddleware Define struct +// AuthenticationMiddleware Define struct type AuthenticationMiddleware struct { AuthURL string RequiredHeaders []string Headers map[string]string Params map[string]string } -type BlockListMiddleware struct { +type AccessListMiddleware struct { Path string Destination string List []string } -// AuthBasic Define Basic auth +// AuthBasic contains Basic auth configuration type AuthBasic struct { Username string Password string diff --git a/pkg/route.go b/pkg/route.go index 46d330b..8623102 100644 --- a/pkg/route.go +++ b/pkg/route.go @@ -53,11 +53,11 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router { } else { // Apply access middleware if accessMiddleware.Type == AccessMiddleware { - blM := middleware.BlockListMiddleware{ + blM := middleware.AccessListMiddleware{ Path: route.Path, List: accessMiddleware.Paths, } - r.Use(blM.BlocklistMiddleware) + r.Use(blM.AccessMiddleware) } From c45b62ec67460b461f3d2037559db017986a3fe9 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Wed, 30 Oct 2024 16:58:22 +0100 Subject: [PATCH 3/5] chore: update default configuration --- pkg/config.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pkg/config.go b/pkg/config.go index 4ad163e..17e0cfa 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -134,8 +134,6 @@ type Route struct { DisableHeaderXForward bool `yaml:"disableHeaderXForward"` // HealthCheck Defines the backend is health check PATH HealthCheck string `yaml:"healthCheck"` - // Blocklist Defines route blacklist - Blocklist []string `yaml:"blocklist"` // InterceptErrors intercepts backend errors based on the status codes // // Eg: [ 403, 405, 500 ] @@ -283,7 +281,6 @@ func initConfig(configFile string) { Destination: "https://example.com", Rewrite: "/", HealthCheck: "", - Blocklist: []string{}, Cors: Cors{ Origins: []string{"http://localhost:3000", "https://dev.example.com"}, Headers: map[string]string{ @@ -292,7 +289,7 @@ func initConfig(configFile string) { "Access-Control-Max-Age": "1728000", }, }, - Middlewares: []string{"basic-auth"}, + Middlewares: []string{"basic-auth", "api-forbidden-paths"}, }, { Name: "Hostname example", @@ -307,14 +304,23 @@ func initConfig(configFile string) { Middlewares: []Middleware{ { Name: "basic-auth", - Type: "basic", + Type: BasicAuth, + Paths: []string{ + "/user", + "/admin", + "/account", + }, Rule: BasicRuleMiddleware{ Username: "goma", Password: "goma", }, }, { Name: "jwt", - Type: "jwt", + Type: JWTAuth, + Paths: []string{ + "/protected-access", + "/example-of-jwt", + }, Rule: JWTRuleMiddleware{ URL: "https://www.googleapis.com/auth/userinfo.email", RequiredHeaders: []string{ @@ -324,6 +330,17 @@ func initConfig(configFile string) { Params: map[string]string{}, }, }, + { + Name: "api-forbidden-paths", + Type: AccessMiddleware, + Paths: []string{ + "/swagger-ui/*", + "/v2/swagger-ui/*", + "/api-docs/*", + "/internal/*", + "/actuator/*", + }, + }, }, } yamlData, err := yaml.Marshal(&conf) From c228c605ef015111dafe89ca7506b4764b0f38a5 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Wed, 30 Oct 2024 18:01:53 +0100 Subject: [PATCH 4/5] refactor: refatcoring code --- README.md | 71 +++++++++++------------------ goma.yml | 68 +++++++++++---------------- internal/logger/logger.go | 4 +- pkg/config.go | 6 --- pkg/middleware/access-middleware.go | 2 +- pkg/middleware/middleware.go | 2 +- util/constants.go | 2 +- 7 files changed, 58 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 413d603..43de580 100644 --- a/README.md +++ b/README.md @@ -145,46 +145,12 @@ gateway: Access-Control-Allow-Headers: 'Origin, Authorization, Accept, Content-Type, Access-Control-Allow-Headers, X-Client-Id, X-Session-Id' Access-Control-Allow-Credentials: 'true' Access-Control-Max-Age: 1728000 - #### Define route blocklist paths - blocklist: - - /swagger-ui/* - - /v2/swagger-ui/* - - /api-docs/* - - /internal/* - - /actuator/* ##### Define route middlewares from middlewares names ## The name must be unique ## List of middleware name middlewares: - # path to protect - - path: /user - # Rules defines which specific middleware applies to a route path - rules: - - basic-auth - # path to protect - - path: /path-example - # Rules defines which specific middleware applies to a route path - rules: - - jwt - # path to protect - - path: /admin - # Rules defines which specific middleware applies to a route path - rules: - - basic-auth - # path to protect - - path: /path-example - # Rules defines which specific middleware applies to a route path - rules: - - jwt - - path: /history - http: - url: http://security-service:8080/security/authUser - headers: - #Key from backend authentication header, and inject to the request with custom key name - userId: X-Auth-UserId - userCountryId: X-Auth-UserCountryId - params: - userCountryId: X-countryId + - api-forbidden-paths + - basic-auth # Example of a route | 2 - name: Authentication service path: /auth @@ -192,8 +158,8 @@ gateway: destination: 'http://security-service:8080' healthCheck: /internal/health/ready cors: {} - blocklist: [] - middlewares: [] + middlewares: + - api-forbidden-paths # Example of a route | 3 - name: Basic auth path: /protected @@ -201,7 +167,6 @@ gateway: destination: 'http://notification-service:8080' healthCheck: cors: {} - blocklist: [] middlewares: [] #Defines proxy middlewares @@ -210,6 +175,10 @@ middlewares: - name: basic-auth # Authentication types | jwt, basic, OAuth type: basic + paths: + - /user + - /admin + - /account rule: username: admin password: admin @@ -218,6 +187,10 @@ middlewares: # Authentication types | jwt, basic, OAuth # jwt authorization based on the result of backend's response and continue the request when the client is authorized type: jwt + # Paths to protect + paths: + - /protected-access + - /example-of-jwt rule: # This is an example URL url: https://www.googleapis.com/auth/userinfo.email @@ -234,12 +207,22 @@ middlewares: # Add header to the next request from AuthRequest header, depending on your requirements # Key is AuthRequest's response header Key, and value is Request's header Key # In case you want to get headers from the Authentication service and inject them into the next request's headers - headers: - userId: X-Auth-UserId - userCountryId: X-Auth-UserCountryId + headers: + userId: X-Auth-UserId + userCountryId: X-Auth-UserCountryId # In case you want to get headers from the Authentication service and inject them to the next request's params - params: - userCountryId: countryId + params: + userCountryId: countryId + # The server will return 404 + - name: api-forbidden-paths + type: access + ## Forbidden paths + paths: + - /swagger-ui/* + - /v2/swagger-ui/* + - /api-docs/* + - /internal/* + - /actuator/* ``` ## Requirement diff --git a/goma.yml b/goma.yml index 7abc163..e158515 100644 --- a/goma.yml +++ b/goma.yml @@ -66,46 +66,12 @@ gateway: Access-Control-Allow-Headers: 'Origin, Authorization, Accept, Content-Type, Access-Control-Allow-Headers, X-Client-Id, X-Session-Id' Access-Control-Allow-Credentials: 'true' Access-Control-Max-Age: 1728000 - #### Define route blocklist paths - blocklist: - - /swagger-ui/* - - /v2/swagger-ui/* - - /api-docs/* - - /internal/* - - /actuator/* ##### Define route middlewares from middlewares names ## The name must be unique ## List of middleware name middlewares: - # path to protect - - path: /user - # Rules defines which specific middleware applies to a route path - rules: - - basic-auth - # path to protect - - path: /path-example - # Rules defines which specific middleware applies to a route path - rules: - - jwt - # path to protect - - path: /admin - # Rules defines which specific middleware applies to a route path - rules: - - basic-auth - # path to protect - - path: /path-example - # Rules defines which specific middleware applies to a route path - rules: - - jwt - - path: /history - http: - url: http://security-service:8080/security/authUser - headers: - #Key from backend authentication header, and inject to the request with custom key name - userId: X-Auth-UserId - userCountryId: X-Auth-UserCountryId - params: - userCountryId: X-countryId + - api-forbidden-paths + - basic-auth # Example of a route | 2 - name: Authentication service path: /auth @@ -113,8 +79,8 @@ gateway: destination: 'http://security-service:8080' healthCheck: /internal/health/ready cors: {} - blocklist: [] - middlewares: [] + middlewares: + - api-forbidden-paths # Example of a route | 3 - name: Basic auth path: /protected @@ -122,15 +88,19 @@ gateway: destination: 'http://notification-service:8080' healthCheck: cors: {} - blocklist: [] middlewares: [] #Defines proxy middlewares +# middleware name must be unique middlewares: # Enable Basic auth authorization based - name: basic-auth # Authentication types | jwt, basic, OAuth type: basic + paths: + - /user + - /admin + - /account rule: username: admin password: admin @@ -139,6 +109,10 @@ middlewares: # Authentication types | jwt, basic, OAuth # jwt authorization based on the result of backend's response and continue the request when the client is authorized type: jwt + # Paths to protect + paths: + - /protected-access + - /example-of-jwt rule: # This is an example URL url: https://www.googleapis.com/auth/userinfo.email @@ -155,9 +129,19 @@ middlewares: # Add header to the next request from AuthRequest header, depending on your requirements # Key is AuthRequest's response header Key, and value is Request's header Key # In case you want to get headers from the Authentication service and inject them into the next request's headers - headers: + headers: userId: X-Auth-UserId userCountryId: X-Auth-UserCountryId # In case you want to get headers from the Authentication service and inject them to the next request's params - params: - userCountryId: countryId \ No newline at end of file + params: + userCountryId: countryId +# The server will return 404 + - name: api-forbidden-paths + type: access + ## Forbidden paths + paths: + - /swagger-ui/* + - /v2/swagger-ui/* + - /api-docs/* + - /internal/* + - /actuator/* \ No newline at end of file diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 26abe18..04d4426 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -77,9 +77,9 @@ func Debug(msg string, args ...interface{}) { log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout"))) formattedMessage := fmt.Sprintf(msg, args...) if len(args) == 0 { - log.Printf("DUBUG: %s\n", msg) + log.Printf("DEBUG: %s\n", msg) } else { - log.Printf("DUBUG: %s\n", formattedMessage) + log.Printf("DEBUG: %s\n", formattedMessage) } } func getStd(out string) *os.File { diff --git a/pkg/config.go b/pkg/config.go index 17e0cfa..6885149 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -103,12 +103,6 @@ type Middleware struct { type MiddlewareName struct { name string `yaml:"name"` } -type RouteMiddleware struct { - //Path contains the path to protect - Path string `yaml:"path"` - //Rules defines which specific middleware applies to a route path - Rules []string `yaml:"rules"` -} // Route defines gateway route type Route struct { diff --git a/pkg/middleware/access-middleware.go b/pkg/middleware/access-middleware.go index decd703..500f201 100644 --- a/pkg/middleware/access-middleware.go +++ b/pkg/middleware/access-middleware.go @@ -30,7 +30,7 @@ func (blockList AccessListMiddleware) AccessMiddleware(next http.Handler) http.H return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for _, block := range blockList.List { if isPathBlocked(r.URL.Path, util.ParseURLPath(blockList.Path+block)) { - logger.Warn("%s: access to %s is forbidden", getRealIP(r), r.URL.Path) + logger.Debug("%s: access to %s is forbidden", getRealIP(r), r.URL.Path) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) err := json.NewEncoder(w).Encode(ProxyResponseError{ diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 04433fe..e30d6cc 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -162,7 +162,7 @@ func (jwtAuth JwtAuth) AuthMiddleware(next http.Handler) http.Handler { client := &http.Client{} authResp, err := client.Do(authReq) if err != nil || authResp.StatusCode != http.StatusOK { - logger.Info("%s %s %s %s", r.Method, r.RemoteAddr, r.URL, r.UserAgent()) + logger.Info("%s %s %s %s", r.Method, getRealIP(r), r.URL, r.UserAgent()) logger.Warn("Proxy authentication error") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) diff --git a/util/constants.go b/util/constants.go index e003539..5a15dff 100644 --- a/util/constants.go +++ b/util/constants.go @@ -30,6 +30,6 @@ func FullVersion() string { return ver } -const MainExample = "Initialize config: init config --output config.yml\n" + +const MainExample = "Initialize config: config init --output config.yml\n" + "Start server: server \n" + "Start server with custom config file: server --config config.yml" From 3ad36e5041e8bed64d2bea763faa13e86ac70764 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Wed, 30 Oct 2024 20:48:58 +0100 Subject: [PATCH 5/5] refactoring of code --- pkg/middleware.go | 11 +++++++++++ pkg/middleware/access-middleware.go | 2 +- pkg/middleware_test.go | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/middleware.go b/pkg/middleware.go index b0a4951..aee826a 100644 --- a/pkg/middleware.go +++ b/pkg/middleware.go @@ -34,3 +34,14 @@ func doesExist(tyName string) bool { } return false } +func GetMiddleware(rule string, middlewares []Middleware) (Middleware, error) { + for _, m := range middlewares { + if strings.Contains(rule, m.Name) { + + return m, nil + } + continue + } + + return Middleware{}, errors.New("no middleware found with name " + rule) +} diff --git a/pkg/middleware/access-middleware.go b/pkg/middleware/access-middleware.go index 500f201..1686a4b 100644 --- a/pkg/middleware/access-middleware.go +++ b/pkg/middleware/access-middleware.go @@ -30,7 +30,7 @@ func (blockList AccessListMiddleware) AccessMiddleware(next http.Handler) http.H return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for _, block := range blockList.List { if isPathBlocked(r.URL.Path, util.ParseURLPath(blockList.Path+block)) { - logger.Debug("%s: access to %s is forbidden", getRealIP(r), r.URL.Path) + logger.Debug("%s: %s access forbidden", getRealIP(r), r.URL.Path) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) err := json.NewEncoder(w).Encode(ProxyResponseError{ diff --git a/pkg/middleware_test.go b/pkg/middleware_test.go index 7e8161c..cb464c1 100644 --- a/pkg/middleware_test.go +++ b/pkg/middleware_test.go @@ -102,7 +102,7 @@ func TestReadMiddleware(t *testing.T) { func TestFoundMiddleware(t *testing.T) { middlewares := getMiddlewares(t) - middleware, err := getAuthMiddleware("jwt", middlewares) + middleware, err := GetMiddleware("jwt", middlewares) if err != nil { t.Errorf("Error getting middleware %v", err) }