forked from wgroeneveld/go-jamming
added more readme, forward IP header checks in limiter
This commit is contained in:
parent
bc525c5b40
commit
9b46138489
57
README.md
57
README.md
|
@ -28,19 +28,59 @@ Well, that's easy!
|
||||||
3. ???
|
3. ???
|
||||||
4. Profit!
|
4. Profit!
|
||||||
|
|
||||||
|
It's very much a fire-and-forget thing. Put it behind a reverse proxy such as nginx using something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
|
||||||
|
server_name [your-domain];
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_pass http://127.0.0.1:[your-port];
|
||||||
|
}
|
||||||
|
ssl_certificate /etc/letsencrypt/live/[your-domain]/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/[your-domain]/privkey.pem;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a very simple Linux system service that fires up the jam:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Go-Jamming
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=[myuser]
|
||||||
|
WorkingDirectory=/var/www/gojamming
|
||||||
|
ExecStart=/var/www/gojamming/go-jamming
|
||||||
|
SuccessExitStatus=0
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Now install using `sudo systemctl enable/install gojamming` and you're done!
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Place a `config.json` file in the same directory that looks like this:
|
Place a `config.json` file in the same directory that looks like this: (below are the default values)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"port": 1337,
|
"port": 1337,
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"token": "sometoken",
|
"token": "miauwkes",
|
||||||
"dataPath": "data",
|
"dataPath": "data",
|
||||||
"utcOffset": 60,
|
"utcOffset": 60,
|
||||||
"allowedWebmentionSources": [
|
"allowedWebmentionSources": [
|
||||||
"blah.com"
|
"brainbaking.com",
|
||||||
|
"jefklakscodex.com"
|
||||||
],
|
],
|
||||||
"disallowedWebmentionDomains": [
|
"disallowedWebmentionDomains": [
|
||||||
"youtube.com"
|
"youtube.com"
|
||||||
|
@ -55,6 +95,8 @@ Place a `config.json` file in the same directory that looks like this:
|
||||||
|
|
||||||
If a config file is missing, or required keys are missing, a warning will be generated and default values will be used instead. See `common/config.go`.
|
If a config file is missing, or required keys are missing, a warning will be generated and default values will be used instead. See `common/config.go`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## What's in it?
|
## What's in it?
|
||||||
|
|
||||||
### 1. Webmentions
|
### 1. Webmentions
|
||||||
|
@ -131,3 +173,12 @@ Will result in a `200 OK` - that returns XML according to [The W3 pingback XML-R
|
||||||
|
|
||||||
Happens automatically through `PUT /webmention/:domain/:token`! Links that are discovered as `rel="pingback"` that **do not** already have a webmention link will be processed as XML-RPC requests to be send.
|
Happens automatically through `PUT /webmention/:domain/:token`! Links that are discovered as `rel="pingback"` that **do not** already have a webmention link will be processed as XML-RPC requests to be send.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
Run in verbose mode: use `-versbose`. This also logs debug info. Structured JSON is outputted through os.Stderr - which is usually `/var/log/syslog`.
|
||||||
|
|
||||||
|
If rolling files in a separate location is required, [lumberjack](https://github.com/natefinch/lumberjack) could be added in `main.go`.
|
||||||
|
|
||||||
|
There's a **rate limiting** system implemented with a rate limit of 5 requests per second and a maximum burst rate of 10.
|
||||||
|
That's pretty flexible. I have not taken the trouble to put this into the config, it should do in most cases. If you get a `429 too many requests`, you've hit the limiter.
|
||||||
|
A separate goroutine cleans up ips each 2 minutes, the TTL is 5 minuts. See `limiter.go`.
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -76,7 +77,7 @@ func (rl *RateLimiter) cleanupVisitors() {
|
||||||
// with the help of https://www.alexedwards.net/blog/how-to-rate-limit-http-requests, TY!
|
// with the help of https://www.alexedwards.net/blog/how-to-rate-limit-http-requests, TY!
|
||||||
func (rl *RateLimiter) limiterMiddleware(next http.Handler) http.Handler {
|
func (rl *RateLimiter) limiterMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ip := r.RemoteAddr // also contains port, but don't care
|
ip := rl.guessIp(r)
|
||||||
limiter := rl.getVisitor(ip)
|
limiter := rl.getVisitor(ip)
|
||||||
|
|
||||||
if limiter.Allow() == false {
|
if limiter.Allow() == false {
|
||||||
|
@ -88,3 +89,15 @@ func (rl *RateLimiter) limiterMiddleware(next http.Handler) http.Handler {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rl *RateLimiter) guessIp(r *http.Request) string {
|
||||||
|
realIp := r.Header.Get("X-Real-IP")
|
||||||
|
forwardedFor := r.Header.Get("X-Forwarded-For")
|
||||||
|
if realIp != "" { // in case of proxy. is IP itself
|
||||||
|
return realIp
|
||||||
|
}
|
||||||
|
if forwardedFor != "" { // in case of proxy. Could be: clientip, proxy1, proxy2, ...
|
||||||
|
return strings.Split(forwardedFor, ",")[0]
|
||||||
|
}
|
||||||
|
return r.RemoteAddr // also contains port, but don't care
|
||||||
|
}
|
||||||
|
|
|
@ -43,5 +43,5 @@ func Start() {
|
||||||
r.Use(NewRateLimiter(5, 10).Middleware)
|
r.Use(NewRateLimiter(5, 10).Middleware)
|
||||||
|
|
||||||
log.Info().Int("port", server.conf.Port).Msg("Serving...")
|
log.Info().Int("port", server.conf.Port).Msg("Serving...")
|
||||||
http.ListenAndServe(":"+strconv.Itoa(server.conf.Port), nil)
|
log.Fatal().Err(http.ListenAndServe(":"+strconv.Itoa(server.conf.Port), nil))
|
||||||
}
|
}
|
||||||
|
|
13
main.go
13
main.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"brainbaking.com/go-jamming/app"
|
"brainbaking.com/go-jamming/app"
|
||||||
|
@ -11,8 +12,16 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||||
// TODO this should only be enabled in local mode. Fix with config?
|
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
verboseFlag := flag.Bool("verbose", false, "Verbose mode (pretty print log, debug level)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// logs by default to Stderr (/var/log/syslog). Rolling files possible via lumberjack.
|
||||||
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||||
|
if *verboseFlag == true {
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Let's a go!")
|
log.Debug().Msg("Let's a go!")
|
||||||
app.Start()
|
app.Start()
|
||||||
|
|
Loading…
Reference in New Issue