refactor: refactoring of code to meet all golangci-lint requirements
This commit is contained in:
44
.golangci.yml
Normal file
44
.golangci.yml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
run:
|
||||||
|
timeout: 5m
|
||||||
|
allow-parallel-runners: true
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# don't skip warning about doc comments
|
||||||
|
# don't exclude the default set of lint
|
||||||
|
exclude-use-default: false
|
||||||
|
# restore some of the defaults
|
||||||
|
# (fill in the rest as needed)
|
||||||
|
exclude-rules:
|
||||||
|
- path: "internal/*"
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
|
- lll
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- dupl
|
||||||
|
- errcheck
|
||||||
|
- copyloopvar
|
||||||
|
- ginkgolinter
|
||||||
|
- goconst
|
||||||
|
- gocyclo
|
||||||
|
- gofmt
|
||||||
|
#- goimports
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- lll
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- prealloc
|
||||||
|
- revive
|
||||||
|
- staticcheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
- name: comment-spacings
|
||||||
@@ -19,9 +19,10 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
pkg "github.com/jkaninda/goma-gateway/internal"
|
pkg "github.com/jkaninda/goma-gateway/internal"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var CheckConfigCmd = &cobra.Command{
|
var CheckConfigCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Cmd = &cobra.Command{
|
var Cmd = &cobra.Command{
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jkaninda/goma-gateway/internal"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
pkg "github.com/jkaninda/goma-gateway/internal"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var InitConfigCmd = &cobra.Command{
|
var InitConfigCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func CheckConfig(fileName string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check middlewares
|
// Check middlewares
|
||||||
for index, mid := range c.Middlewares {
|
for index, mid := range c.Middlewares {
|
||||||
if util.HasWhitespace(mid.Name) {
|
if util.HasWhitespace(mid.Name) {
|
||||||
fmt.Printf("Warning: Middleware contains whitespace: %s | index: [%d], please remove whitespace characters\n", mid.Name, index)
|
fmt.Printf("Warning: Middleware contains whitespace: %s | index: [%d], please remove whitespace characters\n", mid.Name, index)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (GatewayServer) Config(configFile string, ctx context.Context) (*GatewaySer
|
|||||||
|
|
||||||
}
|
}
|
||||||
logger.Info("Generating new configuration file...")
|
logger.Info("Generating new configuration file...")
|
||||||
//check if config directory does exist
|
// check if config directory does exist
|
||||||
if !util.FolderExists(ConfigDir) {
|
if !util.FolderExists(ConfigDir) {
|
||||||
err := os.MkdirAll(ConfigDir, os.ModePerm)
|
err := os.MkdirAll(ConfigDir, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ package pkg
|
|||||||
|
|
||||||
type Cors struct {
|
type Cors struct {
|
||||||
// Cors Allowed origins,
|
// Cors Allowed origins,
|
||||||
//e.g:
|
// e.g:
|
||||||
//
|
//
|
||||||
// - http://localhost:80
|
// - http://localhost:80
|
||||||
//
|
//
|
||||||
// - https://example.com
|
// - https://example.com
|
||||||
Origins []string `yaml:"origins"`
|
Origins []string `yaml:"origins"`
|
||||||
//
|
//
|
||||||
//e.g:
|
// e.g:
|
||||||
//
|
//
|
||||||
//Access-Control-Allow-Origin: '*'
|
// Access-Control-Allow-Origin: '*'
|
||||||
//
|
//
|
||||||
// Access-Control-Allow-Methods: 'GET, POST, PUT, DELETE, OPTIONS'
|
// Access-Control-Allow-Methods: 'GET, POST, PUT, DELETE, OPTIONS'
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ type Gateway struct {
|
|||||||
DisableHealthCheckStatus bool `yaml:"disableHealthCheckStatus"`
|
DisableHealthCheckStatus bool `yaml:"disableHealthCheckStatus"`
|
||||||
// DisableRouteHealthCheckError allows enabling and disabling backend healthcheck errors
|
// DisableRouteHealthCheckError allows enabling and disabling backend healthcheck errors
|
||||||
DisableRouteHealthCheckError bool `yaml:"disableRouteHealthCheckError"`
|
DisableRouteHealthCheckError bool `yaml:"disableRouteHealthCheckError"`
|
||||||
//Disable allows enabling and disabling displaying routes on start
|
// Disable allows enabling and disabling displaying routes on start
|
||||||
DisableDisplayRouteOnStart bool `yaml:"disableDisplayRouteOnStart"`
|
DisableDisplayRouteOnStart bool `yaml:"disableDisplayRouteOnStart"`
|
||||||
// DisableKeepAlive allows enabling and disabling KeepALive server
|
// DisableKeepAlive allows enabling and disabling KeepALive server
|
||||||
DisableKeepAlive bool `yaml:"disableKeepAlive"`
|
DisableKeepAlive bool `yaml:"disableKeepAlive"`
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ func CORSHandler(cors Cors) mux.MiddlewareFunc {
|
|||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Set CORS headers from the cors config
|
// Set CORS headers from the cors config
|
||||||
//Update Cors Headers
|
// Update Cors Headers
|
||||||
for k, v := range cors.Headers {
|
for k, v := range cors.Headers {
|
||||||
w.Header().Set(k, v)
|
w.Header().Set(k, v)
|
||||||
}
|
}
|
||||||
//Update Origin Cors Headers
|
// Update Origin Cors Headers
|
||||||
if allowedOrigin(cors.Origins, r.Header.Get("Origin")) {
|
if allowedOrigin(cors.Origins, r.Header.Get("Origin")) {
|
||||||
// Handle preflight requests (OPTIONS)
|
// Handle preflight requests (OPTIONS)
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
@@ -90,7 +90,7 @@ func (heathRoute HealthCheckRoute) HealthCheckHandler(w http.ResponseWriter, r *
|
|||||||
}
|
}
|
||||||
wg.Wait() // Wait for all requests to complete
|
wg.Wait() // Wait for all requests to complete
|
||||||
response := HealthCheckResponse{
|
response := HealthCheckResponse{
|
||||||
Status: "healthy", //Goma proxy
|
Status: "healthy", // Goma proxy
|
||||||
Routes: routes, // Routes health check
|
Routes: routes, // Routes health check
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (oauth *OauthRulerMiddleware) getUserInfo(token *oauth2.Token) (UserInfo, e
|
|||||||
defer func(Body io.ReadCloser) {
|
defer func(Body io.ReadCloser) {
|
||||||
err := Body.Close()
|
err := Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}(resp.Body)
|
}(resp.Body)
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,12 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PrometheusRoute struct {
|
type PrometheusRoute struct {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package pkg
|
|||||||
|
|
||||||
// Middleware defined the route middlewares
|
// Middleware defined the route middlewares
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
//Path contains the name of middlewares and must be unique
|
// Path contains the name of middlewares and must be unique
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
// Type contains authentication types
|
// Type contains authentication types
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (jwtAuth JwtAuth) AuthMiddleware(next http.Handler) http.Handler {
|
|||||||
if r.Header.Get(header) == "" {
|
if r.Header.Get(header) == "" {
|
||||||
logger.Error("Proxy error, missing %s header", header)
|
logger.Error("Proxy error, missing %s header", header)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
//check allowed origin
|
// check allowed origin
|
||||||
if allowedOrigin(jwtAuth.Origins, r.Header.Get("Origin")) {
|
if allowedOrigin(jwtAuth.Origins, r.Header.Get("Origin")) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,6 @@ func (jwtAuth JwtAuth) AuthMiddleware(next http.Handler) http.Handler {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//token := r.Header.Get("Authorization")
|
|
||||||
authURL, err := url.Parse(jwtAuth.AuthURL)
|
authURL, err := url.Parse(jwtAuth.AuthURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error parsing auth URL: %v", err)
|
logger.Error("Error parsing auth URL: %v", err)
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ func (rl *TokenRateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
|
|||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !rl.Allow() {
|
if !rl.Allow() {
|
||||||
logger.Error("Too many requests from IP: %s %s %s", getRealIP(r), r.URL, r.UserAgent())
|
logger.Error("Too many requests from IP: %s %s %s", getRealIP(r), r.URL, r.UserAgent())
|
||||||
//RespondWithError(w, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), basicAuth.ErrorInterceptor)
|
|
||||||
|
|
||||||
// Rate limit exceeded, return a 429 Too Many Requests response
|
// Rate limit exceeded, return a 429 Too Many Requests response
|
||||||
w.WriteHeader(http.StatusTooManyRequests)
|
w.WriteHeader(http.StatusTooManyRequests)
|
||||||
_, err := w.Write([]byte(fmt.Sprintf("%d Too many requests, API requests limit exceeded. Please try again later", http.StatusTooManyRequests)))
|
_, err := w.Write([]byte(fmt.Sprintf("%d Too many requests, API requests limit exceeded. Please try again later", http.StatusTooManyRequests)))
|
||||||
@@ -75,7 +73,7 @@ func (rl *RateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
|
|||||||
|
|
||||||
if client.RequestCount > rl.requests {
|
if client.RequestCount > rl.requests {
|
||||||
logger.Error("Too many requests from IP: %s %s %s", clientIP, r.URL, r.UserAgent())
|
logger.Error("Too many requests from IP: %s %s %s", clientIP, r.URL, r.UserAgent())
|
||||||
//Update Origin Cors Headers
|
// Update Origin Cors Headers
|
||||||
if allowedOrigin(rl.origins, r.Header.Get("Origin")) {
|
if allowedOrigin(rl.origins, r.Header.Get("Origin")) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ type RateLimiter struct {
|
|||||||
clientMap map[string]*Client
|
clientMap map[string]*Client
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
origins []string
|
origins []string
|
||||||
//hosts []string
|
|
||||||
redisBased bool
|
redisBased bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (proxyRoute ProxyRoute) ProxyHandler() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set CORS headers from the cors config
|
// Set CORS headers from the cors config
|
||||||
//Update Cors Headers
|
// Update Cors Headers
|
||||||
for k, v := range proxyRoute.cors.Headers {
|
for k, v := range proxyRoute.cors.Headers {
|
||||||
w.Header().Set(k, v)
|
w.Header().Set(k, v)
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ func (proxyRoute ProxyRoute) ProxyHandler() http.HandlerFunc {
|
|||||||
InsecureSkipVerify: proxyRoute.insecureSkipVerify,
|
InsecureSkipVerify: proxyRoute.insecureSkipVerify,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
w.Header().Set("Proxied-By", gatewayName) //Set Server name
|
w.Header().Set("Proxied-By", gatewayName) // Set Server name
|
||||||
w.Header().Del("Server") // Remove the Server header
|
w.Header().Del("Server") // Remove the Server header
|
||||||
// Custom error handler for proxy errors
|
// Custom error handler for proxy errors
|
||||||
proxy.ErrorHandler = ProxyErrorHandler
|
proxy.ErrorHandler = ProxyErrorHandler
|
||||||
|
|||||||
@@ -22,13 +22,12 @@ import (
|
|||||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
"github.com/jkaninda/goma-gateway/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gatewayServer GatewayServer) initRedis() error {
|
func (gatewayServer GatewayServer) initRedis() {
|
||||||
if gatewayServer.gateway.Redis.Addr == "" {
|
if len(gatewayServer.gateway.Redis.Addr) != 0 {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
logger.Info("Initializing Redis...")
|
logger.Info("Initializing Redis...")
|
||||||
middlewares.InitRedis(gatewayServer.gateway.Redis.Addr, gatewayServer.gateway.Redis.Password)
|
middlewares.InitRedis(gatewayServer.gateway.Redis.Addr, gatewayServer.gateway.Redis.Password)
|
||||||
return nil
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gatewayServer GatewayServer) closeRedis() {
|
func (gatewayServer GatewayServer) closeRedis() {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func init() {
|
|||||||
_ = prometheus.Register(metrics.HttpDuration)
|
_ = prometheus.Register(metrics.HttpDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the routes
|
// Initialize initializes the routes
|
||||||
func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
||||||
gateway := gatewayServer.gateway
|
gateway := gatewayServer.gateway
|
||||||
m := gatewayServer.middlewares
|
m := gatewayServer.middlewares
|
||||||
@@ -40,8 +40,9 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
if len(gateway.Redis.Addr) != 0 {
|
if len(gateway.Redis.Addr) != 0 {
|
||||||
redisBased = true
|
redisBased = true
|
||||||
}
|
}
|
||||||
//Routes background healthcheck
|
// Routes background healthcheck
|
||||||
routesHealthCheck(gateway.Routes)
|
routesHealthCheck(gateway.Routes)
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
heath := HealthCheckRoute{
|
heath := HealthCheckRoute{
|
||||||
DisableRouteHealthCheckError: gateway.DisableRouteHealthCheckError,
|
DisableRouteHealthCheckError: gateway.DisableRouteHealthCheckError,
|
||||||
@@ -68,7 +69,7 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
if gateway.RateLimit != 0 {
|
if gateway.RateLimit != 0 {
|
||||||
// Add rate limit middlewares to all routes, if defined
|
// Add rate limit middlewares to all routes, if defined
|
||||||
rateLimit := middlewares.RateLimit{
|
rateLimit := middlewares.RateLimit{
|
||||||
Id: "global_rate", //Generate a unique ID for routes
|
Id: "global_rate", // Generate a unique ID for routes
|
||||||
Requests: gateway.RateLimit,
|
Requests: gateway.RateLimit,
|
||||||
Window: time.Minute, // requests per minute
|
Window: time.Minute, // requests per minute
|
||||||
Origins: gateway.Cors.Origins,
|
Origins: gateway.Cors.Origins,
|
||||||
@@ -80,16 +81,17 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
r.Use(limiter.RateLimitMiddleware())
|
r.Use(limiter.RateLimitMiddleware())
|
||||||
}
|
}
|
||||||
for rIndex, route := range gateway.Routes {
|
for rIndex, route := range gateway.Routes {
|
||||||
if route.Path != "" {
|
if len(route.Path) != 0 {
|
||||||
if route.Destination == "" && len(route.Backends) == 0 {
|
// Checks if route destination and backend are empty
|
||||||
|
if len(route.Destination) == 0 && len(route.Backends) == 0 {
|
||||||
logger.Fatal("Route %s : destination or backends should not be empty", route.Name)
|
logger.Fatal("Route %s : destination or backends should not be empty", route.Name)
|
||||||
|
|
||||||
}
|
}
|
||||||
// Apply middlewares to route
|
// Apply middlewares to the route
|
||||||
for _, mid := range route.Middlewares {
|
for _, middleware := range route.Middlewares {
|
||||||
if mid != "" {
|
if middleware != "" {
|
||||||
// Get Access middlewares if it does exist
|
// Get Access middlewares if it does exist
|
||||||
accessMiddleware, err := getMiddleware([]string{mid}, m)
|
accessMiddleware, err := getMiddleware([]string{middleware}, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error: %v", err.Error())
|
logger.Error("Error: %v", err.Error())
|
||||||
} else {
|
} else {
|
||||||
@@ -105,114 +107,12 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
|
|
||||||
}
|
}
|
||||||
// Get route authentication middlewares if it does exist
|
// Get route authentication middlewares if it does exist
|
||||||
rMiddleware, err := getMiddleware([]string{mid}, m)
|
routeMiddleware, err := getMiddleware([]string{middleware}, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Error: middlewares not found
|
// Error: middlewares not found
|
||||||
logger.Error("Error: %v", err.Error())
|
logger.Error("Error: %v", err.Error())
|
||||||
} else {
|
} else {
|
||||||
for _, midPath := range rMiddleware.Paths {
|
attachAuthMiddlewares(route, routeMiddleware, gateway, r)
|
||||||
proxyRoute := ProxyRoute{
|
|
||||||
path: route.Path,
|
|
||||||
rewrite: route.Rewrite,
|
|
||||||
destination: route.Destination,
|
|
||||||
backends: route.Backends,
|
|
||||||
disableHostFording: route.DisableHostFording,
|
|
||||||
methods: route.Methods,
|
|
||||||
cors: route.Cors,
|
|
||||||
insecureSkipVerify: route.InsecureSkipVerify,
|
|
||||||
}
|
|
||||||
secureRouter := r.PathPrefix(util.ParseRoutePath(route.Path, midPath)).Subrouter()
|
|
||||||
//callBackRouter := r.PathPrefix(util.ParseRoutePath(route.Path, "/callback")).Subrouter()
|
|
||||||
//Check Authentication middlewares
|
|
||||||
switch rMiddleware.Type {
|
|
||||||
case BasicAuth:
|
|
||||||
basicAuth, err := getBasicAuthMiddleware(rMiddleware.Rule)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Error: %s", err.Error())
|
|
||||||
} else {
|
|
||||||
amw := middlewares.AuthBasic{
|
|
||||||
Username: basicAuth.Username,
|
|
||||||
Password: basicAuth.Password,
|
|
||||||
Headers: nil,
|
|
||||||
Params: nil,
|
|
||||||
}
|
|
||||||
// Apply JWT authentication middlewares
|
|
||||||
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 := middlewares.JwtAuth{
|
|
||||||
AuthURL: jwt.URL,
|
|
||||||
RequiredHeaders: jwt.RequiredHeaders,
|
|
||||||
Headers: jwt.Headers,
|
|
||||||
Params: jwt.Params,
|
|
||||||
Origins: gateway.Cors.Origins,
|
|
||||||
}
|
|
||||||
// Apply JWT authentication middlewares
|
|
||||||
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, "openid":
|
|
||||||
oauth, err := oAuthMiddleware(rMiddleware.Rule)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Error: %s", err.Error())
|
|
||||||
} else {
|
|
||||||
redirectURL := "/callback" + route.Path
|
|
||||||
if oauth.RedirectURL != "" {
|
|
||||||
redirectURL = oauth.RedirectURL
|
|
||||||
}
|
|
||||||
amw := middlewares.Oauth{
|
|
||||||
ClientID: oauth.ClientID,
|
|
||||||
ClientSecret: oauth.ClientSecret,
|
|
||||||
RedirectURL: redirectURL,
|
|
||||||
Scopes: oauth.Scopes,
|
|
||||||
Endpoint: middlewares.OauthEndpoint{
|
|
||||||
AuthURL: oauth.Endpoint.AuthURL,
|
|
||||||
TokenURL: oauth.Endpoint.TokenURL,
|
|
||||||
UserInfoURL: oauth.Endpoint.UserInfoURL,
|
|
||||||
},
|
|
||||||
State: oauth.State,
|
|
||||||
Origins: gateway.Cors.Origins,
|
|
||||||
JWTSecret: oauth.JWTSecret,
|
|
||||||
Provider: oauth.Provider,
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
if oauthRuler.Provider == "" {
|
|
||||||
oauthRuler.Provider = "custom"
|
|
||||||
}
|
|
||||||
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
|
|
||||||
r.HandleFunc(util.UrlParsePath(redirectURL), oauthRuler.callbackHandler).Methods("GET")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if !doesExist(rMiddleware.Type) {
|
|
||||||
logger.Error("Unknown middlewares type %s", rMiddleware.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.Error("Error, middlewares path is empty")
|
logger.Error("Error, middlewares path is empty")
|
||||||
@@ -300,3 +200,107 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func attachAuthMiddlewares(route Route, routeMiddleware Middleware, gateway Gateway, r *mux.Router) {
|
||||||
|
for _, middlewarePath := range routeMiddleware.Paths {
|
||||||
|
proxyRoute := ProxyRoute{
|
||||||
|
path: route.Path,
|
||||||
|
rewrite: route.Rewrite,
|
||||||
|
destination: route.Destination,
|
||||||
|
backends: route.Backends,
|
||||||
|
disableHostFording: route.DisableHostFording,
|
||||||
|
methods: route.Methods,
|
||||||
|
cors: route.Cors,
|
||||||
|
insecureSkipVerify: route.InsecureSkipVerify,
|
||||||
|
}
|
||||||
|
secureRouter := r.PathPrefix(util.ParseRoutePath(route.Path, middlewarePath)).Subrouter()
|
||||||
|
// Check Authentication middleware types
|
||||||
|
switch routeMiddleware.Type {
|
||||||
|
case BasicAuth:
|
||||||
|
basicAuth, err := getBasicAuthMiddleware(routeMiddleware.Rule)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error: %s", err.Error())
|
||||||
|
} else {
|
||||||
|
authBasic := middlewares.AuthBasic{
|
||||||
|
Username: basicAuth.Username,
|
||||||
|
Password: basicAuth.Password,
|
||||||
|
Headers: nil,
|
||||||
|
Params: nil,
|
||||||
|
}
|
||||||
|
// Apply JWT authentication middlewares
|
||||||
|
secureRouter.Use(authBasic.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(routeMiddleware.Rule)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error: %s", err.Error())
|
||||||
|
} else {
|
||||||
|
jwtAuth := middlewares.JwtAuth{
|
||||||
|
AuthURL: jwt.URL,
|
||||||
|
RequiredHeaders: jwt.RequiredHeaders,
|
||||||
|
Headers: jwt.Headers,
|
||||||
|
Params: jwt.Params,
|
||||||
|
Origins: gateway.Cors.Origins,
|
||||||
|
}
|
||||||
|
// Apply JWT authentication middlewares
|
||||||
|
secureRouter.Use(jwtAuth.AuthMiddleware)
|
||||||
|
secureRouter.Use(CORSHandler(route.Cors))
|
||||||
|
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||||
|
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||||
|
|
||||||
|
}
|
||||||
|
case OAuth:
|
||||||
|
oauth, err := oAuthMiddleware(routeMiddleware.Rule)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error: %s", err.Error())
|
||||||
|
} else {
|
||||||
|
redirectURL := "/callback" + route.Path
|
||||||
|
if oauth.RedirectURL != "" {
|
||||||
|
redirectURL = oauth.RedirectURL
|
||||||
|
}
|
||||||
|
amw := middlewares.Oauth{
|
||||||
|
ClientID: oauth.ClientID,
|
||||||
|
ClientSecret: oauth.ClientSecret,
|
||||||
|
RedirectURL: redirectURL,
|
||||||
|
Scopes: oauth.Scopes,
|
||||||
|
Endpoint: middlewares.OauthEndpoint{
|
||||||
|
AuthURL: oauth.Endpoint.AuthURL,
|
||||||
|
TokenURL: oauth.Endpoint.TokenURL,
|
||||||
|
UserInfoURL: oauth.Endpoint.UserInfoURL,
|
||||||
|
},
|
||||||
|
State: oauth.State,
|
||||||
|
Origins: gateway.Cors.Origins,
|
||||||
|
JWTSecret: oauth.JWTSecret,
|
||||||
|
Provider: oauth.Provider,
|
||||||
|
}
|
||||||
|
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, middlewarePath)
|
||||||
|
}
|
||||||
|
if oauthRuler.Provider == "" {
|
||||||
|
oauthRuler.Provider = "custom"
|
||||||
|
}
|
||||||
|
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
|
||||||
|
r.HandleFunc(util.UrlParsePath(redirectURL), oauthRuler.callbackHandler).Methods("GET")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if !doesExist(routeMiddleware.Type) {
|
||||||
|
logger.Error("Unknown middlewares type %s", routeMiddleware.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ type Route struct {
|
|||||||
Path string `yaml:"path"`
|
Path string `yaml:"path"`
|
||||||
// Name defines route name
|
// Name defines route name
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
//Host Domain/host based request routing
|
// Host Domain/host based request routing
|
||||||
//Host string `yaml:"host"`
|
// Host string `yaml:"host"`
|
||||||
//Hosts Domains/hosts based request routing
|
// Hosts Domains/hosts based request routing
|
||||||
Hosts []string `yaml:"hosts"`
|
Hosts []string `yaml:"hosts"`
|
||||||
// Rewrite rewrites route path to desired path
|
// Rewrite rewrites route path to desired path
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
"github.com/jkaninda/goma-gateway/pkg/logger"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -33,9 +32,7 @@ func (gatewayServer GatewayServer) Start() error {
|
|||||||
logger.Info("Initializing routes...")
|
logger.Info("Initializing routes...")
|
||||||
route := gatewayServer.Initialize()
|
route := gatewayServer.Initialize()
|
||||||
logger.Debug("Routes count=%d, Middlewares count=%d", len(gatewayServer.gateway.Routes), len(gatewayServer.middlewares))
|
logger.Debug("Routes count=%d, Middlewares count=%d", len(gatewayServer.gateway.Routes), len(gatewayServer.middlewares))
|
||||||
if err := gatewayServer.initRedis(); err != nil {
|
gatewayServer.initRedis()
|
||||||
return fmt.Errorf("failed to initialize Redis: %w", err)
|
|
||||||
}
|
|
||||||
defer gatewayServer.closeRedis()
|
defer gatewayServer.closeRedis()
|
||||||
|
|
||||||
tlsConfig, listenWithTLS, err := gatewayServer.initTLS()
|
tlsConfig, listenWithTLS, err := gatewayServer.initTLS()
|
||||||
@@ -51,9 +48,7 @@ func (gatewayServer GatewayServer) Start() error {
|
|||||||
httpsServer := gatewayServer.createServer(":8443", route, tlsConfig)
|
httpsServer := gatewayServer.createServer(":8443", route, tlsConfig)
|
||||||
|
|
||||||
// Start HTTP/HTTPS servers
|
// Start HTTP/HTTPS servers
|
||||||
if err := gatewayServer.startServers(httpServer, httpsServer, listenWithTLS); err != nil {
|
gatewayServer.startServers(httpServer, httpsServer, listenWithTLS)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle graceful shutdown
|
// Handle graceful shutdown
|
||||||
return gatewayServer.shutdown(httpServer, httpsServer, listenWithTLS)
|
return gatewayServer.shutdown(httpServer, httpsServer, listenWithTLS)
|
||||||
@@ -70,7 +65,7 @@ func (gatewayServer GatewayServer) createServer(addr string, handler http.Handle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gatewayServer GatewayServer) startServers(httpServer, httpsServer *http.Server, listenWithTLS bool) error {
|
func (gatewayServer GatewayServer) startServers(httpServer, httpsServer *http.Server, listenWithTLS bool) {
|
||||||
go func() {
|
go func() {
|
||||||
logger.Info("Starting HTTP server on 0.0.0.0:8080")
|
logger.Info("Starting HTTP server on 0.0.0.0:8080")
|
||||||
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
@@ -86,8 +81,6 @@ func (gatewayServer GatewayServer) startServers(httpServer, httpsServer *http.Se
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gatewayServer GatewayServer) shutdown(httpServer, httpsServer *http.Server, listenWithTLS bool) error {
|
func (gatewayServer GatewayServer) shutdown(httpServer, httpsServer *http.Server, listenWithTLS bool) error {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type JWTRuleMiddleware struct {
|
|||||||
//
|
//
|
||||||
// In case you want to get headers from Authentication service and inject them to next request's params.
|
// In case you want to get headers from Authentication service and inject them to next request's params.
|
||||||
//
|
//
|
||||||
//e.g: Header X-Auth-UserId to query userId
|
// e.g: Header X-Auth-UserId to query userId
|
||||||
Params map[string]string `yaml:"params"`
|
Params map[string]string `yaml:"params"`
|
||||||
}
|
}
|
||||||
type OauthRulerMiddleware struct {
|
type OauthRulerMiddleware struct {
|
||||||
@@ -66,7 +66,7 @@ type OauthRulerMiddleware struct {
|
|||||||
RedirectURL string `yaml:"redirectUrl"`
|
RedirectURL string `yaml:"redirectUrl"`
|
||||||
// RedirectPath is the PATH to redirect users after authentication, e.g: /my-protected-path/dashboard
|
// RedirectPath is the PATH to redirect users after authentication, e.g: /my-protected-path/dashboard
|
||||||
RedirectPath string `yaml:"redirectPath"`
|
RedirectPath string `yaml:"redirectPath"`
|
||||||
//CookiePath e.g: /my-protected-path or / || by default is applied on a route path
|
// CookiePath e.g: /my-protected-path or / || by default is applied on a route path
|
||||||
CookiePath string `yaml:"cookiePath"`
|
CookiePath string `yaml:"cookiePath"`
|
||||||
|
|
||||||
// Scope specifies optional requested permissions.
|
// Scope specifies optional requested permissions.
|
||||||
@@ -123,7 +123,6 @@ type ProxyRoute struct {
|
|||||||
rewrite string
|
rewrite string
|
||||||
destination string
|
destination string
|
||||||
backends []string
|
backends []string
|
||||||
//healthCheck RouteHealthCheck
|
|
||||||
methods []string
|
methods []string
|
||||||
cors Cors
|
cors Cors
|
||||||
disableHostFording bool
|
disableHostFording bool
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func Fatal(msg string, args ...interface{}) {
|
|||||||
func Debug(msg string, args ...interface{}) {
|
func Debug(msg string, args ...interface{}) {
|
||||||
log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout")))
|
log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout")))
|
||||||
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
||||||
if strings.ToLower(logLevel) == "trace" || strings.ToLower(logLevel) == "debug" {
|
if strings.ToLower(logLevel) == traceLog || strings.ToLower(logLevel) == "debug" {
|
||||||
logWithCaller("DEBUG", msg, args...)
|
logWithCaller("DEBUG", msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ func Debug(msg string, args ...interface{}) {
|
|||||||
func Trace(msg string, args ...interface{}) {
|
func Trace(msg string, args ...interface{}) {
|
||||||
log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout")))
|
log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout")))
|
||||||
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
||||||
if strings.ToLower(logLevel) == "trace" {
|
if strings.ToLower(logLevel) == traceLog {
|
||||||
logWithCaller("DEBUG", msg, args...)
|
logWithCaller("DEBUG", msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func logWithCaller(level, msg string, args ...interface{}) {
|
|||||||
// Log message with caller information if GOMA_LOG_LEVEL is trace
|
// Log message with caller information if GOMA_LOG_LEVEL is trace
|
||||||
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
||||||
if strings.ToLower(logLevel) != "off" {
|
if strings.ToLower(logLevel) != "off" {
|
||||||
if strings.ToLower(logLevel) == "trace" {
|
if strings.ToLower(logLevel) == traceLog {
|
||||||
log.Printf("%s: %s (File: %s, Line: %d)\n", level, formattedMessage, file, line)
|
log.Printf("%s: %s (File: %s, Line: %d)\n", level, formattedMessage, file, line)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s: %s\n", level, formattedMessage)
|
log.Printf("%s: %s\n", level, formattedMessage)
|
||||||
|
|||||||
20
pkg/logger/var.go
Normal file
20
pkg/logger/var.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
const traceLog = "trace"
|
||||||
@@ -10,13 +10,14 @@ You may get a copy of the License at
|
|||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileExists checks if the file does exist
|
// FileExists checks if the file does exist
|
||||||
@@ -148,7 +149,7 @@ func Slug(text string) string {
|
|||||||
text = strings.ToLower(text)
|
text = strings.ToLower(text)
|
||||||
|
|
||||||
// Replace spaces and special characters with hyphens
|
// Replace spaces and special characters with hyphens
|
||||||
re := regexp.MustCompile(`[^\w]+`)
|
re := regexp.MustCompile(`\W+`)
|
||||||
text = re.ReplaceAllString(text, "-")
|
text = re.ReplaceAllString(text, "-")
|
||||||
|
|
||||||
// Remove leading and trailing hyphens
|
// Remove leading and trailing hyphens
|
||||||
|
|||||||
Reference in New Issue
Block a user