Merge pull request #61 from jkaninda/develop

Develop
This commit is contained in:
2024-11-05 21:06:46 +01:00
committed by GitHub
18 changed files with 203 additions and 184 deletions

View File

@@ -2,7 +2,6 @@ name: CI
on: on:
push: push:
tags: tags:
- 0.**
- v0.** - v0.**
jobs: jobs:
docker: docker:
@@ -26,7 +25,12 @@ jobs:
- -
name: Get the tag name name: Get the tag name
id: get_tag_name id: get_tag_name
run: echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV run: |
# Extract tag name and remove "v" if present
TAG_NAME="${GITHUB_REF#refs/tags/}"
VERSION="${TAG_NAME#v}"
echo "VERSION=${VERSION}" >> $GITHUB_ENV
echo "BUILD_TIME=${(date -u +"%Y-%m-%dT%H:%M:%SZ")}"
- -
name: Build and push name: Build and push
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
@@ -35,8 +39,8 @@ jobs:
file: "./Dockerfile" file: "./Dockerfile"
platforms: linux/amd64,linux/arm64,linux/arm/v7 platforms: linux/amd64,linux/arm64,linux/arm/v7
build-args: | build-args: |
appVersion=${{ env.TAG_NAME }} appVersion=${{ env.VERSION }}
tags: | tags: |
"${{vars.BUILDKIT_IMAGE}}:${{ env.TAG_NAME }}" "${{vars.BUILDKIT_IMAGE}}:${{ env.VERSION }}"
"${{vars.BUILDKIT_IMAGE}}:latest" "${{vars.BUILDKIT_IMAGE}}:latest"

View File

@@ -92,8 +92,8 @@ docker run --rm --name goma-gateway \
``` ```
### 4. Healthcheck ### 4. Healthcheck
- Goma Gateway readiness: `/readyz` - Goma Gateway health check: `/health/live`
- Routes health check: `/healthz` - Routes health check: `health/routes`
### 5. Simple deployment in docker compose file ### 5. Simple deployment in docker compose file
@@ -103,7 +103,7 @@ services:
image: jkaninda/goma-gateway image: jkaninda/goma-gateway
command: server command: server
healthcheck: healthcheck:
test: curl -f http://localhost/readyz || exit 1 test: curl -f http://localhost/heath/live || exit 1
interval: 30s interval: 30s
retries: 5 retries: 5
start_period: 20s start_period: 20s
@@ -119,10 +119,8 @@ Create a config file in this format
Example of a configuration file Example of a configuration file
```yaml ```yaml
# Goma Gateway configurations ## Goma Gateway configurations
gateway: gateway:
########## Global settings
listenAddr: :80 #:443 SSL
# Proxy write timeout # Proxy write timeout
writeTimeout: 15 writeTimeout: 15
# Proxy read timeout # Proxy read timeout
@@ -134,7 +132,6 @@ gateway:
## SSL Private Key file ## SSL Private Key file
sslKeyFile: ''#key.pem sslKeyFile: ''#key.pem
# Proxy rate limit, it's In-Memory IP based # Proxy rate limit, it's In-Memory IP based
# Distributed Rate Limiting for Token based across multiple instances is not yet integrated
rateLimiter: 0 rateLimiter: 0
accessLog: "/dev/Stdout" accessLog: "/dev/Stdout"
errorLog: "/dev/stderr" errorLog: "/dev/stderr"

View File

@@ -19,7 +19,6 @@ Goma Gateway supports :
- JWT `client authorization based on the result of a request` - JWT `client authorization based on the result of a request`
- Basic-Auth - Basic-Auth
- Rate limiting middleware - Rate limiting middleware
- In-Memory Token Bucket based
- In-Memory client IP based - In-Memory client IP based
- Access middleware - Access middleware

View File

@@ -28,8 +28,8 @@ docker run --rm --name goma-gateway \
``` ```
### 4. Healthcheck ### 4. Healthcheck
- Goma Gateway readiness: `/readyz` - Goma Gateway health check: `/health/live`
- Routes health check: `/healthz` - Routes health check: `health/routes`
### 5. Simple deployment in docker compose file ### 5. Simple deployment in docker compose file
@@ -46,6 +46,7 @@ services:
timeout: 10s timeout: 10s
ports: ports:
- "80:80" - "80:80"
- "443:443"
volumes: volumes:
- ./config:/config/ - ./config:/config/
``` ```
@@ -56,8 +57,6 @@ Example of a configuration file
```yaml ```yaml
# Goma Gateway configurations # Goma Gateway configurations
gateway: gateway:
########## Global settings
listenAddr: :80 #:443 SSL
# Proxy write timeout # Proxy write timeout
writeTimeout: 15 writeTimeout: 15
# Proxy read timeout # Proxy read timeout
@@ -69,7 +68,6 @@ gateway:
## SSL Private Key file ## SSL Private Key file
sslKeyFile: ''#key.pem sslKeyFile: ''#key.pem
# Proxy rate limit, it's In-Memory IP based # Proxy rate limit, it's In-Memory IP based
# Distributed Rate Limiting for Token based across multiple instances is not yet integrated
rateLimiter: 0 rateLimiter: 0
accessLog: "/dev/Stdout" accessLog: "/dev/Stdout"
errorLog: "/dev/stderr" errorLog: "/dev/stderr"

View File

@@ -13,6 +13,46 @@ The Route allows you to match on HTTP traffic and direct it to the backend.
### Create a route ### Create a route
```yaml ```yaml
# Goma Gateway configurations
gateway:
# Proxy write timeout
writeTimeout: 15
# Proxy read timeout
readTimeout: 15
# Proxy idle timeout
idleTimeout: 60
## SSL Certificate file
sslCertFile: '' #cert.pem
## SSL Private Key file
sslKeyFile: ''#key.pem
# Proxy rate limit, it's In-Memory IP based
rateLimiter: 0
accessLog: "/dev/Stdout"
errorLog: "/dev/stderr"
## Enable, disable routes health check
disableHealthCheckStatus: false
## Returns backend route healthcheck errors
disableRouteHealthCheckError: false
# Disable display routes on start
disableDisplayRouteOnStart: false
# disableKeepAlive allows enabling and disabling KeepALive server
disableKeepAlive: false
# interceptErrors intercepts backend errors based on defined the status codes
interceptErrors:
- 405
- 500
# - 400
# Proxy Global HTTP Cors
cors:
# Global routes cors for all routes
origins:
- http://localhost:8080
- https://example.com
# Global routes cors headers for all routes
headers:
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 routes ##### Define routes
routes: routes:
# Example of a route | 1 # Example of a route | 1
@@ -32,119 +72,32 @@ The Route allows you to match on HTTP traffic and direct it to the backend.
healthCheck: '' #/internal/health/ready healthCheck: '' #/internal/health/ready
# Route Cors, global cors will be overridden by route # Route Cors, global cors will be overridden by route
cors: cors:
# Route Origins Cors, global cors will be overridden by route # Route Origins Cors, route will override global cors origins
origins: origins:
- https://dev.example.com - https://dev.example.com
- http://localhost:3000 - http://localhost:3000
- https://example.com - https://example.com
# Route Cors headers, route will override global cors # Route Cors headers, route will override global cors headers
headers: headers:
Access-Control-Allow-Methods: 'GET' Access-Control-Allow-Methods: 'GET'
Access-Control-Allow-Headers: 'Origin, Authorization, Accept, Content-Type, Access-Control-Allow-Headers, X-Client-Id, X-Session-Id' 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-Allow-Credentials: 'true'
Access-Control-Max-Age: 1728000 Access-Control-Max-Age: 1728000
##### Define route middlewares from middlewares names ##### Apply middlewares to the route
## The name must be unique ## The name must be unique
## List of middleware name ## List of middleware name
middlewares: middlewares:
- api-forbidden-paths - api-forbidden-paths
- basic-auth
```
### Full example of route
```yaml
## Goma - simple lightweight API Gateway and Reverse Proxy.
# Goma Gateway configurations
gateway:
########## Global settings
listenAddr: 0.0.0.0:80
# Proxy write timeout
writeTimeout: 15
# Proxy read timeout
readTimeout: 15
# Proxy idle timeout
idleTimeout: 60
# Proxy rate limit, it's In-Memory IP based
# Distributed Rate Limiting for Token based across multiple instances is not yet integrated
rateLimiter: 0
accessLog: "/dev/Stdout"
errorLog: "/dev/stderr"
## Returns backend route healthcheck errors
disableRouteHealthCheckError: false
# Disable display routes on start
disableDisplayRouteOnStart: false
# disableKeepAlive allows enabling and disabling KeepALive server
disableKeepAlive: false
# interceptErrors intercepts backend errors based on defined the status codes
interceptErrors:
- 405
- 500
# - 400
# Proxy Global HTTP Cors
cors:
# Global routes cors for all routes
origins:
- http://localhost:8080
- https://example.com
# Global routes cors headers for all routes
headers:
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 routes
routes:
# Example of a route | 1
- name: Public
# host Domain/host based request routing
host: "" # Host is optional
path: /public
## Rewrite a request path
# e.g rewrite: /store to /
rewrite: /
destination: https://example.com
#DisableHeaderXForward Disable X-forwarded header.
# [X-Forwarded-Host, X-Forwarded-For, Host, Scheme ]
# It will not match the backend route, by default, it's disabled
disableHeaderXForward: false
# Internal health check
healthCheck: '' #/internal/health/ready
# Route Cors, global cors will be overridden by route
cors:
# Route Origins Cors, global cors will be overridden by route
origins:
- https://dev.example.com
- http://localhost:3000
- https://example.com
# Route Cors headers, route will override global cors
headers:
Access-Control-Allow-Methods: 'GET'
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 middlewares from middlewares names
## The name must be unique
## List of middleware name
middlewares:
- api-forbidden-paths
- basic-auth
# Example of a route | 2 # Example of a route | 2
- name: Authentication service
path: /auth
rewrite: /
destination: https://example.com
healthCheck: /
cors: {}
middlewares:
- api-forbidden-paths
# Example of a route | 3
- name: Basic auth - name: Basic auth
path: /protected path: /protected
rewrite: / rewrite: /
destination: 'http://notification-service:8080' destination: https://example.com
healthCheck: healthCheck:
cors: {} cors: {}
middlewares: [] middlewares:
- api-forbidden-paths
- basic-auth
#Defines proxy middlewares #Defines proxy middlewares
# middleware name must be unique # middleware name must be unique
@@ -176,26 +129,22 @@ middlewares:
# Required headers, if not present in the request, the proxy will return 403 # Required headers, if not present in the request, the proxy will return 403
requiredHeaders: requiredHeaders:
- Authorization - Authorization
#Sets the request variable to the given value after the authorization request completes. # You can also get headers from the authentication request result and inject them into the next request header or params.
# # In case you want to get headers from the authentication service and inject them into the next request headers.
# Add header to the next request from AuthRequest header, depending on your requirements # Set the request variable to the given value after the authorization request completes.
# 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
#Sets the request variable to the given value after the authorization request completes.
#
# 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 headers. # In case you want to get headers from the authentication service and inject them into the next request headers.
# Key is authentication request response header Key. Value is the next Request header Key.
headers: headers:
userId: X-Auth-UserId userId: Auth-UserId
userCountryId: X-Auth-UserCountryId userCountryId: Auth-UserCountryId
# In case you want to get headers from the Authentication service and inject them to the next request params. # In case you want to get headers from the Authentication service and inject them to the next request params.
#Key is authentication request response header Key. Value is the next Request parameter Key.
params: params:
userCountryId: countryId userCountryId: countryId
# The server will return 403 # The server will return 403
- name: api-forbidden-paths - name: api-forbidden-paths
type: access type: access
## prevents access paths ## prevents access paths
paths: paths:
- /swagger-ui/* - /swagger-ui/*
- /v2/swagger-ui/* - /v2/swagger-ui/*

View File

@@ -10,5 +10,6 @@ services:
timeout: 10s timeout: 10s
ports: ports:
- "80:80" - "80:80"
- "443:443"
volumes: volumes:
- ./config:/config/ - ./config:/config/

View File

@@ -4,20 +4,24 @@ metadata:
name: goma-config name: goma-config
data: data:
goma.yml: | goma.yml: |
# Goma Gateway configurations
gateway: gateway:
########## Global settings
listenAddr: 0.0.0.0:80
# Proxy write timeout # Proxy write timeout
writeTimeout: 15 writeTimeout: 15
# Proxy read timeout # Proxy read timeout
readTimeout: 15 readTimeout: 15
# Proxy idle timeout # Proxy idle timeout
idleTimeout: 60 idleTimeout: 60
## SSL Certificate file
sslCertFile: '' #cert.pem
## SSL Private Key file
sslKeyFile: ''#key.pem
# Proxy rate limit, it's In-Memory IP based # Proxy rate limit, it's In-Memory IP based
# Distributed Rate Limiting for Token based across multiple instances is not yet integrated
rateLimiter: 0 rateLimiter: 0
accessLog: "/dev/Stdout" accessLog: "/dev/Stdout"
errorLog: "/dev/stderr" errorLog: "/dev/stderr"
## Enable, disable routes health check
disableHealthCheckStatus: false
## Returns backend route healthcheck errors ## Returns backend route healthcheck errors
disableRouteHealthCheckError: false disableRouteHealthCheckError: false
# Disable display routes on start # Disable display routes on start
@@ -59,23 +63,23 @@ data:
healthCheck: '' #/internal/health/ready healthCheck: '' #/internal/health/ready
# Route Cors, global cors will be overridden by route # Route Cors, global cors will be overridden by route
cors: cors:
# Route Origins Cors, global cors will be overridden by route # Route Origins Cors, route will override global cors origins
origins: origins:
- https://dev.example.com - https://dev.example.com
- http://localhost:3000 - http://localhost:3000
- https://example.com - https://example.com
# Route Cors headers, route will override global cors # Route Cors headers, route will override global cors headers
headers: headers:
Access-Control-Allow-Methods: 'GET' Access-Control-Allow-Methods: 'GET'
Access-Control-Allow-Headers: 'Origin, Authorization, Accept, Content-Type, Access-Control-Allow-Headers, X-Client-Id, X-Session-Id' 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-Allow-Credentials: 'true'
Access-Control-Max-Age: 1728000 Access-Control-Max-Age: 1728000
##### Define route middlewares from middlewares names ##### Apply middlewares to the route
## The name must be unique ## The name must be unique
## List of middleware name ## List of middleware name
middlewares: middlewares:
- api-forbidden-paths - api-forbidden-paths
# Example of a route | 3 # Example of a route | 2
- name: Basic auth - name: Basic auth
path: /protected path: /protected
rewrite: / rewrite: /
@@ -116,20 +120,16 @@ data:
# Required headers, if not present in the request, the proxy will return 403 # Required headers, if not present in the request, the proxy will return 403
requiredHeaders: requiredHeaders:
- Authorization - Authorization
#Sets the request variable to the given value after the authorization request completes. # You can also get headers from the authentication request result and inject them into the next request header or params.
# # In case you want to get headers from the authentication service and inject them into the next request headers.
# Add header to the next request from AuthRequest header, depending on your requirements # Set the request variable to the given value after the authorization request completes.
# 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
#Sets the request variable to the given value after the authorization request completes.
#
# 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 headers. # In case you want to get headers from the authentication service and inject them into the next request headers.
# Key is authentication request response header Key. Value is the next Request header Key.
headers: headers:
userId: X-Auth-UserId userId: Auth-UserId
userCountryId: X-Auth-UserCountryId userCountryId: Auth-UserCountryId
# In case you want to get headers from the Authentication service and inject them to the next request params. # In case you want to get headers from the Authentication service and inject them to the next request params.
#Key is authentication request response header Key. Value is the next Request parameter Key.
params: params:
userCountryId: countryId userCountryId: countryId
# The server will return 403 # The server will return 403

View File

@@ -23,14 +23,14 @@ spec:
- containerPort: 80 - containerPort: 80
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /health/live
port: 80 port: 80
initialDelaySeconds: 15 initialDelaySeconds: 15
periodSeconds: 30 periodSeconds: 30
timeoutSeconds: 10 timeoutSeconds: 10
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /readyz path: /health/live
port: 80 port: 80
initialDelaySeconds: 15 initialDelaySeconds: 15
periodSeconds: 40 periodSeconds: 40

View File

@@ -1,7 +1,5 @@
# Goma Gateway configurations # Goma Gateway configurations
gateway: gateway:
########## Global settings
listenAddr: :80 #:443 SSL
# Proxy write timeout # Proxy write timeout
writeTimeout: 15 writeTimeout: 15
# Proxy read timeout # Proxy read timeout
@@ -13,7 +11,6 @@ gateway:
## SSL Private Key file ## SSL Private Key file
sslKeyFile: ''#key.pem sslKeyFile: ''#key.pem
# Proxy rate limit, it's In-Memory IP based # Proxy rate limit, it's In-Memory IP based
# Distributed Rate Limiting for Token based across multiple instances is not yet integrated
rateLimiter: 0 rateLimiter: 0
accessLog: "/dev/Stdout" accessLog: "/dev/Stdout"
errorLog: "/dev/stderr" errorLog: "/dev/stderr"

View File

@@ -85,7 +85,6 @@ func initConfig(configFile string) {
} }
conf := &GatewayConfig{ conf := &GatewayConfig{
GatewayConfig: Gateway{ GatewayConfig: Gateway{
ListenAddr: ":80",
WriteTimeout: 15, WriteTimeout: 15,
ReadTimeout: 15, ReadTimeout: 15,
IdleTimeout: 60, IdleTimeout: 60,

View File

@@ -49,6 +49,10 @@ func (intercept InterceptErrors) ErrorInterceptor(next http.Handler) http.Handle
logger.Error("Backend error") logger.Error("Backend error")
logger.Error("An error occurred from the backend with the status code: %d", rec.statusCode) logger.Error("An error occurred from the backend with the status code: %d", rec.statusCode)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
//Update Origin Cors Headers
if allowedOrigin(intercept.Origins, r.Header.Get("Origin")) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
}
w.WriteHeader(rec.statusCode) w.WriteHeader(rec.statusCode)
err := json.NewEncoder(w).Encode(ProxyResponseError{ err := json.NewEncoder(w).Encode(ProxyResponseError{
Success: false, Success: false,

View File

@@ -0,0 +1,40 @@
/*
* 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 middleware
import "net/http"
func getRealIP(r *http.Request) string {
if ip := r.Header.Get("X-Real-IP"); ip != "" {
return ip
}
if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
return ip
}
return r.RemoteAddr
}
func allowedOrigin(origins []string, origin string) bool {
for _, o := range origins {
if o == origin {
return true
}
continue
}
return false
}

View File

@@ -34,6 +34,10 @@ 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")
//Update Origin Cors Headers
if allowedOrigin(jwtAuth.Origins, r.Header.Get("Origin")) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
}
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
err := json.NewEncoder(w).Encode(ProxyResponseError{ err := json.NewEncoder(w).Encode(ProxyResponseError{
Message: http.StatusText(http.StatusUnauthorized), Message: http.StatusText(http.StatusUnauthorized),

View File

@@ -69,6 +69,10 @@ func (rl *RateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
logger.Error("Too many requests from IP: %s %s %s", clientID, r.URL, r.UserAgent()) logger.Error("Too many requests from IP: %s %s %s", clientID, r.URL, r.UserAgent())
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusTooManyRequests) w.WriteHeader(http.StatusTooManyRequests)
//Update Origin Cors Headers
if allowedOrigin(rl.Origins, r.Header.Get("Origin")) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
}
err := json.NewEncoder(w).Encode(ProxyResponseError{ err := json.NewEncoder(w).Encode(ProxyResponseError{
Success: false, Success: false,
Code: http.StatusTooManyRequests, Code: http.StatusTooManyRequests,
@@ -84,12 +88,3 @@ func (rl *RateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
}) })
} }
} }
func getRealIP(r *http.Request) string {
if ip := r.Header.Get("X-Real-IP"); ip != "" {
return ip
}
if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
return ip
}
return r.RemoteAddr
}

View File

@@ -30,6 +30,7 @@ type RateLimiter struct {
Window time.Duration Window time.Duration
ClientMap map[string]*Client ClientMap map[string]*Client
mu sync.Mutex mu sync.Mutex
Origins []string
} }
// Client stores request count and window expiration for each client. // Client stores request count and window expiration for each client.
@@ -39,11 +40,12 @@ type Client struct {
} }
// NewRateLimiterWindow creates a new RateLimiter. // NewRateLimiterWindow creates a new RateLimiter.
func NewRateLimiterWindow(requests int, window time.Duration) *RateLimiter { func NewRateLimiterWindow(requests int, window time.Duration, origin []string) *RateLimiter {
return &RateLimiter{ return &RateLimiter{
Requests: requests, Requests: requests,
Window: window, Window: window,
ClientMap: make(map[string]*Client), ClientMap: make(map[string]*Client),
Origins: origin,
} }
} }
@@ -69,6 +71,7 @@ type JwtAuth struct {
RequiredHeaders []string RequiredHeaders []string
Headers map[string]string Headers map[string]string
Params map[string]string Params map[string]string
Origins []string
} }
// AuthenticationMiddleware Define struct // AuthenticationMiddleware Define struct
@@ -94,7 +97,8 @@ type AuthBasic struct {
// InterceptErrors contains backend status code errors to intercept // InterceptErrors contains backend status code errors to intercept
type InterceptErrors struct { type InterceptErrors struct {
Errors []int Errors []int
Origins []string
} }
// responseRecorder intercepts the response body and status code // responseRecorder intercepts the response body and status code

View File

@@ -35,13 +35,15 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
// Routes health check // Routes health check
if !gateway.DisableHealthCheckStatus { if !gateway.DisableHealthCheckStatus {
r.HandleFunc("/healthz", heath.HealthCheckHandler).Methods("GET") r.HandleFunc("/healthz", heath.HealthCheckHandler).Methods("GET")
r.HandleFunc("/health/routes", heath.HealthCheckHandler).Methods("GET")
} }
// Readiness // Health check
r.HandleFunc("/health/live", heath.HealthReadyHandler).Methods("GET")
r.HandleFunc("/readyz", heath.HealthReadyHandler).Methods("GET") r.HandleFunc("/readyz", heath.HealthReadyHandler).Methods("GET")
if gateway.RateLimiter != 0 { if gateway.RateLimiter != 0 {
//rateLimiter := middleware.NewRateLimiter(gateway.RateLimiter, time.Minute) //rateLimiter := middleware.NewRateLimiter(gateway.RateLimiter, time.Minute)
limiter := middleware.NewRateLimiterWindow(gateway.RateLimiter, time.Minute) // requests per minute limiter := middleware.NewRateLimiterWindow(gateway.RateLimiter, time.Minute, gateway.Cors.Origins) // requests per minute
// Add rate limit middleware to all routes, if defined // Add rate limit middleware to all routes, if defined
r.Use(limiter.RateLimitMiddleware()) r.Use(limiter.RateLimitMiddleware())
} }
@@ -111,6 +113,7 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
RequiredHeaders: jwt.RequiredHeaders, RequiredHeaders: jwt.RequiredHeaders,
Headers: jwt.Headers, Headers: jwt.Headers,
Params: jwt.Params, Params: jwt.Params,
Origins: gateway.Cors.Origins,
} }
// Apply JWT authentication middleware // Apply JWT authentication middleware
secureRouter.Use(amw.AuthMiddleware) secureRouter.Use(amw.AuthMiddleware)
@@ -161,7 +164,8 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
r.Use(CORSHandler(gateway.Cors)) // Apply CORS middleware r.Use(CORSHandler(gateway.Cors)) // Apply CORS middleware
// Apply errorInterceptor middleware // Apply errorInterceptor middleware
interceptErrors := middleware.InterceptErrors{ interceptErrors := middleware.InterceptErrors{
Errors: gateway.InterceptErrors, Errors: gateway.InterceptErrors,
Origins: gateway.Cors.Origins,
} }
r.Use(interceptErrors.ErrorInterceptor) r.Use(interceptErrors.ErrorInterceptor)
return r return r

View File

@@ -29,6 +29,7 @@ import (
func (gatewayServer GatewayServer) Start(ctx context.Context) error { func (gatewayServer GatewayServer) Start(ctx context.Context) 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.Info("Initializing routes...done") logger.Info("Initializing routes...done")
tlsConfig := &tls.Config{} tlsConfig := &tls.Config{}
var listenWithTLS = false var listenWithTLS = false
@@ -41,8 +42,17 @@ func (gatewayServer GatewayServer) Start(ctx context.Context) error {
listenWithTLS = true listenWithTLS = true
} }
srv := &http.Server{ // HTTP Server
Addr: gatewayServer.gateway.ListenAddr, httpServer := &http.Server{
Addr: ":80",
WriteTimeout: time.Second * time.Duration(gatewayServer.gateway.WriteTimeout),
ReadTimeout: time.Second * time.Duration(gatewayServer.gateway.ReadTimeout),
IdleTimeout: time.Second * time.Duration(gatewayServer.gateway.IdleTimeout),
Handler: route, // Pass our instance of gorilla/mux in.
}
// HTTPS Server
httpsServer := &http.Server{
Addr: ":443",
WriteTimeout: time.Second * time.Duration(gatewayServer.gateway.WriteTimeout), WriteTimeout: time.Second * time.Duration(gatewayServer.gateway.WriteTimeout),
ReadTimeout: time.Second * time.Duration(gatewayServer.gateway.ReadTimeout), ReadTimeout: time.Second * time.Duration(gatewayServer.gateway.ReadTimeout),
IdleTimeout: time.Second * time.Duration(gatewayServer.gateway.IdleTimeout), IdleTimeout: time.Second * time.Duration(gatewayServer.gateway.IdleTimeout),
@@ -53,36 +63,52 @@ func (gatewayServer GatewayServer) Start(ctx context.Context) error {
printRoute(gatewayServer.gateway.Routes) printRoute(gatewayServer.gateway.Routes)
} }
// Set KeepAlive // Set KeepAlive
srv.SetKeepAlivesEnabled(!gatewayServer.gateway.DisableKeepAlive) httpServer.SetKeepAlivesEnabled(!gatewayServer.gateway.DisableKeepAlive)
httpsServer.SetKeepAlivesEnabled(!gatewayServer.gateway.DisableKeepAlive)
go func() {
logger.Info("Starting HTTP server listen=0.0.0.0:80")
if err := httpServer.ListenAndServe(); err != nil {
logger.Fatal("Error starting Goma Gateway HTTP server: %v", err)
}
}()
go func() { go func() {
logger.Info("Started Goma Gateway server on %v", gatewayServer.gateway.ListenAddr)
if listenWithTLS { if listenWithTLS {
logger.Info("Server is running securely over HTTPS on %v ", gatewayServer.gateway.ListenAddr) logger.Info("Starting HTTPS server listen=0.0.0.0:443")
if err := srv.ListenAndServeTLS("", ""); err != nil { if err := httpsServer.ListenAndServeTLS("", ""); err != nil {
logger.Fatal("Error starting Goma Gateway server: %v", err) logger.Fatal("Error starting Goma Gateway HTTPS server: %v", err)
}
} else {
if err := srv.ListenAndServe(); err != nil {
logger.Fatal("Error starting Goma Gateway server: %v", err)
} }
} }
}() }()
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(2)
go func() { go func() {
defer wg.Done() defer wg.Done()
<-ctx.Done() <-ctx.Done()
shutdownCtx := context.Background() shutdownCtx := context.Background()
shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second) shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second)
defer cancel() defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil { if err := httpServer.Shutdown(shutdownCtx); err != nil {
_, err := fmt.Fprintf(os.Stderr, "error shutting down Goma Gateway server: %s\n", err) _, err := fmt.Fprintf(os.Stderr, "error shutting down HTTP server: %s\n", err)
if err != nil { if err != nil {
return return
} }
} }
}() }()
go func() {
defer wg.Done()
<-ctx.Done()
shutdownCtx := context.Background()
shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second)
defer cancel()
if listenWithTLS {
if err := httpsServer.Shutdown(shutdownCtx); err != nil {
_, err := fmt.Fprintf(os.Stderr, "error shutting HTTPS server: %s\n", err)
if err != nil {
return
}
}
}
}()
wg.Wait() wg.Wait()
return nil return nil

View File

@@ -133,12 +133,10 @@ type Route struct {
// Gateway contains Goma Proxy Gateway's configs // Gateway contains Goma Proxy Gateway's configs
type Gateway struct { type Gateway struct {
// ListenAddr Defines the server listenAddr // SSLCertFile SSL Certificate file
//
//e.g: localhost:8080
ListenAddr string `yaml:"listenAddr" env:"GOMA_LISTEN_ADDR, overwrite"`
SSLCertFile string `yaml:"sslCertFile" env:"GOMA_SSL_CERT_FILE, overwrite"` SSLCertFile string `yaml:"sslCertFile" env:"GOMA_SSL_CERT_FILE, overwrite"`
SSLKeyFile string `yaml:"sslKeyFile" env:"GOMA_SSL_KEY_FILE, overwrite"` // SSLKeyFile SSL Private key file
SSLKeyFile string `yaml:"sslKeyFile" env:"GOMA_SSL_KEY_FILE, overwrite"`
// WriteTimeout defines proxy write timeout // WriteTimeout defines proxy write timeout
WriteTimeout int `yaml:"writeTimeout" env:"GOMA_WRITE_TIMEOUT, overwrite"` WriteTimeout int `yaml:"writeTimeout" env:"GOMA_WRITE_TIMEOUT, overwrite"`
// ReadTimeout defines proxy read timeout // ReadTimeout defines proxy read timeout