Skip to content

Commit

Permalink
Implemented blocklist for URLs and IP addresses.
Browse files Browse the repository at this point in the history
  • Loading branch information
Bartlomiej Mika committed Aug 13, 2024
1 parent a405fbd commit 9d843bf
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 5 deletions.
4 changes: 4 additions & 0 deletions cloud/cps-backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,7 @@ static/Pedigree.pdf
# Executable
bin/
cps-backend

# Blacklist - Don't share items we banned from the server.
static/blacklist/ips.json
static/blacklist/urls.json
1 change: 1 addition & 0 deletions cloud/cps-backend/config/constants/session_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const (
SessionSkipAuthorization
SessionID
SessionIPAddress
SessionProxies
SessionUser
SessionUserCompanyName
SessionUserRole
Expand Down
70 changes: 66 additions & 4 deletions cloud/cps-backend/inputport/http/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
gateway_c "github.com/LuchaComics/monorepo/cloud/cps-backend/app/gateway/controller"
"github.com/LuchaComics/monorepo/cloud/cps-backend/config"
"github.com/LuchaComics/monorepo/cloud/cps-backend/config/constants"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/blacklist"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/jwt"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/time"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/uuid"
Expand All @@ -29,6 +30,7 @@ type middleware struct {
Time time.Provider
JWT jwt.Provider
UUID uuid.Provider
Blacklist blacklist.Provider
GatewayController gateway_c.GatewayController
}

Expand All @@ -38,13 +40,15 @@ func NewMiddleware(
uuidp uuid.Provider,
timep time.Provider,
jwtp jwt.Provider,
blp blacklist.Provider,
gatewayController gateway_c.GatewayController,
) Middleware {
return &middleware{
Logger: loggerp,
UUID: uuidp,
Time: timep,
JWT: jwtp,
Blacklist: blp,
GatewayController: gatewayController,
}
}
Expand All @@ -53,13 +57,14 @@ func NewMiddleware(
func (mid *middleware) Attach(fn http.HandlerFunc) http.HandlerFunc {
// Attach our middleware handlers here. Please note that all our middleware
// will start from the bottom and proceed upwards.
// Ex: `URLProcessorMiddleware` will be executed first and
// `PostJWTProcessorMiddleware` will be executed last.
// Ex: `RateLimitMiddleware` will be executed first and
// `ProtectedURLsMiddleware` will be executed last.
fn = mid.ProtectedURLsMiddleware(fn)
fn = mid.IPAddressMiddleware(fn)
fn = mid.PostJWTProcessorMiddleware(fn) // Note: Must be above `JWTProcessorMiddleware`.
fn = mid.JWTProcessorMiddleware(fn) // Note: Must be above `PreJWTProcessorMiddleware`.
fn = mid.PreJWTProcessorMiddleware(fn) // Note: Must be above `URLProcessorMiddleware`.
fn = mid.PreJWTProcessorMiddleware(fn) // Note: Must be above `URLProcessorMiddleware` and `IPAddressMiddleware`.
fn = mid.EnforceBlacklistMiddleware(fn)
fn = mid.IPAddressMiddleware(fn)
fn = mid.URLProcessorMiddleware(fn)
fn = mid.RateLimitMiddleware(fn)

Expand Down Expand Up @@ -105,6 +110,63 @@ func (mid *middleware) RateLimitMiddleware(fn http.HandlerFunc) http.HandlerFunc
}
}

// Note: This middleware must have `IPAddressMiddleware` executed first before running.
func (mid *middleware) EnforceBlacklistMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Open our program's context based on the request and save the
// slash-seperated array from our URL path.
ctx := r.Context()

ipAddress, _ := ctx.Value(constants.SessionIPAddress).(string)
proxies, _ := ctx.Value(constants.SessionProxies).(string)

// Case 1 of 2: Check banned IP addresses.
if mid.Blacklist.IsBannedIPAddress(ipAddress) {

// If the client IP address is banned, check to see if the client
// is making a call to a URL which is not banned, and if the URL
// is not banned (has not been banned before) then print it to
// the console logs for future analysis. Else if the URL is banned
// then don't bother printing to console. The purpose of this code
// is to not clog the console log with warnings.
if !mid.Blacklist.IsBannedURL(r.URL.Path) {
mid.Logger.Warn("rejected request by ip",
slog.Any("url", r.URL.Path),
slog.String("ip_address", ipAddress),
slog.String("proxies", proxies),
slog.Any("middleware", "EnforceBlacklistMiddleware"))
}
http.Error(w, "forbidden at this time", http.StatusForbidden)
return
}

// Case 2 of 2: Check banned URL.
if mid.Blacklist.IsBannedURL(r.URL.Path) {

// If the URL is banned, check to see if the client IP address is
// banned, and if the client has not been banned before then print
// to console the new offending client ip. Else do not print if
// the offending client IP address has been banned before. The
// purpose of this code is to not clog the console log with warnings.
if !mid.Blacklist.IsBannedIPAddress(ipAddress) {
mid.Logger.Warn("rejected request by url",
slog.Any("url", r.URL.Path),
slog.String("ip_address", ipAddress),
slog.String("proxies", proxies),
slog.Any("middleware", "EnforceBlacklistMiddleware"))
}

// DEVELOPERS NOTE:
// Simply return a 404, but in our console log we can see the IP
// address whom made this call.
http.Error(w, "does not exist at this time", http.StatusNotFound)
return
}

next(w, r.WithContext(ctx))
}
}

// URLProcessorMiddleware Middleware will split the full URL path into slash-sperated parts and save to
// the context to flow downstream in the app for this particular request.
func (mid *middleware) URLProcessorMiddleware(fn http.HandlerFunc) http.HandlerFunc {
Expand Down
76 changes: 76 additions & 0 deletions cloud/cps-backend/provider/blacklist/blacklist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package blacklist

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)

// Provider provides an interface for abstracting time.
type Provider interface {
IsBannedIPAddress(ipAddress string) bool
IsBannedURL(url string) bool
}

type blacklistProvider struct {
bannedIPAddresses map[string]bool
bannedURLs map[string]bool
}

// readBlacklistFileContent reads the contents of the blacklist file and returns
// the list of banned items (ex: IP, URLs, etc).
func readBlacklistFileContent(filePath string) ([]string, error) {
// Check if the file exists
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, fmt.Errorf("file %s does not exist", filePath)
}

// Read the file contents
data, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read file %s: %v", filePath, err)
}

// Parse the JSON content as a list of IPs
var ips []string
if err := json.Unmarshal(data, &ips); err != nil {
return nil, fmt.Errorf("failed to parse JSON file %s: %v", filePath, err)
}

return ips, nil
}

// NewProvider Provider contructor that returns the default time provider.
func NewProvider() Provider {
bannedIPAddresses := make(map[string]bool)
bannedIPAddressesFilePath := "static/blacklist/ips.json"
ips, err := readBlacklistFileContent(bannedIPAddressesFilePath)
if err == nil { // Aka: if the file exists...
for _, ip := range ips {
bannedIPAddresses[ip] = true
}
}

bannedURLs := make(map[string]bool)
bannedURLsFilePath := "static/blacklist/urls.json"
urls, err := readBlacklistFileContent(bannedURLsFilePath)
if err == nil { // Aka: if the file exists...
for _, url := range urls {
bannedURLs[url] = true
}
}

return blacklistProvider{
bannedIPAddresses: bannedIPAddresses,
bannedURLs: bannedURLs,
}
}

func (p blacklistProvider) IsBannedIPAddress(ipAddress string) bool {
return p.bannedIPAddresses[ipAddress]
}

func (p blacklistProvider) IsBannedURL(url string) bool {
return p.bannedURLs[url]
}
7 changes: 7 additions & 0 deletions cloud/cps-backend/static/blacklist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This file is empty, but if you would like to add ip addresses to ban then do the following:

1. Create a new file called `ips.json`.

2. Open that file and your ip addresses using this JSON style: ``{"192.168.9.1","192.168.9.2","192.168.9.3"}``

3. Rebuild your docker container and deploy. You are done.
2 changes: 2 additions & 0 deletions cloud/cps-backend/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import (
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/password"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/time"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/uuid"
"github.com/LuchaComics/monorepo/cloud/cps-backend/provider/blacklist"
)

func InitializeEvent() Application {
Expand All @@ -72,6 +73,7 @@ func InitializeEvent() Application {
password.NewProvider,
cpsrn.NewProvider,
mongodb.NewStorage,
blacklist.NewProvider,
mongodbcache.NewCache,
s3_storage.NewStorage,
pdfbuilder.NewCBFFBuilder,
Expand Down
4 changes: 3 additions & 1 deletion cloud/cps-backend/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9d843bf

Please sign in to comment.