Skip to content

Commit

Permalink
Add x-forwarded-for trust level option (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbrazier committed Aug 18, 2019
1 parent 8de2504 commit ffda301
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 71 deletions.
45 changes: 23 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,29 @@ POSTGRES_PASS=password HOSTS=localhost APP_URI=http://localhost:3000 go run serv

## Enviroment Configuration

| Env Var | Required | Default | Example | Description |
| ---------------------- | -------- | -------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------ |
| `HOSTS` | yes | | go.domain.com,go2.domain.com | List of comma separated hosts that the server will be able to be accessed from |
| `BLOCKED_HOSTS` | | | go.domain.com,go2.domain.com | List of hosts you want to block from being linked - HOSTS are already included to stop recursive calls |
| `APP_URI` | yes | | https://go.domain.com | Default URI of app - used to link back to app |
| `PORT` | | 1323 | | Port the app will run on |
| `DEBUG` | | false | | Enable more logging |
| `JSON_LOGS` | | false | | Use JSON logs where possible |
| `POSTGRES_ADDR` | | localhost:5432 | | Postgres db address |
| `POSTGRES_DATABASE` | | go | | Postgres db name |
| `POSTGRES_USER` | | postgres | | Postgres user |
| `POSTGRES_PASS` | | password | | Postgres password |
| `SLACK_TOKEN` | | | xoxb-xxxxxxxxx-xxxxxxxx-xxxx | Slack OAuth token to enable slackbot |
| `SLACK_SIGNING_SECRET` | | | xxxxxxxxxxx | Slack signing secret to enable Slack `/go` command |
| `SLACK_TEAM_ID` | | | Txxxxxxxx | Slack team id to restrict slash command responses to single team |
| `ENABLE_AUTH` | | false | | Enable Azure auth or not - if enabled, all other fields must be filled in |
| `AD_TENANT_ID` | | | | Azure AD tenant ID |
| `AD_CLIENT_ID` | | | | Azure AD client ID |
| `AD_CLIENT_SECRET` | | | | Azure AD client secret |
| `SESSION_TOKEN` | | | | Secret session token to store the user sessions |
| `ALLOWED_IPS` | | | 110.1.10.2,1.1.22.1 | IP addresses that are always allowed access, even with auth enabled |
| `ALLOW_FORWARDED_FOR` | | false | | Retrieve origin IP from X-Forwarded-For header. Only enable if source is trusted, e.g. via Cloudfront |
| Env Var | Required | Default | Example | Description |
| --------------------------- | -------- | -------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------ |
| `HOSTS` | yes | | go.domain.com,go2.domain.com | List of comma separated hosts that the server will be able to be accessed from |
| `BLOCKED_HOSTS` | | | go.domain.com,go2.domain.com | List of hosts you want to block from being linked - HOSTS are already included to stop recursive calls |
| `APP_URI` | yes | | https://go.domain.com | Default URI of app - used to link back to app |
| `PORT` | | 1323 | | Port the app will run on |
| `DEBUG` | | false | | Enable more logging |
| `JSON_LOGS` | | false | | Use JSON logs where possible |
| `POSTGRES_ADDR` | | localhost:5432 | | Postgres db address |
| `POSTGRES_DATABASE` | | go | | Postgres db name |
| `POSTGRES_USER` | | postgres | | Postgres user |
| `POSTGRES_PASS` | | password | | Postgres password |
| `SLACK_TOKEN` | | | xoxb-xxxxxxxxx-xxxxxxxx-xxxx | Slack OAuth token to enable slackbot |
| `SLACK_SIGNING_SECRET` | | | xxxxxxxxxxx | Slack signing secret to enable Slack `/go` command |
| `SLACK_TEAM_ID` | | | Txxxxxxxx | Slack team id to restrict slash command responses to single team |
| `ENABLE_AUTH` | | false | | Enable Azure auth or not - if enabled, all other fields must be filled in |
| `AD_TENANT_ID` | | | | Azure AD tenant ID |
| `AD_CLIENT_ID` | | | | Azure AD client ID |
| `AD_CLIENT_SECRET` | | | | Azure AD client secret |
| `SESSION_TOKEN` | | | | Secret session token to store the user sessions |
| `ALLOWED_IPS` | | | 110.1.10.2,1.1.22.1 | IP addresses that are always allowed access, even with auth enabled |
| `ALLOW_FORWARDED_FOR` | | false | | Retrieve origin IP from X-Forwarded-For header. Only enable if source is trusted, e.g. via Cloudfront |
| `FORWARDED_FOR_TRUST_LEVEL` | | 1 | | Number of levels to trust X-Forwarded-For header - should map to number of proxies used |

## FAQ

Expand Down
79 changes: 45 additions & 34 deletions api/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,39 @@ import (

// Specification definition in env
type Specification struct {
Debug bool
JSONLogs bool `envconfig:"JSON_LOGS"`
Port int `default:"1323"`
EnableAuth bool `envconfig:"ENABLE_AUTH"`
ADTenantID string `envconfig:"AD_TENANT_ID"`
ADClientID string `envconfig:"AD_CLIENT_ID"`
ADClientSecret string `envconfig:"AD_CLIENT_SECRET"`
SessionToken string `envconfig:"SESSION_TOKEN"`
PostgresAddr string `envconfig:"POSTGRES_ADDR" default:"localhost:5432"`
PostgresDatabase string `envconfig:"POSTGRES_DATABASE" default:"go"`
PostgresUser string `envconfig:"POSTGRES_USER" default:"postgres"`
PostgresPass string `envconfig:"POSTGRES_PASS" default:"password"`
Hosts []string `required:"true"`
BlockedHosts []string `envconfig:"BLOCKED_HOSTS"`
AppURI string `envconfig:"APP_URI" required:"true"`
SlackToken string `envconfig:"SLACK_TOKEN"`
SlackSigningSecret string `envconfig:"SLACK_SIGNING_SECRET"`
SlackTeamID string `envconfig:"SLACK_TEAM_ID"`
AllowedIPs []string `envconfig:"ALLOWED_IPS"`
AllowForwardedFor bool `envconfig:"ALLOW_FORWARDED_FOR"`
Debug bool
JSONLogs bool `envconfig:"JSON_LOGS"`
Port int `default:"1323"`
EnableAuth bool `envconfig:"ENABLE_AUTH"`
ADTenantID string `envconfig:"AD_TENANT_ID"`
ADClientID string `envconfig:"AD_CLIENT_ID"`
ADClientSecret string `envconfig:"AD_CLIENT_SECRET"`
SessionToken string `envconfig:"SESSION_TOKEN"`
PostgresAddr string `envconfig:"POSTGRES_ADDR" default:"localhost:5432"`
PostgresDatabase string `envconfig:"POSTGRES_DATABASE" default:"go"`
PostgresUser string `envconfig:"POSTGRES_USER" default:"postgres"`
PostgresPass string `envconfig:"POSTGRES_PASS" default:"password"`
Hosts []string `required:"true"`
BlockedHosts []string `envconfig:"BLOCKED_HOSTS"`
AppURI string `envconfig:"APP_URI" required:"true"`
SlackToken string `envconfig:"SLACK_TOKEN"`
SlackSigningSecret string `envconfig:"SLACK_SIGNING_SECRET"`
SlackTeamID string `envconfig:"SLACK_TEAM_ID"`
AllowedIPs []string `envconfig:"ALLOWED_IPS"`
AllowForwardedFor bool `envconfig:"ALLOW_FORWARDED_FOR"`
ForwardedForTrustLevel int `envconfig:"FORWARDED_FOR_TRUST_LEVEL" default:"1"`
}

// Auth config
type Auth struct {
Enabled bool
ADTenantID string
ADClientID string
ADClientSecret string
SessionToken string
AllowedIPs []string
AllowForwardedFor bool
Enabled bool
ADTenantID string
ADClientID string
ADClientSecret string
SessionToken string
AllowedIPs []string
AllowForwardedFor bool
ForwardedForTrustLevel int
}

// Database config
Expand Down Expand Up @@ -68,6 +70,12 @@ type Config struct {
Slack Slack
}

func validateConfig(c Config) {
if c.Auth.ForwardedForTrustLevel < 1 {
log.Fatal("FORWARDED_FOR_TRUST_LEVEL must be greater than 0")
}
}

var config = Config{}

// Init loads the config from the env and sets defaults
Expand All @@ -82,13 +90,14 @@ func Init() {
config.Port = spec.Port
config.JSONLogs = spec.JSONLogs
config.Auth = Auth{
Enabled: spec.EnableAuth,
ADTenantID: spec.ADTenantID,
ADClientID: spec.ADClientID,
ADClientSecret: spec.ADClientSecret,
SessionToken: spec.SessionToken,
AllowedIPs: spec.AllowedIPs,
AllowForwardedFor: spec.AllowForwardedFor,
Enabled: spec.EnableAuth,
ADTenantID: spec.ADTenantID,
ADClientID: spec.ADClientID,
ADClientSecret: spec.ADClientSecret,
SessionToken: spec.SessionToken,
AllowedIPs: spec.AllowedIPs,
AllowForwardedFor: spec.AllowForwardedFor,
ForwardedForTrustLevel: spec.ForwardedForTrustLevel,
}
config.Database = Database{
Addr: spec.PostgresAddr,
Expand All @@ -105,6 +114,8 @@ func Init() {
}

config.BlockedHosts = append(spec.BlockedHosts, spec.Hosts...)

validateConfig(config)
}

// GetConfig returns the config
Expand Down
30 changes: 15 additions & 15 deletions api/handler/ip_restrict.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package handler

import (
"fmt"
"net/http"
"strings"

"github.com/alexbrazier/go-url/api/config"
"github.com/labstack/echo"
)

func ipAllowed(allowedIPs []string, ip string) bool {
func ipAllowed(allowedIPs, ips []string) bool {
for _, allowedIP := range allowedIPs {
if ip == allowedIP {
return true
for _, ip := range ips {
if strings.TrimSpace(ip) == allowedIP {
return true
}
}
}
return false
Expand All @@ -26,22 +27,21 @@ func ipWhitelisted(forwardedFor, remoteAddr string) bool {
return false
}

if appConfig.Auth.AllowForwardedFor {
forwardedFor := forwardedFor
fmt.Println(forwardedFor)
if forwardedFor != "" {
// Only the last ip in the X-Forwarded-For can be trusted
ips := strings.Split(forwardedFor, ",")
ip := strings.TrimSpace(ips[len(ips)-1])
if ipAllowed(allowedIPs, ip) {
return true
}
if appConfig.Auth.AllowForwardedFor && forwardedFor != "" {
ips := strings.Split(forwardedFor, ",")
pos := len(ips) - appConfig.Auth.ForwardedForTrustLevel
if pos < 0 {
pos = 0
}
trustedIps := ips[pos:]
if ipAllowed(allowedIPs, trustedIps) {
return true
}
}

remoteIP := strings.Split(remoteAddr, ":")[0]

if ipAllowed(allowedIPs, remoteIP) {
if ipAllowed(allowedIPs, []string{remoteIP}) {
return true
}
return false
Expand Down

0 comments on commit ffda301

Please sign in to comment.