2024-10-27 06:10:27 +01:00
|
|
|
package pkg
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Copyright 2024 Jonas Kaninda
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
import (
|
|
|
|
|
"github.com/gorilla/mux"
|
2024-11-04 08:48:38 +01:00
|
|
|
"github.com/jkaninda/goma-gateway/internal/middleware"
|
|
|
|
|
"github.com/jkaninda/goma-gateway/pkg/logger"
|
2024-10-27 06:10:27 +01:00
|
|
|
"github.com/jkaninda/goma-gateway/util"
|
2024-11-10 17:06:58 +01:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
2024-10-27 06:10:27 +01:00
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2024-11-10 17:06:58 +01:00
|
|
|
func init() {
|
|
|
|
|
_ = prometheus.Register(totalRequests)
|
|
|
|
|
_ = prometheus.Register(responseStatus)
|
|
|
|
|
_ = prometheus.Register(httpDuration)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 06:52:41 +01:00
|
|
|
// Initialize the routes
|
2024-10-27 06:10:27 +01:00
|
|
|
func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|
|
|
|
gateway := gatewayServer.gateway
|
|
|
|
|
middlewares := gatewayServer.middlewares
|
2024-11-14 11:38:36 +01:00
|
|
|
redisBased := false
|
|
|
|
|
if len(gateway.Redis.Addr) != 0 {
|
|
|
|
|
redisBased = true
|
|
|
|
|
}
|
2024-11-12 12:38:34 +01:00
|
|
|
//Routes background healthcheck
|
|
|
|
|
routesHealthCheck(gateway.Routes)
|
2024-10-27 06:10:27 +01:00
|
|
|
r := mux.NewRouter()
|
|
|
|
|
heath := HealthCheckRoute{
|
|
|
|
|
DisableRouteHealthCheckError: gateway.DisableRouteHealthCheckError,
|
|
|
|
|
Routes: gateway.Routes,
|
|
|
|
|
}
|
2024-11-10 17:06:58 +01:00
|
|
|
if gateway.EnableMetrics {
|
|
|
|
|
// Prometheus endpoint
|
|
|
|
|
r.Path("/metrics").Handler(promhttp.Handler())
|
|
|
|
|
}
|
2024-11-04 06:52:41 +01:00
|
|
|
// Routes health check
|
|
|
|
|
if !gateway.DisableHealthCheckStatus {
|
|
|
|
|
r.HandleFunc("/healthz", heath.HealthCheckHandler).Methods("GET")
|
2024-11-05 20:11:24 +01:00
|
|
|
r.HandleFunc("/health/routes", heath.HealthCheckHandler).Methods("GET")
|
2024-11-04 06:52:41 +01:00
|
|
|
}
|
2024-11-10 17:06:58 +01:00
|
|
|
|
2024-11-05 20:11:24 +01:00
|
|
|
// Health check
|
|
|
|
|
r.HandleFunc("/health/live", heath.HealthReadyHandler).Methods("GET")
|
2024-10-27 07:24:50 +01:00
|
|
|
r.HandleFunc("/readyz", heath.HealthReadyHandler).Methods("GET")
|
2024-11-06 09:17:21 +01:00
|
|
|
// Enable common exploits
|
|
|
|
|
if gateway.BlockCommonExploits {
|
|
|
|
|
logger.Info("Block common exploits enabled")
|
2024-11-14 00:26:21 +01:00
|
|
|
blockCommon := middleware.BlockCommon{}
|
|
|
|
|
r.Use(blockCommon.BlockExploitsMiddleware)
|
2024-11-06 09:17:21 +01:00
|
|
|
}
|
2024-11-14 11:38:36 +01:00
|
|
|
if gateway.RateLimit > 0 {
|
2024-10-27 06:10:27 +01:00
|
|
|
// Add rate limit middleware to all routes, if defined
|
2024-11-14 13:17:28 +01:00
|
|
|
rateLimit := middleware.RateLimit{
|
|
|
|
|
Id: 1,
|
|
|
|
|
Requests: gateway.RateLimit,
|
|
|
|
|
Window: time.Minute, // requests per minute
|
|
|
|
|
Origins: gateway.Cors.Origins,
|
|
|
|
|
Hosts: []string{},
|
|
|
|
|
RedisBased: redisBased,
|
|
|
|
|
}
|
|
|
|
|
limiter := rateLimit.NewRateLimiterWindow()
|
|
|
|
|
// Add rate limit middleware
|
2024-10-27 06:10:27 +01:00
|
|
|
r.Use(limiter.RateLimitMiddleware())
|
|
|
|
|
}
|
2024-11-14 13:17:28 +01:00
|
|
|
for rIndex, route := range gateway.Routes {
|
2024-10-27 07:24:50 +01:00
|
|
|
if route.Path != "" {
|
2024-11-09 10:59:17 +01:00
|
|
|
if route.Destination == "" && len(route.Backends) == 0 {
|
|
|
|
|
logger.Fatal("Route %s : destination or backends should not be empty", route.Name)
|
2024-10-30 16:38:09 +01:00
|
|
|
|
2024-11-09 10:59:17 +01:00
|
|
|
}
|
2024-10-30 16:38:09 +01:00
|
|
|
// Apply middlewares to route
|
2024-10-27 07:24:50 +01:00
|
|
|
for _, mid := range route.Middlewares {
|
2024-10-30 16:38:09 +01:00
|
|
|
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 {
|
|
|
|
|
// Apply access middleware
|
|
|
|
|
if accessMiddleware.Type == AccessMiddleware {
|
2024-10-30 16:45:13 +01:00
|
|
|
blM := middleware.AccessListMiddleware{
|
2024-10-30 16:38:09 +01:00
|
|
|
Path: route.Path,
|
|
|
|
|
List: accessMiddleware.Paths,
|
|
|
|
|
}
|
2024-10-30 16:45:13 +01:00
|
|
|
r.Use(blM.AccessMiddleware)
|
2024-10-30 16:38:09 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-27 06:10:27 +01:00
|
|
|
}
|
2024-10-30 16:38:09 +01:00
|
|
|
// Get route authentication middleware if it does exist
|
|
|
|
|
rMiddleware, err := getMiddleware([]string{mid}, middlewares)
|
2024-10-27 06:10:27 +01:00
|
|
|
if err != nil {
|
2024-10-30 16:38:09 +01:00
|
|
|
//Error: middleware not found
|
2024-10-28 03:41:29 +01:00
|
|
|
logger.Error("Error: %v", err.Error())
|
2024-10-27 06:10:27 +01:00
|
|
|
} else {
|
2024-10-30 16:38:09 +01:00
|
|
|
for _, midPath := range rMiddleware.Paths {
|
|
|
|
|
proxyRoute := ProxyRoute{
|
2024-11-12 17:38:55 +01:00
|
|
|
path: route.Path,
|
|
|
|
|
rewrite: route.Rewrite,
|
|
|
|
|
destination: route.Destination,
|
|
|
|
|
backends: route.Backends,
|
|
|
|
|
disableHostFording: route.DisableHostFording,
|
|
|
|
|
methods: route.Methods,
|
|
|
|
|
cors: route.Cors,
|
2024-10-27 07:24:50 +01:00
|
|
|
}
|
2024-11-02 11:55:37 +01:00
|
|
|
secureRouter := r.PathPrefix(util.ParseRoutePath(route.Path, midPath)).Subrouter()
|
2024-11-07 09:45:09 +01:00
|
|
|
//callBackRouter := r.PathPrefix(util.ParseRoutePath(route.Path, "/callback")).Subrouter()
|
2024-10-30 16:38:09 +01:00
|
|
|
//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,
|
2024-11-05 20:44:06 +01:00
|
|
|
Origins: gateway.Cors.Origins,
|
2024-10-30 16:38:09 +01:00
|
|
|
}
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
}
|
2024-11-07 09:45:09 +01:00
|
|
|
case OAuth, "openid":
|
|
|
|
|
oauth, err := oAuthMiddleware(rMiddleware.Rule)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("Error: %s", err.Error())
|
|
|
|
|
} else {
|
2024-11-08 12:03:52 +01:00
|
|
|
redirectURL := "/callback" + route.Path
|
|
|
|
|
if oauth.RedirectURL != "" {
|
|
|
|
|
redirectURL = oauth.RedirectURL
|
|
|
|
|
}
|
2024-11-07 09:45:09 +01:00
|
|
|
amw := middleware.Oauth{
|
|
|
|
|
ClientID: oauth.ClientID,
|
|
|
|
|
ClientSecret: oauth.ClientSecret,
|
2024-11-08 12:03:52 +01:00
|
|
|
RedirectURL: redirectURL,
|
2024-11-07 09:45:09 +01:00
|
|
|
Scopes: oauth.Scopes,
|
|
|
|
|
Endpoint: middleware.OauthEndpoint{
|
2024-11-08 12:03:52 +01:00
|
|
|
AuthURL: oauth.Endpoint.AuthURL,
|
|
|
|
|
TokenURL: oauth.Endpoint.TokenURL,
|
|
|
|
|
UserInfoURL: oauth.Endpoint.UserInfoURL,
|
2024-11-07 09:45:09 +01:00
|
|
|
},
|
2024-11-08 12:03:52 +01:00
|
|
|
State: oauth.State,
|
|
|
|
|
Origins: gateway.Cors.Origins,
|
|
|
|
|
JWTSecret: oauth.JWTSecret,
|
|
|
|
|
Provider: oauth.Provider,
|
2024-11-07 09:45:09 +01:00
|
|
|
}
|
|
|
|
|
oauthRuler := oauthRulerMiddleware(amw)
|
|
|
|
|
// Check if a cookie path is defined
|
|
|
|
|
if oauthRuler.CookiePath == "" {
|
|
|
|
|
oauthRuler.CookiePath = route.Path
|
|
|
|
|
}
|
|
|
|
|
// Check if a RedirectPath is defined
|
|
|
|
|
if oauthRuler.RedirectPath == "" {
|
|
|
|
|
oauthRuler.RedirectPath = util.ParseRoutePath(route.Path, midPath)
|
|
|
|
|
}
|
2024-11-08 12:03:52 +01:00
|
|
|
if oauthRuler.Provider == "" {
|
|
|
|
|
oauthRuler.Provider = "custom"
|
|
|
|
|
}
|
2024-11-07 09:45:09 +01:00
|
|
|
secureRouter.Use(amw.AuthMiddleware)
|
|
|
|
|
secureRouter.Use(CORSHandler(route.Cors))
|
|
|
|
|
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
|
|
|
|
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
|
|
|
|
// Callback route
|
2024-11-08 12:03:52 +01:00
|
|
|
r.HandleFunc(util.UrlParsePath(redirectURL), oauthRuler.callbackHandler).Methods("GET")
|
2024-11-07 09:45:09 +01:00
|
|
|
}
|
2024-10-30 16:38:09 +01:00
|
|
|
default:
|
|
|
|
|
if !doesExist(rMiddleware.Type) {
|
|
|
|
|
logger.Error("Unknown middleware type %s", rMiddleware.Type)
|
2024-10-27 07:24:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-27 06:10:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2024-10-27 07:24:50 +01:00
|
|
|
} else {
|
|
|
|
|
logger.Error("Error, middleware path is empty")
|
|
|
|
|
logger.Error("Middleware ignored")
|
2024-10-27 06:10:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-10-27 07:24:50 +01:00
|
|
|
proxyRoute := ProxyRoute{
|
2024-11-12 17:38:55 +01:00
|
|
|
path: route.Path,
|
|
|
|
|
rewrite: route.Rewrite,
|
|
|
|
|
destination: route.Destination,
|
|
|
|
|
backends: route.Backends,
|
|
|
|
|
methods: route.Methods,
|
|
|
|
|
disableHostFording: route.DisableHostFording,
|
|
|
|
|
cors: route.Cors,
|
2024-10-27 07:24:50 +01:00
|
|
|
}
|
2024-11-10 14:52:31 +01:00
|
|
|
// create route
|
2024-10-27 07:24:50 +01:00
|
|
|
router := r.PathPrefix(route.Path).Subrouter()
|
2024-11-10 14:52:31 +01:00
|
|
|
// Apply common exploits to the route
|
|
|
|
|
// Enable common exploits
|
|
|
|
|
if route.BlockCommonExploits {
|
2024-11-14 00:26:21 +01:00
|
|
|
blockCommon := middleware.BlockCommon{
|
|
|
|
|
ErrorInterceptor: route.ErrorInterceptor,
|
|
|
|
|
}
|
2024-11-10 14:52:31 +01:00
|
|
|
logger.Info("Block common exploits enabled")
|
2024-11-14 00:26:21 +01:00
|
|
|
router.Use(blockCommon.BlockExploitsMiddleware)
|
2024-11-10 14:52:31 +01:00
|
|
|
}
|
|
|
|
|
// Apply route rate limit
|
|
|
|
|
if route.RateLimit > 0 {
|
2024-11-14 13:17:28 +01:00
|
|
|
rateLimit := middleware.RateLimit{
|
|
|
|
|
Id: rIndex,
|
|
|
|
|
Requests: route.RateLimit,
|
|
|
|
|
Window: time.Minute, // requests per minute
|
|
|
|
|
Origins: route.Cors.Origins,
|
|
|
|
|
Hosts: route.Hosts,
|
|
|
|
|
RedisBased: redisBased,
|
|
|
|
|
}
|
|
|
|
|
limiter := rateLimit.NewRateLimiterWindow()
|
|
|
|
|
// Add rate limit middleware
|
2024-11-10 14:52:31 +01:00
|
|
|
router.Use(limiter.RateLimitMiddleware())
|
|
|
|
|
}
|
2024-10-28 10:10:53 +01:00
|
|
|
// Apply route Cors
|
2024-10-27 07:24:50 +01:00
|
|
|
router.Use(CORSHandler(route.Cors))
|
2024-11-10 07:56:46 +01:00
|
|
|
if len(route.Hosts) > 0 {
|
|
|
|
|
for _, host := range route.Hosts {
|
|
|
|
|
router.Host(host).PathPrefix("").Handler(proxyRoute.ProxyHandler())
|
|
|
|
|
}
|
2024-10-28 04:10:24 +01:00
|
|
|
} else {
|
|
|
|
|
router.PathPrefix("").Handler(proxyRoute.ProxyHandler())
|
|
|
|
|
}
|
2024-11-11 08:50:34 +01:00
|
|
|
if gateway.EnableMetrics {
|
|
|
|
|
pr := PrometheusRoute{
|
|
|
|
|
name: route.Name,
|
|
|
|
|
path: route.Path,
|
|
|
|
|
}
|
|
|
|
|
// Prometheus endpoint
|
|
|
|
|
router.Use(pr.prometheusMiddleware)
|
|
|
|
|
}
|
2024-11-14 00:26:21 +01:00
|
|
|
// Apply route Error interceptor middleware
|
2024-11-14 13:17:28 +01:00
|
|
|
interceptErrors := middleware.InterceptErrors{
|
|
|
|
|
Origins: gateway.Cors.Origins,
|
2024-11-14 00:26:21 +01:00
|
|
|
}
|
2024-11-14 13:17:28 +01:00
|
|
|
router.Use(interceptErrors.ErrorInterceptor)
|
2024-10-27 07:24:50 +01:00
|
|
|
} else {
|
|
|
|
|
logger.Error("Error, path is empty in route %s", route.Name)
|
2024-11-10 07:56:46 +01:00
|
|
|
logger.Error("Route path ignored: %s", route.Path)
|
2024-10-27 07:24:50 +01:00
|
|
|
}
|
2024-10-27 06:10:27 +01:00
|
|
|
}
|
2024-10-28 10:10:53 +01:00
|
|
|
// Apply global Cors middlewares
|
|
|
|
|
r.Use(CORSHandler(gateway.Cors)) // Apply CORS middleware
|
2024-10-29 09:39:31 +01:00
|
|
|
// Apply errorInterceptor middleware
|
|
|
|
|
interceptErrors := middleware.InterceptErrors{
|
2024-11-05 20:44:06 +01:00
|
|
|
Errors: gateway.InterceptErrors,
|
|
|
|
|
Origins: gateway.Cors.Origins,
|
2024-10-29 09:39:31 +01:00
|
|
|
}
|
|
|
|
|
r.Use(interceptErrors.ErrorInterceptor)
|
2024-10-27 06:10:27 +01:00
|
|
|
return r
|
|
|
|
|
|
|
|
|
|
}
|