Compare commits
7 Commits
v2.0.0-rc1
...
v2.0.0-rc3
Author | SHA1 | Date | |
---|---|---|---|
5a17187855 | |||
e7b567bc92 | |||
a4a34dcd76 | |||
d1b12e4ffb | |||
6f3ac5efe5 | |||
b0e4b6333d | |||
dd13f42ddf |
22
README.md
22
README.md
@ -16,7 +16,7 @@ A minimal forward authentication service that provides Google oauth based login
|
|||||||
|
|
||||||
# Contents
|
# Contents
|
||||||
|
|
||||||
- [Usage](#installation)
|
- [Usage](#usage)
|
||||||
- [Simple](#simple)
|
- [Simple](#simple)
|
||||||
- [Advanced](#advanced)
|
- [Advanced](#advanced)
|
||||||
- [OAuth Configuration](#oauth-configuration)
|
- [OAuth Configuration](#oauth-configuration)
|
||||||
@ -29,6 +29,8 @@ A minimal forward authentication service that provides Google oauth based login
|
|||||||
- [Operation Modes](#operation-modes)
|
- [Operation Modes](#operation-modes)
|
||||||
- [Overlay Mode](#overlay-mode)
|
- [Overlay Mode](#overlay-mode)
|
||||||
- [Auth Host Mode](#auth-host-mode)
|
- [Auth Host Mode](#auth-host-mode)
|
||||||
|
- [Copyright](#copyright)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:1
|
image: traefik:1.7
|
||||||
ports:
|
ports:
|
||||||
- "8085:80"
|
- "8085:80"
|
||||||
volumes:
|
volumes:
|
||||||
@ -96,7 +98,7 @@ Click "Create Credentials" > "OAuth client ID". Select "Web Application", fill i
|
|||||||
|
|
||||||
#### Upgrade Guide
|
#### Upgrade Guide
|
||||||
|
|
||||||
v2 was released in April 2019, whilst this is fully backwards compatibile, a number of configuration options were modified, please see the [upgrade guide](https://github.com/thomseddon/traefik-forward-auth/wiki/v2-Upgrade-Guide) to prevent warnings on startup and ensure you are using the current configuration.
|
v2 was released in April 2019, whilst this is fully backwards compatible, a number of configuration options were modified, please see the [upgrade guide](https://github.com/thomseddon/traefik-forward-auth/wiki/v2-Upgrade-Guide) to prevent warnings on startup and ensure you are using the current configuration.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@ -126,12 +128,12 @@ Application Options:
|
|||||||
--rules.<name>.<param>= Rule definitions, param can be: "action" or "rule"
|
--rules.<name>.<param>= Rule definitions, param can be: "action" or "rule"
|
||||||
|
|
||||||
Google Provider:
|
Google Provider:
|
||||||
--providers.google.client-id= Client ID [$CLIENT_ID]
|
--providers.google.client-id= Client ID [$PROVIDERS_GOOGLE_CLIENT_ID]
|
||||||
--providers.google.client-secret= Client Secret [$CLIENT_SECRET]
|
--providers.google.client-secret= Client Secret [$PROVIDERS_GOOGLE_CLIENT_SECRET]
|
||||||
--providers.google.prompt= Space separated list of OpenID prompt options [$PROMPT]
|
--providers.google.prompt= Space separated list of OpenID prompt options [$PROVIDERS_GOOGLE_PROMPT]
|
||||||
|
|
||||||
Help Options:
|
Help Options:
|
||||||
-h, --help Show this help message
|
-h, --help Show this help message
|
||||||
```
|
```
|
||||||
|
|
||||||
All options can be supplied in any of the following ways, in the following precedence (first is highest precedence):
|
All options can be supplied in any of the following ways, in the following precedence (first is highest precedence):
|
||||||
@ -283,7 +285,7 @@ The authenticated user is set in the `X-Forwarded-User` header, to pass this on
|
|||||||
|
|
||||||
### Operation Modes
|
### Operation Modes
|
||||||
|
|
||||||
#### Overlay
|
#### Overlay Mode
|
||||||
|
|
||||||
Overlay is the default operation mode, in this mode the authorisation endpoint is overlayed onto any domain. By default the `/_oauth` path is used, this can be customised using the `url-path` option.
|
Overlay is the default operation mode, in this mode the authorisation endpoint is overlayed onto any domain. By default the `/_oauth` path is used, this can be customised using the `url-path` option.
|
||||||
|
|
||||||
@ -298,7 +300,7 @@ The user flow will be:
|
|||||||
|
|
||||||
As the hostname in the `redirect_uri` is dynamically generated based on the original request, every hostname must be permitted in the Google OAuth console (e.g. `www.myappp.com` would need to be added in the above example)
|
As the hostname in the `redirect_uri` is dynamically generated based on the original request, every hostname must be permitted in the Google OAuth console (e.g. `www.myappp.com` would need to be added in the above example)
|
||||||
|
|
||||||
#### Auth Host
|
#### Auth Host Mode
|
||||||
|
|
||||||
This is an optional mode of operation that is useful when dealing with a large number of subdomains, it is activated by using the `auth-host` config option (see [this example docker-compose.yml](https://github.com/thomseddon/traefik-forward-auth/blob/master/examples/docker-compose-auth-host.yml)).
|
This is an optional mode of operation that is useful when dealing with a large number of subdomains, it is activated by using the `auth-host` config option (see [this example docker-compose.yml](https://github.com/thomseddon/traefik-forward-auth/blob/master/examples/docker-compose-auth-host.yml)).
|
||||||
|
|
||||||
@ -321,6 +323,8 @@ Two criteria must be met for an `auth-host` to be used:
|
|||||||
1. Request matches given `cookie-domain`
|
1. Request matches given `cookie-domain`
|
||||||
2. `auth-host` is also subdomain of same `cookie-domain`
|
2. `auth-host` is also subdomain of same `cookie-domain`
|
||||||
|
|
||||||
|
Please note: For Auth Host mode to work, you must ensure that requests to your auth-host are routed to the traefik-forward-auth container, as demonstrated with the service labels in the [docker-compose-auth.yml](https://github.com/thomseddon/traefik-forward-auth/blob/master/examples/docker-compose-auth-host.yml) example.
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
2018 Thom Seddon
|
2018 Thom Seddon
|
||||||
|
@ -33,7 +33,7 @@ services:
|
|||||||
- AUTH_HOST=auth.yourdomain.com
|
- AUTH_HOST=auth.yourdomain.com
|
||||||
networks:
|
networks:
|
||||||
- traefik
|
- traefik
|
||||||
# When using an auth host, adding it here prompts traefik to generate certs
|
# When using an auth host, the below must be added
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.port=4181
|
- traefik.port=4181
|
||||||
|
@ -23,13 +23,15 @@ services:
|
|||||||
- "traefik.frontend.rule=Host:whoami.localhost.com"
|
- "traefik.frontend.rule=Host:whoami.localhost.com"
|
||||||
|
|
||||||
traefik-forward-auth:
|
traefik-forward-auth:
|
||||||
image: thomseddon/traefik-forward-auth
|
build: ../
|
||||||
|
command: ./traefik-forward-auth --rule.1.action=allow --rule.1.rule="Path(`/`)"
|
||||||
environment:
|
environment:
|
||||||
- PROVIDERS_GOOGLE_CLIENT_ID=your-client-id
|
- PROVIDERS_GOOGLE_CLIENT_ID=your-client-id
|
||||||
- PROVIDERS_GOOGLE_CLIENT_SECRET=your-client-secret
|
- PROVIDERS_GOOGLE_CLIENT_SECRET=your-client-secret
|
||||||
- SECRET=something-random
|
- SECRET=something-random
|
||||||
- INSECURE_COOKIE=true
|
- INSECURE_COOKIE=true
|
||||||
- DOMAIN=yourcompany.com
|
- DOMAIN=yourcompany.com
|
||||||
|
- LOG_LEVEL=debug
|
||||||
networks:
|
networks:
|
||||||
- traefik
|
- traefik
|
||||||
|
|
||||||
|
3
go.mod
3
go.mod
@ -13,7 +13,7 @@ require (
|
|||||||
github.com/go-kit/kit v0.8.0 // indirect
|
github.com/go-kit/kit v0.8.0 // indirect
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
github.com/gorilla/context v1.1.1 // indirect
|
||||||
github.com/gravitational/trace v0.0.0-20190409171327-f30095ced5ff // indirect
|
github.com/gravitational/trace v0.0.0-20190409171327-f30095ced5ff // indirect
|
||||||
github.com/jessevdk/go-flags v1.4.0
|
github.com/jessevdk/go-flags v1.4.1-0.20181221193153-c0795c8afcf4
|
||||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
@ -25,6 +25,7 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.4.1
|
github.com/sirupsen/logrus v1.4.1
|
||||||
github.com/stretchr/objx v0.2.0 // indirect
|
github.com/stretchr/objx v0.2.0 // indirect
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
|
github.com/thomseddon/go-flags v1.4.1-0.20190507184247-a3629c504486
|
||||||
github.com/vulcand/predicate v1.1.0 // indirect
|
github.com/vulcand/predicate v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd // indirect
|
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd // indirect
|
||||||
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 // indirect
|
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 // indirect
|
||||||
|
8
go.sum
8
go.sum
@ -24,6 +24,8 @@ github.com/gravitational/trace v0.0.0-20190409171327-f30095ced5ff h1:xL/fJdlTJL6
|
|||||||
github.com/gravitational/trace v0.0.0-20190409171327-f30095ced5ff/go.mod h1:RvdOUHE4SHqR3oXlFFKnGzms8a5dugHygGw1bqDstYI=
|
github.com/gravitational/trace v0.0.0-20190409171327-f30095ced5ff/go.mod h1:RvdOUHE4SHqR3oXlFFKnGzms8a5dugHygGw1bqDstYI=
|
||||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jessevdk/go-flags v1.4.1-0.20181221193153-c0795c8afcf4 h1:xKkUL6QBojwguhKKetf1SocCAKqc6W7S/mGm9xEGllo=
|
||||||
|
github.com/jessevdk/go-flags v1.4.1-0.20181221193153-c0795c8afcf4/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
@ -53,6 +55,12 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/thomseddon/go-flags v1.4.0 h1:cHj56pbnQxlGo2lx2P8f0Dph4TRYKBJzoPuF2lqNvW4=
|
||||||
|
github.com/thomseddon/go-flags v1.4.0/go.mod h1:NK9eZpNBmSKVxvyB/MExg6jW0Bo9hQyAuCP+b8MJFow=
|
||||||
|
github.com/thomseddon/go-flags v1.4.1-0.20190507181358-ce437f05b7fb h1:L311/fJ7WXmFDDtuhf22PkVJqZpqLbEsmGSTEGv7ZQY=
|
||||||
|
github.com/thomseddon/go-flags v1.4.1-0.20190507181358-ce437f05b7fb/go.mod h1:NK9eZpNBmSKVxvyB/MExg6jW0Bo9hQyAuCP+b8MJFow=
|
||||||
|
github.com/thomseddon/go-flags v1.4.1-0.20190507184247-a3629c504486 h1:hk17f4niAl4e6viTj2uf/fpfACa6QPmrtMDAo+1tifE=
|
||||||
|
github.com/thomseddon/go-flags v1.4.1-0.20190507184247-a3629c504486/go.mod h1:NK9eZpNBmSKVxvyB/MExg6jW0Bo9hQyAuCP+b8MJFow=
|
||||||
github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg=
|
github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg=
|
||||||
github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg=
|
github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jessevdk/go-flags"
|
"github.com/thomseddon/go-flags"
|
||||||
"github.com/thomseddon/traefik-forward-auth/internal/provider"
|
"github.com/thomseddon/traefik-forward-auth/internal/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ type Config struct {
|
|||||||
LogFormat string `long:"log-format" env:"LOG_FORMAT" default:"text" choice:"text" choice:"json" choice:"pretty" description:"Log format"`
|
LogFormat string `long:"log-format" env:"LOG_FORMAT" default:"text" choice:"text" choice:"json" choice:"pretty" description:"Log format"`
|
||||||
|
|
||||||
AuthHost string `long:"auth-host" env:"AUTH_HOST" description:"Single host to use when returning from 3rd party auth"`
|
AuthHost string `long:"auth-host" env:"AUTH_HOST" description:"Single host to use when returning from 3rd party auth"`
|
||||||
Config func(s string) error `long:"config" env:"CONFIG" description:"Path to config file"`
|
Config func(s string) error `long:"config" env:"CONFIG" description:"Path to config file" json:"-"`
|
||||||
CookieDomains []CookieDomain `long:"cookie-domain" env:"COOKIE_DOMAIN" description:"Domain to set auth cookie on, can be set multiple times"`
|
CookieDomains []CookieDomain `long:"cookie-domain" env:"COOKIE_DOMAIN" description:"Domain to set auth cookie on, can be set multiple times"`
|
||||||
InsecureCookie bool `long:"insecure-cookie" env:"INSECURE_COOKIE" description:"Use insecure cookies"`
|
InsecureCookie bool `long:"insecure-cookie" env:"INSECURE_COOKIE" description:"Use insecure cookies"`
|
||||||
CookieName string `long:"cookie-name" env:"COOKIE_NAME" default:"_forward_auth" description:"Cookie Name"`
|
CookieName string `long:"cookie-name" env:"COOKIE_NAME" default:"_forward_auth" description:"Cookie Name"`
|
||||||
@ -34,23 +34,23 @@ type Config struct {
|
|||||||
Domains []string `long:"domain" env:"DOMAIN" description:"Only allow given email domains, can be set multiple times"`
|
Domains []string `long:"domain" env:"DOMAIN" description:"Only allow given email domains, can be set multiple times"`
|
||||||
LifetimeString int `long:"lifetime" env:"LIFETIME" default:"43200" description:"Lifetime in seconds"`
|
LifetimeString int `long:"lifetime" env:"LIFETIME" default:"43200" description:"Lifetime in seconds"`
|
||||||
Path string `long:"url-path" env:"URL_PATH" default:"/_oauth" description:"Callback URL Path"`
|
Path string `long:"url-path" env:"URL_PATH" default:"/_oauth" description:"Callback URL Path"`
|
||||||
SecretString string `long:"secret" env:"SECRET" description:"Secret used for signing (required)"`
|
SecretString string `long:"secret" env:"SECRET" description:"Secret used for signing (required)" json:"-"`
|
||||||
Whitelist CommaSeparatedList `long:"whitelist" env:"WHITELIST" description:"Only allow given email addresses, can be set multiple times"`
|
Whitelist CommaSeparatedList `long:"whitelist" env:"WHITELIST" description:"Only allow given email addresses, can be set multiple times"`
|
||||||
|
|
||||||
Providers provider.Providers `group:"providers" namespace:"providers" env-namespace:"PROVIDERS"`
|
Providers provider.Providers `group:"providers" namespace:"providers" env-namespace:"PROVIDERS"`
|
||||||
Rules map[string]*Rule `long:"rules.<name>.<param>" description:"Rule definitions, param can be: \"action\" or \"rule\""`
|
Rules map[string]*Rule `long:"rules.<name>.<param>" description:"Rule definitions, param can be: \"action\" or \"rule\""`
|
||||||
|
|
||||||
// Filled during transformations
|
// Filled during transformations
|
||||||
Secret []byte
|
Secret []byte `json:"-"`
|
||||||
Lifetime time.Duration
|
Lifetime time.Duration
|
||||||
|
|
||||||
// Legacy
|
// Legacy
|
||||||
CookieDomainsLegacy CookieDomains `long:"cookie-domains" env:"COOKIE_DOMAINS" description:"DEPRECATED - Use \"cookie-domain\""`
|
CookieDomainsLegacy CookieDomains `long:"cookie-domains" env:"COOKIE_DOMAINS" description:"DEPRECATED - Use \"cookie-domain\""`
|
||||||
CookieSecretLegacy string `long:"cookie-secret" env:"COOKIE_SECRET" description:"DEPRECATED - Use \"secret\""`
|
CookieSecretLegacy string `long:"cookie-secret" env:"COOKIE_SECRET" description:"DEPRECATED - Use \"secret\"" json:"-"`
|
||||||
CookieSecureLegacy string `long:"cookie-secure" env:"COOKIE_SECURE" description:"DEPRECATED - Use \"insecure-cookie\""`
|
CookieSecureLegacy string `long:"cookie-secure" env:"COOKIE_SECURE" description:"DEPRECATED - Use \"insecure-cookie\""`
|
||||||
DomainsLegacy CommaSeparatedList `long:"domains" env:"DOMAINS" description:"DEPRECATED - Use \"domain\""`
|
DomainsLegacy CommaSeparatedList `long:"domains" env:"DOMAINS" description:"DEPRECATED - Use \"domain\""`
|
||||||
ClientIdLegacy string `long:"client-id" env:"CLIENT_ID" group:"DEPs" description:"DEPRECATED - Use \"providers.google.client-id\""`
|
ClientIdLegacy string `long:"client-id" env:"CLIENT_ID" group:"DEPs" description:"DEPRECATED - Use \"providers.google.client-id\""`
|
||||||
ClientSecretLegacy string `long:"client-secret" env:"CLIENT_SECRET" description:"DEPRECATED - Use \"providers.google.client-id\""`
|
ClientSecretLegacy string `long:"client-secret" env:"CLIENT_SECRET" description:"DEPRECATED - Use \"providers.google.client-id\"" json:"-"`
|
||||||
PromptLegacy string `long:"prompt" env:"PROMPT" description:"DEPRECATED - Use \"providers.google.prompt\""`
|
PromptLegacy string `long:"prompt" env:"PROMPT" description:"DEPRECATED - Use \"providers.google.prompt\""`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +100,7 @@ func NewConfig(args []string) (Config, error) {
|
|||||||
|
|
||||||
// Backwards compatability
|
// Backwards compatability
|
||||||
if c.CookieSecretLegacy != "" && c.SecretString == "" {
|
if c.CookieSecretLegacy != "" && c.SecretString == "" {
|
||||||
|
fmt.Println("cookie-secret config option is deprecated, please use secret")
|
||||||
c.SecretString = c.CookieSecretLegacy
|
c.SecretString = c.CookieSecretLegacy
|
||||||
}
|
}
|
||||||
if c.ClientIdLegacy != "" {
|
if c.ClientIdLegacy != "" {
|
||||||
@ -109,9 +110,11 @@ func NewConfig(args []string) (Config, error) {
|
|||||||
c.Providers.Google.ClientSecret = c.ClientSecretLegacy
|
c.Providers.Google.ClientSecret = c.ClientSecretLegacy
|
||||||
}
|
}
|
||||||
if c.PromptLegacy != "" {
|
if c.PromptLegacy != "" {
|
||||||
|
fmt.Println("prompt config option is deprecated, please use providers.google.prompt")
|
||||||
c.Providers.Google.Prompt = c.PromptLegacy
|
c.Providers.Google.Prompt = c.PromptLegacy
|
||||||
}
|
}
|
||||||
if c.CookieSecureLegacy != "" {
|
if c.CookieSecureLegacy != "" {
|
||||||
|
fmt.Println("cookie-secure config option is deprecated, please use insecure-cookie")
|
||||||
secure, err := strconv.ParseBool(c.CookieSecureLegacy)
|
secure, err := strconv.ParseBool(c.CookieSecureLegacy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c, err
|
return c, err
|
||||||
@ -119,9 +122,11 @@ func NewConfig(args []string) (Config, error) {
|
|||||||
c.InsecureCookie = !secure
|
c.InsecureCookie = !secure
|
||||||
}
|
}
|
||||||
if len(c.CookieDomainsLegacy) > 0 {
|
if len(c.CookieDomainsLegacy) > 0 {
|
||||||
|
fmt.Println("cookie-domains config option is deprecated, please use cookie-domain")
|
||||||
c.CookieDomains = append(c.CookieDomains, c.CookieDomainsLegacy...)
|
c.CookieDomains = append(c.CookieDomains, c.CookieDomainsLegacy...)
|
||||||
}
|
}
|
||||||
if len(c.DomainsLegacy) > 0 {
|
if len(c.DomainsLegacy) > 0 {
|
||||||
|
fmt.Println("domains config option is deprecated, please use domain")
|
||||||
c.Domains = append(c.Domains, c.DomainsLegacy...)
|
c.Domains = append(c.Domains, c.DomainsLegacy...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +141,7 @@ func NewConfig(args []string) (Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) parseFlags(args []string) error {
|
func (c *Config) parseFlags(args []string) error {
|
||||||
p := flags.NewParser(c, flags.Default)
|
p := flags.NewParser(c, flags.Default|flags.IniUnknownOptionHandler)
|
||||||
p.UnknownOptionHandler = c.parseUnknownFlag
|
p.UnknownOptionHandler = c.parseUnknownFlag
|
||||||
|
|
||||||
i := flags.NewIniParser(p)
|
i := flags.NewIniParser(p)
|
||||||
@ -152,6 +157,7 @@ func (c *Config) parseFlags(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("config format deprecated, please use ini format")
|
||||||
return i.Parse(converted)
|
return i.Parse(converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +277,12 @@ func NewRule() *Rule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Rule) formattedRule() string {
|
||||||
|
// Traefik implements their own "Host" matcher and then offers "HostRegexp"
|
||||||
|
// to invoke the mux "Host" matcher. This ensures the mux version is used
|
||||||
|
return strings.ReplaceAll(r.Rule, "Host(", "HostRegexp(")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Rule) Validate() {
|
func (r *Rule) Validate() {
|
||||||
if r.Action != "auth" && r.Action != "allow" {
|
if r.Action != "auth" && r.Action != "allow" {
|
||||||
log.Fatal("invalid rule action, must be \"auth\" or \"allow\"")
|
log.Fatal("invalid rule action, must be \"auth\" or \"allow\"")
|
||||||
|
@ -165,6 +165,18 @@ func TestConfigParseIni(t *testing.T) {
|
|||||||
assert.Equal("inicookiename", c.CookieName, "should be read from ini file")
|
assert.Equal("inicookiename", c.CookieName, "should be read from ini file")
|
||||||
assert.Equal("csrfcookiename", c.CSRFCookieName, "should be read from ini file")
|
assert.Equal("csrfcookiename", c.CSRFCookieName, "should be read from ini file")
|
||||||
assert.Equal("/two", c.Path, "variable in second ini file should override first ini file")
|
assert.Equal("/two", c.Path, "variable in second ini file should override first ini file")
|
||||||
|
assert.Equal(map[string]*Rule{
|
||||||
|
"1": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "PathPrefix(`/one`)",
|
||||||
|
Provider: "google",
|
||||||
|
},
|
||||||
|
"two": {
|
||||||
|
Action: "auth",
|
||||||
|
Rule: "Host(`two.com`) && Path(`/two`)",
|
||||||
|
Provider: "google",
|
||||||
|
},
|
||||||
|
}, c.Rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigFileBackwardsCompatability(t *testing.T) {
|
func TestConfigFileBackwardsCompatability(t *testing.T) {
|
||||||
@ -181,10 +193,12 @@ func TestConfigFileBackwardsCompatability(t *testing.T) {
|
|||||||
func TestConfigParseEnvironment(t *testing.T) {
|
func TestConfigParseEnvironment(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
os.Setenv("COOKIE_NAME", "env_cookie_name")
|
os.Setenv("COOKIE_NAME", "env_cookie_name")
|
||||||
|
os.Setenv("PROVIDERS_GOOGLE_CLIENT_ID", "env_client_id")
|
||||||
c, err := NewConfig([]string{})
|
c, err := NewConfig([]string{})
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
assert.Equal("env_cookie_name", c.CookieName, "variable should be read from environment")
|
assert.Equal("env_cookie_name", c.CookieName, "variable should be read from environment")
|
||||||
|
assert.Equal("env_client_id", c.Providers.Google.ClientId, "namespace variable should be read from environment")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigTransformation(t *testing.T) {
|
func TestConfigTransformation(t *testing.T) {
|
||||||
|
@ -26,11 +26,11 @@ func (s *Server) buildRoutes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let's build a router
|
// Let's build a router
|
||||||
for _, rule := range config.Rules {
|
for name, rule := range config.Rules {
|
||||||
if rule.Action == "allow" {
|
if rule.Action == "allow" {
|
||||||
s.router.AddRoute(rule.Rule, 1, s.AllowHandler())
|
s.router.AddRoute(rule.formattedRule(), 1, s.AllowHandler(name))
|
||||||
} else {
|
} else {
|
||||||
s.router.AddRoute(rule.Rule, 1, s.AuthHandler())
|
s.router.AddRoute(rule.formattedRule(), 1, s.AuthHandler(name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,14 +39,16 @@ func (s *Server) buildRoutes() {
|
|||||||
|
|
||||||
// Add a default handler
|
// Add a default handler
|
||||||
if config.DefaultAction == "allow" {
|
if config.DefaultAction == "allow" {
|
||||||
s.router.NewRoute().Handler(s.AllowHandler())
|
s.router.NewRoute().Handler(s.AllowHandler("default"))
|
||||||
} else {
|
} else {
|
||||||
s.router.NewRoute().Handler(s.AuthHandler())
|
s.router.NewRoute().Handler(s.AuthHandler("default"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) RootHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) RootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// Modify request
|
// Modify request
|
||||||
|
r.Method = r.Header.Get("X-Forwarded-Method")
|
||||||
|
r.Host = r.Header.Get("X-Forwarded-Host")
|
||||||
r.URL, _ = url.Parse(r.Header.Get("X-Forwarded-Uri"))
|
r.URL, _ = url.Parse(r.Header.Get("X-Forwarded-Uri"))
|
||||||
|
|
||||||
// Pass to mux
|
// Pass to mux
|
||||||
@ -54,18 +56,18 @@ func (s *Server) RootHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handler that allows requests
|
// Handler that allows requests
|
||||||
func (s *Server) AllowHandler() http.HandlerFunc {
|
func (s *Server) AllowHandler(rule string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
s.logger(r, "Allowing request")
|
s.logger(r, rule, "Allowing request")
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate requests
|
// Authenticate requests
|
||||||
func (s *Server) AuthHandler() http.HandlerFunc {
|
func (s *Server) AuthHandler(rule string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Logging setup
|
// Logging setup
|
||||||
logger := s.logger(r, "Authenticating request")
|
logger := s.logger(r, rule, "Authenticating request")
|
||||||
|
|
||||||
// Get auth cookie
|
// Get auth cookie
|
||||||
c, err := r.Cookie(config.CookieName)
|
c, err := r.Cookie(config.CookieName)
|
||||||
@ -118,7 +120,7 @@ func (s *Server) AuthHandler() http.HandlerFunc {
|
|||||||
func (s *Server) AuthCallbackHandler() http.HandlerFunc {
|
func (s *Server) AuthCallbackHandler() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Logging setup
|
// Logging setup
|
||||||
logger := s.logger(r, "Handling callback")
|
logger := s.logger(r, "default", "Handling callback")
|
||||||
|
|
||||||
// Check for CSRF cookie
|
// Check for CSRF cookie
|
||||||
c, err := r.Cookie(config.CSRFCookieName)
|
c, err := r.Cookie(config.CSRFCookieName)
|
||||||
@ -165,16 +167,17 @@ func (s *Server) AuthCallbackHandler() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) logger(r *http.Request, msg string) *logrus.Entry {
|
func (s *Server) logger(r *http.Request, rule, msg string) *logrus.Entry {
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := log.WithFields(logrus.Fields{
|
logger := log.WithFields(logrus.Fields{
|
||||||
"SourceIP": r.Header.Get("X-Forwarded-For"),
|
"source_ip": r.Header.Get("X-Forwarded-For"),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Log request
|
// Log request
|
||||||
logger.WithFields(logrus.Fields{
|
logger.WithFields(logrus.Fields{
|
||||||
"Headers": r.Header,
|
"rule": rule,
|
||||||
}).Debugf(msg)
|
"headers": r.Header,
|
||||||
|
}).Debug(msg)
|
||||||
|
|
||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func TestServerAuthHandler(t *testing.T) {
|
|||||||
config, _ = NewConfig([]string{})
|
config, _ = NewConfig([]string{})
|
||||||
|
|
||||||
// Should redirect vanilla request to login url
|
// Should redirect vanilla request to login url
|
||||||
req := newHttpRequest("/foo")
|
req := newDefaultHttpRequest("/foo")
|
||||||
res, _ := doHttpRequest(req, nil)
|
res, _ := doHttpRequest(req, nil)
|
||||||
assert.Equal(307, res.StatusCode, "vanilla request should be redirected")
|
assert.Equal(307, res.StatusCode, "vanilla request should be redirected")
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ func TestServerAuthHandler(t *testing.T) {
|
|||||||
assert.Equal("/o/oauth2/auth", fwd.Path, "vanilla request should be redirected to google")
|
assert.Equal("/o/oauth2/auth", fwd.Path, "vanilla request should be redirected to google")
|
||||||
|
|
||||||
// Should catch invalid cookie
|
// Should catch invalid cookie
|
||||||
req = newHttpRequest("/foo")
|
req = newDefaultHttpRequest("/foo")
|
||||||
c := MakeCookie(req, "test@example.com")
|
c := MakeCookie(req, "test@example.com")
|
||||||
parts := strings.Split(c.Value, "|")
|
parts := strings.Split(c.Value, "|")
|
||||||
c.Value = fmt.Sprintf("bad|%s|%s", parts[1], parts[2])
|
c.Value = fmt.Sprintf("bad|%s|%s", parts[1], parts[2])
|
||||||
@ -51,7 +51,7 @@ func TestServerAuthHandler(t *testing.T) {
|
|||||||
assert.Equal(401, res.StatusCode, "invalid cookie should not be authorised")
|
assert.Equal(401, res.StatusCode, "invalid cookie should not be authorised")
|
||||||
|
|
||||||
// Should validate email
|
// Should validate email
|
||||||
req = newHttpRequest("/foo")
|
req = newDefaultHttpRequest("/foo")
|
||||||
c = MakeCookie(req, "test@example.com")
|
c = MakeCookie(req, "test@example.com")
|
||||||
config.Domains = []string{"test.com"}
|
config.Domains = []string{"test.com"}
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ func TestServerAuthHandler(t *testing.T) {
|
|||||||
assert.Equal(401, res.StatusCode, "invalid email should not be authorised")
|
assert.Equal(401, res.StatusCode, "invalid email should not be authorised")
|
||||||
|
|
||||||
// Should allow valid request email
|
// Should allow valid request email
|
||||||
req = newHttpRequest("/foo")
|
req = newDefaultHttpRequest("/foo")
|
||||||
c = MakeCookie(req, "test@example.com")
|
c = MakeCookie(req, "test@example.com")
|
||||||
config.Domains = []string{}
|
config.Domains = []string{}
|
||||||
|
|
||||||
@ -91,18 +91,18 @@ func TestServerAuthCallback(t *testing.T) {
|
|||||||
config.Providers.Google.UserURL = userUrl
|
config.Providers.Google.UserURL = userUrl
|
||||||
|
|
||||||
// Should pass auth response request to callback
|
// Should pass auth response request to callback
|
||||||
req := newHttpRequest("/_oauth")
|
req := newDefaultHttpRequest("/_oauth")
|
||||||
res, _ := doHttpRequest(req, nil)
|
res, _ := doHttpRequest(req, nil)
|
||||||
assert.Equal(401, res.StatusCode, "auth callback without cookie shouldn't be authorised")
|
assert.Equal(401, res.StatusCode, "auth callback without cookie shouldn't be authorised")
|
||||||
|
|
||||||
// Should catch invalid csrf cookie
|
// Should catch invalid csrf cookie
|
||||||
req = newHttpRequest("/_oauth?state=12345678901234567890123456789012:http://redirect")
|
req = newDefaultHttpRequest("/_oauth?state=12345678901234567890123456789012:http://redirect")
|
||||||
c := MakeCSRFCookie(req, "nononononononononononononononono")
|
c := MakeCSRFCookie(req, "nononononononononononononononono")
|
||||||
res, _ = doHttpRequest(req, c)
|
res, _ = doHttpRequest(req, c)
|
||||||
assert.Equal(401, res.StatusCode, "auth callback with invalid cookie shouldn't be authorised")
|
assert.Equal(401, res.StatusCode, "auth callback with invalid cookie shouldn't be authorised")
|
||||||
|
|
||||||
// Should redirect valid request
|
// Should redirect valid request
|
||||||
req = newHttpRequest("/_oauth?state=12345678901234567890123456789012:http://redirect")
|
req = newDefaultHttpRequest("/_oauth?state=12345678901234567890123456789012:http://redirect")
|
||||||
c = MakeCSRFCookie(req, "12345678901234567890123456789012")
|
c = MakeCSRFCookie(req, "12345678901234567890123456789012")
|
||||||
res, _ = doHttpRequest(req, c)
|
res, _ = doHttpRequest(req, c)
|
||||||
assert.Equal(307, res.StatusCode, "valid auth callback should be allowed")
|
assert.Equal(307, res.StatusCode, "valid auth callback should be allowed")
|
||||||
@ -117,33 +117,151 @@ func TestServerDefaultAction(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
config, _ = NewConfig([]string{})
|
config, _ = NewConfig([]string{})
|
||||||
|
|
||||||
req := newHttpRequest("/random")
|
req := newDefaultHttpRequest("/random")
|
||||||
res, _ := doHttpRequest(req, nil)
|
res, _ := doHttpRequest(req, nil)
|
||||||
assert.Equal(307, res.StatusCode, "request should require auth with auth default handler")
|
assert.Equal(307, res.StatusCode, "request should require auth with auth default handler")
|
||||||
|
|
||||||
config.DefaultAction = "allow"
|
config.DefaultAction = "allow"
|
||||||
req = newHttpRequest("/random")
|
req = newDefaultHttpRequest("/random")
|
||||||
res, _ = doHttpRequest(req, nil)
|
res, _ = doHttpRequest(req, nil)
|
||||||
assert.Equal(200, res.StatusCode, "request should be allowed with default handler")
|
assert.Equal(200, res.StatusCode, "request should be allowed with default handler")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerRoutePathPrefix(t *testing.T) {
|
func TestServerRouteHeaders(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
config, _ = NewConfig([]string{})
|
config, _ = NewConfig([]string{})
|
||||||
config.Rules = map[string]*Rule{
|
config.Rules = map[string]*Rule{
|
||||||
"web1": {
|
"1": {
|
||||||
Action: "allow",
|
Action: "allow",
|
||||||
Rule: "PathPrefix(`/api`)",
|
Rule: "Headers(`X-Test`, `test123`)",
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "HeadersRegexp(`X-Test`, `test(456|789)`)",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should block any request
|
// Should block any request
|
||||||
req := newHttpRequest("/random")
|
req := newDefaultHttpRequest("/random")
|
||||||
|
req.Header.Add("X-Random", "hello")
|
||||||
|
res, _ := doHttpRequest(req, nil)
|
||||||
|
assert.Equal(307, res.StatusCode, "request not matching any rule should require auth")
|
||||||
|
|
||||||
|
// Should allow matching
|
||||||
|
req = newDefaultHttpRequest("/api")
|
||||||
|
req.Header.Add("X-Test", "test123")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
|
||||||
|
// Should allow matching
|
||||||
|
req = newDefaultHttpRequest("/api")
|
||||||
|
req.Header.Add("X-Test", "test789")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerRouteHost(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
config, _ = NewConfig([]string{})
|
||||||
|
config.Rules = map[string]*Rule{
|
||||||
|
"1": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "Host(`api.example.com`)",
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "HostRegexp(`sub{num:[0-9]}.example.com`)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should block any request
|
||||||
|
req := newHttpRequest("GET", "https://example.com/", "/")
|
||||||
|
res, _ := doHttpRequest(req, nil)
|
||||||
|
assert.Equal(307, res.StatusCode, "request not matching any rule should require auth")
|
||||||
|
|
||||||
|
// Should allow matching request
|
||||||
|
req = newHttpRequest("GET", "https://api.example.com/", "/")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
|
||||||
|
// Should allow matching request
|
||||||
|
req = newHttpRequest("GET", "https://sub8.example.com/", "/")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerRouteMethod(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
config, _ = NewConfig([]string{})
|
||||||
|
config.Rules = map[string]*Rule{
|
||||||
|
"1": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "Method(`PUT`)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should block any request
|
||||||
|
req := newHttpRequest("GET", "https://example.com/", "/")
|
||||||
|
res, _ := doHttpRequest(req, nil)
|
||||||
|
assert.Equal(307, res.StatusCode, "request not matching any rule should require auth")
|
||||||
|
|
||||||
|
// Should allow matching request
|
||||||
|
req = newHttpRequest("PUT", "https://example.com/", "/")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerRoutePath(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
config, _ = NewConfig([]string{})
|
||||||
|
config.Rules = map[string]*Rule{
|
||||||
|
"1": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "Path(`/api`)",
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "PathPrefix(`/private`)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should block any request
|
||||||
|
req := newDefaultHttpRequest("/random")
|
||||||
res, _ := doHttpRequest(req, nil)
|
res, _ := doHttpRequest(req, nil)
|
||||||
assert.Equal(307, res.StatusCode, "request not matching any rule should require auth")
|
assert.Equal(307, res.StatusCode, "request not matching any rule should require auth")
|
||||||
|
|
||||||
// Should allow /api request
|
// Should allow /api request
|
||||||
req = newHttpRequest("/api")
|
req = newDefaultHttpRequest("/api")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
|
||||||
|
// Should allow /private request
|
||||||
|
req = newDefaultHttpRequest("/private")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
|
||||||
|
req = newDefaultHttpRequest("/private/path")
|
||||||
|
res, _ = doHttpRequest(req, nil)
|
||||||
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerRouteQuery(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
config, _ = NewConfig([]string{})
|
||||||
|
config.Rules = map[string]*Rule{
|
||||||
|
"1": {
|
||||||
|
Action: "allow",
|
||||||
|
Rule: "Query(`q=test123`)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should block any request
|
||||||
|
req := newHttpRequest("GET", "https://example.com/", "/?q=no")
|
||||||
|
res, _ := doHttpRequest(req, nil)
|
||||||
|
assert.Equal(307, res.StatusCode, "request not matching any rule should require auth")
|
||||||
|
|
||||||
|
// Should allow matching request
|
||||||
|
req = newHttpRequest("GET", "https://api.example.com/", "/?q=test123")
|
||||||
res, _ = doHttpRequest(req, nil)
|
res, _ = doHttpRequest(req, nil)
|
||||||
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
assert.Equal(200, res.StatusCode, "request matching allow rule should be allowed")
|
||||||
}
|
}
|
||||||
@ -194,8 +312,15 @@ func doHttpRequest(r *http.Request, c *http.Cookie) (*http.Response, string) {
|
|||||||
return res, string(body)
|
return res, string(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHttpRequest(uri string) *http.Request {
|
func newDefaultHttpRequest(uri string) *http.Request {
|
||||||
r := httptest.NewRequest("", "http://example.com/", nil)
|
return newHttpRequest("", "http://example.com/", uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHttpRequest(method, dest, uri string) *http.Request {
|
||||||
|
r := httptest.NewRequest("", "http://should-use-x-forwarded.com", nil)
|
||||||
|
p, _ := url.Parse(dest)
|
||||||
|
r.Header.Add("X-Forwarded-Method", method)
|
||||||
|
r.Header.Add("X-Forwarded-Host", p.Host)
|
||||||
r.Header.Add("X-Forwarded-Uri", uri)
|
r.Header.Add("X-Forwarded-Uri", uri)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
cookie-name=inicookiename
|
cookie-name=inicookiename
|
||||||
csrf-cookie-name=inicsrfcookiename
|
csrf-cookie-name=inicsrfcookiename
|
||||||
url-path=one
|
url-path=one
|
||||||
|
rule.1.action=allow
|
||||||
|
rule.1.rule=PathPrefix(`/one`)
|
||||||
|
@ -1 +1,3 @@
|
|||||||
url-path=two
|
url-path=two
|
||||||
|
rule.two.action=auth
|
||||||
|
rule.two.rule=Host(`two.com`) && Path(`/two`)
|
||||||
|
Reference in New Issue
Block a user