Skip to content

Commit

Permalink
Merge pull request #102 from AikidoSec/geo-blocking
Browse files Browse the repository at this point in the history
Geo blocking support
  • Loading branch information
willem-delbare authored Dec 13, 2024
2 parents 587b177 + fc31561 commit a8bc34c
Show file tree
Hide file tree
Showing 29 changed files with 357 additions and 42 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
go mod tidy
go get google.golang.org/grpc
go get github.com/stretchr/testify/assert
go get github.com/seancfoley/ipaddress-go/ipaddr
go get main/ipc/protos
go test ./...
go build -ldflags "-s -w" -buildmode=c-shared -o ../../build/aikido-request-processor.so
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ Prerequisites:
#### For Red Hat-based Systems (RHEL, CentOS, Fedora)

```
rpm -Uvh --oldpackage https://github.com/AikidoSec/firewall-php/releases/download/v1.0.99/aikido-php-firewall.x86_64.rpm
rpm -Uvh --oldpackage https://github.com/AikidoSec/firewall-php/releases/download/v1.0.100/aikido-php-firewall.x86_64.rpm
```

#### For Debian-based Systems (Debian, Ubuntu)

```
curl -L -O https://github.com/AikidoSec/firewall-php/releases/download/v1.0.99/aikido-php-firewall.x86_64.deb
curl -L -O https://github.com/AikidoSec/firewall-php/releases/download/v1.0.100/aikido-php-firewall.x86_64.deb
dpkg -i -E ./aikido-php-firewall.x86_64.deb
```

Expand All @@ -54,7 +54,7 @@ dpkg -i -E ./aikido-php-firewall.x86_64.deb
```
commands:
aikido-php-firewall:
command: "rpm -Uvh --oldpackage https://github.com/AikidoSec/firewall-php/releases/download/v1.0.99/aikido-php-firewall.x86_64.rpm"
command: "rpm -Uvh --oldpackage https://github.com/AikidoSec/firewall-php/releases/download/v1.0.100/aikido-php-firewall.x86_64.rpm"
ignoreErrors: true
files:
Expand Down
7 changes: 7 additions & 0 deletions docs/should_block_request.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Should block request

In order to enable the user blocking and rate limiting features, the protected app can call `\aikido\should_block_request` to obtain the blocking decision for the current request and act accordingly.
We provide middleware examples that can be used in different scenarious.

## No framework

Expand Down Expand Up @@ -55,6 +56,9 @@ class AikidoMiddleware implements MiddlewareInterface
else if ($decision->trigger == "ip") {
$message = "Your IP address is not allowed to access this endpoint! (Your IP: {$decision->ip})";
}
else if ($decision->trigger == "geoip") {
$message = "Your IP address is blocked due to geo restrictions! (Your IP: {$decision->ip})";
}

return new Response([
'message' => $message,
Expand Down Expand Up @@ -126,6 +130,9 @@ class ZenBlockDecision
else if ($decision->trigger == "ip") {
return response("Your IP address is not allowed to access this endpoint! (Your IP: {$decision->ip})", 403);
}
else if ($decision->trigger == "geoip") {
return response("Your IP address is blocked due to geo restrictions! (Your IP: {$decision->ip})", 403);
}
}
else if ($decision->type == "ratelimited") {
if ($decision->trigger == "user") {
Expand Down
1 change: 0 additions & 1 deletion docs/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Here's an example:
if (extension_loaded('aikido')) {
\aikido\set_user("123", "John Doe");
}
});
```

Using `\aikido\set_user` has the following benefits:
Expand Down
13 changes: 13 additions & 0 deletions lib/agent/aikido_types/init_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ type CloudConfigData struct {
BypassedIps []string `json:"allowedIPAddresses"`
ReceivedAnyStats bool `json:"receivedAnyStats"`
Block *bool `json:"block,omitempty"`
GeoBlockedIps []string
}

type BlockedIpsData struct {
Source string `json:"source"`
Description string `json:"description"`
Ips []string `json:"ips"`
}

type ListsConfigData struct {
Success bool `json:"success"`
ServiceId int `json:"serviceId"`
BlockedIpAddresses []BlockedIpsData `json:"blockedIPAddresses"`
}

type CloudConfigUpdatedAt struct {
Expand Down
30 changes: 28 additions & 2 deletions lib/agent/cloud/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,37 @@ func ApplyCloudConfig() {
UpdateRateLimitingConfig()
}

func StoreCloudConfig(response []byte) bool {
func UpdateListsConfig() bool {
response, err := SendCloudRequest(globals.EnvironmentConfig.Endpoint, globals.ListsAPI, globals.ListsAPIMethod, nil)
if err != nil {
log.Warn("Error in sending lists request: ", err)
return false
}

tempListsConfig := ListsConfigData{}
err = json.Unmarshal(response, &tempListsConfig)
if err != nil {
log.Warnf("Failed to unmarshal lists config!")
return false
}

CloudConfig.GeoBlockedIps = []string{}
for _, blockedIpsGroup := range tempListsConfig.BlockedIpAddresses {
switch blockedIpsGroup.Source {
case "geoip":
CloudConfig.GeoBlockedIps = blockedIpsGroup.Ips
}
}

return true
}

func StoreCloudConfig(configReponse []byte) bool {
globals.CloudConfigMutex.Lock()
defer globals.CloudConfigMutex.Unlock()

tempCloudConfig := CloudConfigData{}
err := json.Unmarshal(response, &tempCloudConfig)
err := json.Unmarshal(configReponse, &tempCloudConfig)
if err != nil {
log.Warnf("Failed to unmarshal cloud config!")
return false
Expand All @@ -115,6 +140,7 @@ func StoreCloudConfig(response []byte) bool {
return true
}
globals.CloudConfig = tempCloudConfig
UpdateListsConfig()
ApplyCloudConfig()
return true
}
5 changes: 3 additions & 2 deletions lib/agent/cloud/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ func CheckConfigUpdatedAt() {
return
}

response, err = SendCloudRequest(globals.EnvironmentConfig.Endpoint, globals.ConfigAPI, globals.ConfigAPIMethod, nil)
configResponse, err := SendCloudRequest(globals.EnvironmentConfig.Endpoint, globals.ConfigAPI, globals.ConfigAPIMethod, nil)
if err != nil {
log.Warn("Error in sending config request: ", err)
return
}
StoreCloudConfig(response)

StoreCloudConfig(configResponse)
}
4 changes: 3 additions & 1 deletion lib/agent/globals/constants.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package globals

const (
Version = "1.0.99"
Version = "1.0.100"
ConfigUpdatedAtMethod = "GET"
ConfigUpdatedAtAPI = "/config"
ConfigAPIMethod = "GET"
ConfigAPI = "/api/runtime/config"
ListsAPIMethod = "GET"
ListsAPI = "api/runtime/firewall/lists"
EventsAPIMethod = "POST"
EventsAPI = "/api/runtime/events"
MinHeartbeatIntervalInMS = 120000
Expand Down
15 changes: 11 additions & 4 deletions lib/agent/grpc/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,23 @@ func getRateLimitingStatus(method string, route string, user string, ip string)
return &protos.RateLimitingStatus{Block: false}
}

func getCloudConfig() *protos.CloudConfig {
func getCloudConfig(configUpdatedAt int64) *protos.CloudConfig {
isBlockingEnabled := utils.IsBlockingEnabled()

globals.CloudConfigMutex.Lock()
defer globals.CloudConfigMutex.Unlock()

if globals.CloudConfig.ConfigUpdatedAt <= configUpdatedAt {
log.Debugf("CloudConfig.ConfigUpdatedAt was not updated... Returning nil!")
return nil
}

cloudConfig := &protos.CloudConfig{
BlockedUserIds: globals.CloudConfig.BlockedUserIds,
BypassedIps: globals.CloudConfig.BypassedIps,
Block: isBlockingEnabled,
ConfigUpdatedAt: globals.CloudConfig.ConfigUpdatedAt,
BlockedUserIds: globals.CloudConfig.BlockedUserIds,
BypassedIps: globals.CloudConfig.BypassedIps,
GeoBlockedIps: globals.CloudConfig.GeoBlockedIps,
Block: isBlockingEnabled,
}

for _, endpoint := range globals.CloudConfig.Endpoints {
Expand Down
10 changes: 8 additions & 2 deletions lib/agent/grpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"sync/atomic"

"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)

Expand Down Expand Up @@ -53,8 +55,12 @@ func (s *server) OnRequestShutdown(ctx context.Context, req *protos.RequestMetad
return &emptypb.Empty{}, nil
}

func (s *server) GetCloudConfig(ctx context.Context, req *emptypb.Empty) (*protos.CloudConfig, error) {
return getCloudConfig(), nil
func (s *server) GetCloudConfig(ctx context.Context, req *protos.CloudConfigUpdatedAt) (*protos.CloudConfig, error) {
cloudConfig := getCloudConfig(req.ConfigUpdatedAt)
if cloudConfig == nil {
return nil, status.Errorf(codes.Canceled, "CloudConfig was not updated")
}
return cloudConfig, nil
}

func (s *server) OnUser(ctx context.Context, req *protos.User) (*emptypb.Empty, error) {
Expand Down
16 changes: 11 additions & 5 deletions lib/ipc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ service Aikido {
rpc OnDomain (Domain) returns (google.protobuf.Empty);
rpc GetRateLimitingStatus (RateLimitingInfo) returns (RateLimitingStatus);
rpc OnRequestShutdown (RequestMetadataShutdown) returns (google.protobuf.Empty);
rpc GetCloudConfig(google.protobuf.Empty) returns (CloudConfig);
rpc GetCloudConfig(CloudConfigUpdatedAt) returns (CloudConfig);
rpc OnUser(User) returns (google.protobuf.Empty);
rpc OnAttackDetected(AttackDetected) returns (google.protobuf.Empty);
rpc OnMiddlewareInstalled(google.protobuf.Empty) returns (google.protobuf.Empty);
Expand Down Expand Up @@ -58,11 +58,17 @@ message Endpoint {
repeated string allowedIPAddresses = 5;
}

message CloudConfigUpdatedAt {
int64 configUpdatedAt = 1;
}

message CloudConfig {
repeated Endpoint endpoints = 1;
repeated string blockedUserIds = 2;
repeated string bypassedIps = 3;
bool block = 4;
int64 configUpdatedAt = 1;
repeated Endpoint endpoints = 2;
repeated string blockedUserIds = 3;
repeated string bypassedIps = 4;
repeated string geoBlockedIps = 5;
bool block = 6;
}

message RateLimitingStatus {
Expand Down
2 changes: 1 addition & 1 deletion lib/php-extension/include/php_aikido.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
extern zend_module_entry aikido_module_entry;
#define phpext_aikido_ptr &aikido_module_entry

#define PHP_AIKIDO_VERSION "1.0.99"
#define PHP_AIKIDO_VERSION "1.0.100"

#if defined(ZTS) && defined(COMPILE_DL_AIKIDO)
ZEND_TSRMLS_CACHE_EXTERN()
Expand Down
13 changes: 9 additions & 4 deletions lib/request-processor/aikido_types/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package aikido_types

import "github.com/seancfoley/ipaddress-go/ipaddr"

type EnvironmentConfigData struct {
SocketPath string `json:"socket_path"` // '/run/aikido-{version}/aikido-{datetime}-{randint}.sock'
SAPI string `json:"sapi"` // '{php-sapi}'
Expand Down Expand Up @@ -35,8 +37,11 @@ type EndpointKey struct {
}

type CloudConfigData struct {
Endpoints map[EndpointKey]EndpointData
BlockedUserIds map[string]bool
BypassedIps map[string]bool
Block int
ConfigUpdatedAt int64
Endpoints map[EndpointKey]EndpointData
BlockedUserIds map[string]bool
BypassedIps map[string]bool
GeoBlockedIpsTrieV4 *ipaddr.IPv4AddressTrie
GeoBlockedIpsTrieV6 *ipaddr.IPv6AddressTrie
Block int
}
2 changes: 1 addition & 1 deletion lib/request-processor/globals/globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ var CloudConfig CloudConfigData
var CloudConfigMutex sync.Mutex

const (
Version = "1.0.99"
Version = "1.0.100"
)
2 changes: 2 additions & 0 deletions lib/request-processor/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/seancfoley/bintree v1.3.1 // indirect
github.com/seancfoley/ipaddress-go v1.7.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions lib/request-processor/go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI=
github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
github.com/seancfoley/ipaddress-go v1.7.0 h1:vWp3SR3k+HkV3aKiNO2vEe6xbVxS0x/Ixw6hgyP238s=
github.com/seancfoley/ipaddress-go v1.7.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
Expand Down Expand Up @@ -34,5 +41,6 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7 changes: 4 additions & 3 deletions lib/request-processor/grpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"main/globals"
"main/log"
"main/utils"
"time"

"main/ipc/protos"
Expand Down Expand Up @@ -123,12 +124,12 @@ func GetCloudConfig() {
return
}

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

cloudConfig, err := client.GetCloudConfig(ctx, &emptypb.Empty{})
cloudConfig, err := client.GetCloudConfig(ctx, &protos.CloudConfigUpdatedAt{ConfigUpdatedAt: utils.GetCloudConfigUpdatedAt()})
if err != nil {
log.Warnf("Could not get cloud config: %v", err)
log.Infof("Could not get cloud config: %v", err)
return
}

Expand Down
Loading

0 comments on commit a8bc34c

Please sign in to comment.