Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching in Redis #6

Merged
merged 2 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Release

on:
push:
tags:
- "*"

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.18.x"

- name: Build EchoIP binary
run: go build -o ./echoip ./cmd/echoip/main.go

- name: Upload Release Artifact
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
zip -r echoip-linux-amd64-${{ github.ref_name }}.zip echoip html LICENSE

- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: echoip-linux-amd64-${{ github.ref_name }}.zip
bodyFile: "CHANGELOG.md"
7 changes: 2 additions & 5 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Go
name: Test

on: [push]

Expand All @@ -13,10 +13,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.13.x"

- name: Display Go version
run: go version
go-version: "1.18.x"

- name: Run tests
run: go test ./...
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

## 1.0.0 (2023-10-06)

### Features

- IP Stack API [79c8f54d](https://github.com/levelsoftware/echoip/pull/6/commits/79c8f54d4459e69f151cdca917a60ce805b5066f)
- File Configuration [79c8f54d](https://github.com/levelsoftware/echoip/pull/6/commits/79c8f54d4459e69f151cdca917a60ce805b5066f)
- Redis Cache [79c8f54d](https://github.com/levelsoftware/echoip/pull/6/commits/79c8f54d4459e69f151cdca917a60ce805b5066f)

8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ vet:
check-fmt:
bash -c "diff --line-format='%L' <(echo -n) <(gofmt -d -s .)"


lint: check-fmt vet

install:
install: install-config
go install ./...

install-config:
sudo install -D etc/echoip/config.toml /etc/echoip/config.toml

databases := GeoLite2-City GeoLite2-Country GeoLite2-ASN

$(databases):
Expand Down Expand Up @@ -63,7 +67,7 @@ docker-push: docker-test docker-login
docker-pushx: docker-multiarch-builder docker-test docker-login
$(DOCKER) buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t $(DOCKER_IMAGE) --push .

xinstall:
xinstall: install-config
env GOOS=$(XGOOS) GOARCH=$(XGOARCH) go install ./...

publish:
Expand Down
76 changes: 50 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,34 +84,58 @@ between IPv4 and IPv6 lookup.
- Port testing
- All endpoints (except `/port`) can return information about a custom IP address specified via `?ip=` query parameter
- Open source under the [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause)
- Supports IP Stack API or GeoIP

### Installation from Release

- Download release file.
- Install `./echoip` binary ( `sudo install echoip /usr/local/bin/echoip` )
- Install configuration file( `sudo install -D etc/echoip/config.toml /etc/echoip/config.toml` )
- Point `config.TemplateDir` to release `html/`

### Installation from Source

- Install Go 1.18
- `$ cd echoip/`
- `$ make install`

### Usage

```
$ echoip -h
Usage of echoip:
-C int
Size of response cache. Set to 0 to disable
-H value
Header to trust for remote IP, if present (e.g. X-Real-IP)
-P Enables profiling handlers
-S string
IP Stack API Key
-a string
Path to GeoIP ASN database
-c string
Path to GeoIP city database
-d string
Which database to use, 'ipstack' or 'geoip' (default "geoip")
-f string
Path to GeoIP country database
-h Use HTTPS for IP Stack ( only non-free accounts )
-l string
Listening address (default ":8080")
-p Enable port lookup
-r Perform reverse hostname lookups
-s Show sponsor logo
-t string
Path to template dir (default "html")
-x Enable security module for IP Stack ( must have security module, aka. non-free account. )
$ echoip
```

### Configuration

Configuration is managed in the `etc/echoip/config.toml` file. This file should be located in the `/etc` folder on your server ( /etc/echoip/config.toml ). If you have the project on your server, you can run `make install-config` to copy it the right location.

```toml
Listen = ":8080"
TemplateDir = "html" # The directory of the template files ( eg, index.html )
RedisUrl = "redis://localhost:6379" # Redis Connection URL, leave blank for no Cache
CacheTtl = 3600 # in seconds
ReverseLookup = true
PortLookup = true
ShowSponsor = true
Database = "ipstack" # use "IP Stack" or "GeoIP"
TrustedHeaders = [] # Which header to trust, eg, `["X-Real-IP"]`
Profile = false # enable debug / profiling

[IPStack]
ApiKey = ""
UseHttps = true
EnableSecurity = true

[GeoIP]
CountryFile = ""
CityFile = ""
AsnFile = ""
```

### Caching with Redis

You can connect EchoIP to a Redis client to cache each request per IP. You can configure the life of the key in `config.CacheTtl`.

### Running with `systemd`

There is a systemd service file you can install in `/etc/systemd`.
30 changes: 30 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cache

import (
"context"

parser "github.com/levelsoftware/echoip/iputil/paser"
)

type CachedResponse struct {
response *parser.Response
}

func (cr *CachedResponse) Build(response parser.Response) CachedResponse {
return CachedResponse{
response: &response,
}
}

func (cr *CachedResponse) Get() parser.Response {
return *cr.response
}

func (cr *CachedResponse) IsSet() bool {
return cr.response != nil
}

type Cache interface {
Get(ctx context.Context, ip string, cachedResponse *CachedResponse) error
Set(ctx context.Context, ip string, response CachedResponse, cacheTtl int) error
}
15 changes: 15 additions & 0 deletions cache/null.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cache

import (
"context"
)

type Null struct{}

func (nc *Null) Get(ctx context.Context, ip string, cachedResponse *CachedResponse) error {
return nil
}

func (nc *Null) Set(ctx context.Context, ip string, response CachedResponse, cacheTtl int) error {
return nil
}
41 changes: 41 additions & 0 deletions cache/redis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cache

import (
"context"
"time"

"github.com/go-redis/cache/v9"
"github.com/redis/go-redis/v9"
)

type Redis struct {
cache *cache.Cache
}

func RedisCache(url string) (Redis, error) {
opts, err := redis.ParseURL(url)
if err != nil {
return Redis{}, err
}
rdb := redis.NewClient(opts)
cache := cache.New(&cache.Options{
Redis: rdb,
LocalCache: cache.NewTinyLFU(1000, time.Minute),
})
return Redis{
cache: cache,
}, nil
}

func (r *Redis) Get(ctx context.Context, ip string, cachedResponse *CachedResponse) error {
return r.cache.Get(ctx, ip, cachedResponse)
}

func (r *Redis) Set(ctx context.Context, ip string, response CachedResponse, cacheTtl int) error {
return r.cache.Set(&cache.Item{
Ctx: ctx,
Key: ip,
Value: response,
TTL: time.Duration(cacheTtl * int(time.Second)),
})
}
Loading