Skip to content

Commit

Permalink
#minor: support redis for caching
Browse files Browse the repository at this point in the history
  • Loading branch information
circa10a committed Sep 2, 2022
1 parent 23ff57a commit d738b1e
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 90 deletions.
4 changes: 0 additions & 4 deletions .golangci.yml

This file was deleted.

16 changes: 16 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ route /* {

# status_code is the HTTP response code that is returned if IP address is not within proximity. Default is 403
status_code 403

# redis_enabled disables the in-memory cache and will connect to a remote redis instance
# default is false
redis_enabled true
# the <host>:<port> of the remote redis instance
# default is localhost:6379
redis_addr redis:6379
# redis_username is the username to connect to a secured redis instance
# default is ""
redis_username user
# redis_password is the password to connect to a secured redis instance
# default is ""
redis_password password
# redis_db is the id of the redis db to connect to to store cache ip addresses
# default is 0
redis_db 0
}
}

Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ lint:
exit 1;\
fi;\
golangci-lint run -v

build-linux: export GOOS = linux
build-linux:
xcaddy build --with github.com/$(PROJECT)=./

redis: build-linux
docker-compose up
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ A caddy module for IP geofencing your caddy web server using https://ipbase.com/
1. For an IP that is not within the geofence, `403` will be returned on the matching route.
2. An API token from [ipbase.com](https://ipbase.com/) is **required** to run this module.

## Caching

This module by default will use a configurable in-memory cache, should you need an external/persistent cache, the module supports [redis](https://redis.io/). See the caddyfile exmaple below for how to enable.

> Free tier includes 150 requests per month
### Build with caddy
Expand Down Expand Up @@ -58,9 +62,9 @@ route /* {
# ipbase.com API token, this example reads from an environment variable
ipbase_api_token {$IPBASE_API_TOKEN}
// radius is the distance of the geofence in kilometers
// If not supplied, will default to 0.0 kilometers
// 1.0 => 1.0 kilometers
# radius is the distance of the geofence in kilometers
# If not supplied, will default to 0.0 kilometers
# 1.0 => 1.0 kilometers
radius 1.0
# allow_private_ip_addresses is a boolean for whether or not to allow private ip ranges
Expand All @@ -74,6 +78,22 @@ route /* {
# status_code is the HTTP response code that is returned if IP address is not within proximity. Default is 403
status_code 403
# redis_enabled disables the in-memory cache and will connect to a remote redis instance
# default is false
redis_enabled true
# the <host>:<port> of the remote redis instance
# default is localhost:6379
redis_addr redis:6379
# redis_username is the username to connect to a secured redis instance
# default is ""
redis_username user
# redis_password is the password to connect to a secured redis instance
# default is ""
redis_password password
# redis_db is the id of the redis db to connect to to store cache ip addresses
# default is 0
redis_db 0
}
}
Expand All @@ -98,3 +118,10 @@ make run
```shell
make build
```

### Run redis enabled stack

```shell
export IPBASE_API_TOKEN=<token>
make redis
```
47 changes: 39 additions & 8 deletions caddy_geofence.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/circa10a/go-geofence"
"github.com/go-redis/redis/v9"
"go.uber.org/zap"
)

Expand All @@ -19,14 +20,16 @@ const (
defaultCacheTTL = -1
// 403
defaultStatusCode = http.StatusForbidden
// Localhost for default redis instance
defaultRedisAddr = "localhost:6379"
// Logger namespace string
loggerNamespace = "geofence"
)

// CaddyGeofence implements IP geofencing functionality. https://github.com/circa10a/caddy-geofence
type CaddyGeofence struct {
logger *zap.Logger
GeofenceClient *geofence.Geofence
geofenceClient *geofence.Geofence
// ipbase_api_token is REQUIRED and is an API token ipbase.com
// Free tier includes 150 requests per month
IPBaseAPIToken string `json:"ipbase_api_token,omitempty"`
Expand All @@ -40,16 +43,26 @@ type CaddyGeofence struct {
// cache_ttl is string parameter for caching ip addresses with their allowed/not allowed state
// Not specifying a TTL sets no expiration on cached items and will live until restart
// Valid time units are "ms", "s", "m", "h"
// In-memory cache is used if redis is not enabled
CacheTTL time.Duration `json:"cache_ttl,omitempty"`
// radius is the distance of the geofence in kilometers
// If not supplied, will default to 0.0 kilometers
// 1.0 => 1.0 kilometers
Radius float64 `json:"radius"`
// allow_private_ip_addresses is a boolean for whether or not to allow private ip ranges
// such as 192.X, 172.X, 10.X, [::1] (localhost)
// false by default
// such as 192.X, 172.X, 10.X, [::1] (localhost). Default is false
// Some cellular networks doing NATing with 172.X addresses, in which case, you may not want to allow
AllowPrivateIPAddresses bool `json:"allow_private_ip_addresses"`
// RedisEnabled uses redis for caching. Default is false
RedisEnabled bool `json:"redis_enabled,omitempty"`
// RedisUsername is the username to connect to a redis instance. Default is ""
RedisUsername string `json:"redis_username,omitempty"`
// RedisPassword is the password to connect to a redis instance. Default is ""
RedisPassword string `json:"redis_password,omitempty"`
// RedisAddr is the address to connect to a redis instance. Default is localhost:6379
RedisAddr string `json:"redis_addr,omitempty"`
// RedisDB is the db id. Default is 0
RedisDB int `json:"redis_db,omitempty"`
}

func init() {
Expand Down Expand Up @@ -85,19 +98,37 @@ func (cg *CaddyGeofence) Provision(ctx caddy.Context) error {
cg.StatusCode = defaultStatusCode
}

// Setup client
geofenceClient, err := geofence.New(&geofence.Config{
// Setup base client options
geofenceConfig := &geofence.Config{
IPAddress: cg.RemoteIP,
Token: cg.IPBaseAPIToken,
Radius: cg.Radius,
AllowPrivateIPAddresses: cg.AllowPrivateIPAddresses,
CacheTTL: cg.CacheTTL,
})
}

// Setup redis
// Set default redis addr if empty
if cg.RedisAddr == "" {
cg.RedisAddr = defaultRedisAddr
}

// If redis is enabled, set the options for go-geofence to create the client
if cg.RedisEnabled {
geofenceConfig.RedisOptions = &redis.Options{
Addr: cg.RedisAddr,
Username: cg.RedisUsername,
Password: cg.RedisPassword,
DB: cg.RedisDB,
}
}

geofenceClient, err := geofence.New(geofenceConfig)
if err != nil {
return err
}

cg.GeofenceClient = geofenceClient
cg.geofenceClient = geofenceClient
return nil
}

Expand Down Expand Up @@ -130,7 +161,7 @@ func (cg CaddyGeofence) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
}

// Check if ip address is nearby
isAddressNear, err := cg.GeofenceClient.IsIPAddressNear(remoteAddr)
isAddressNear, err := cg.geofenceClient.IsIPAddressNear(remoteAddr)
if err != nil {
return err
}
Expand Down
33 changes: 33 additions & 0 deletions caddyfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,39 @@ func (cg *CaddyGeofence) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return err
}
cg.AllowPrivateIPAddresses = allowPrivateIPAddresses
case "redis_enabled":
if !d.NextArg() {
return d.ArgErr()
}
redisEnabled, err := strconv.ParseBool(d.Val())
if err != nil {
return err
}
cg.RedisEnabled = redisEnabled
case "redis_username":
if !d.NextArg() {
return d.ArgErr()
}
cg.RedisUsername = d.Val()
case "redis_password":
if !d.NextArg() {
return d.ArgErr()
}
cg.RedisPassword = d.Val()
case "redis_addr":
if !d.NextArg() {
return d.ArgErr()
}
cg.RedisAddr = d.Val()
case "redis_db":
if !d.NextArg() {
return d.ArgErr()
}
redisDB, err := strconv.Atoi(d.Val())
if err != nil {
return err
}
cg.RedisDB = redisDB
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: '3'

services:
redis:
container_name: redis
image: redis
ports:
- 6379:6379
caddy:
container_name: caddy
image: caddy
ports:
- 80:80
volumes:
- ./caddy:/usr/bin/caddy
- ./Caddyfile:/etc/caddy/Caddyfile
environment:
- IPBASE_API_TOKEN
depends_on:
- redis
51 changes: 26 additions & 25 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ go 1.17

require (
github.com/caddyserver/caddy/v2 v2.5.2
github.com/circa10a/go-geofence v0.6.0
go.uber.org/zap v1.22.0
github.com/circa10a/go-geofence v0.7.0
go.uber.org/zap v1.23.0
)

require (
Expand All @@ -15,31 +15,33 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220804214150-8b0cc382067f // indirect
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220826213629-cd8f367ca010 // indirect
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/caddyserver/certmagic v0.16.2 // indirect
github.com/caddyserver/certmagic v0.17.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dgraph-io/badger v1.6.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-redis/redis/v9 v9.0.0-beta.2 // indirect
github.com/go-resty/resty/v2 v2.7.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/cel-go v0.12.4 // indirect
github.com/google/cel-go v0.12.5 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
Expand All @@ -50,19 +52,17 @@ require (
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.12.0 // indirect
github.com/jackc/pgx/v4 v4.17.0 // indirect
github.com/jackc/pgx/v4 v4.17.1 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/lucas-clemente/quic-go v0.28.1 // indirect
github.com/lucas-clemente/quic-go v0.29.0 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mholt/acmez v1.0.4 // indirect
Expand All @@ -85,31 +85,32 @@ require (
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/slackhq/nebula v1.6.0 // indirect
github.com/smallstep/certificates v0.21.0 // indirect
github.com/smallstep/cli v0.21.0 // indirect
github.com/smallstep/certificates v0.22.1 // indirect
github.com/smallstep/cli v0.22.0 // indirect
github.com/smallstep/nosql v0.4.0 // indirect
github.com/smallstep/truststore v0.11.0 // indirect
github.com/smallstep/truststore v0.12.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2 // indirect
github.com/urfave/cli v1.22.9 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.step.sm/cli-utils v0.7.3 // indirect
go.step.sm/crypto v0.17.0 // indirect
go.step.sm/linkedca v0.17.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.step.sm/cli-utils v0.7.4 // indirect
go.step.sm/crypto v0.19.0 // indirect
go.step.sm/linkedca v0.18.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/goleak v1.1.12 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced // indirect
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect
golang.org/x/tools v0.1.12 // indirect
google.golang.org/genproto v0.0.0-20220810155839-1856144b1d9c // indirect
google.golang.org/grpc v1.48.0 // indirect
google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf // indirect
google.golang.org/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
Expand Down
Loading

0 comments on commit d738b1e

Please sign in to comment.