Switch options parsing to viper

This commit is contained in:
Travis Groth 2019-05-20 18:39:13 -04:00
parent 702cc30b77
commit febf9464a4
18 changed files with 798 additions and 326 deletions

View file

@ -20,25 +20,10 @@ func ValidateOptions(o *config.Options) error {
if len(decoded) != 32 { if len(decoded) != 32 {
return fmt.Errorf("authorize: `SHARED_SECRET` want 32 but got %d bytes", len(decoded)) return fmt.Errorf("authorize: `SHARED_SECRET` want 32 but got %d bytes", len(decoded))
} }
if o.Policy == "" && o.PolicyFile == "" { if len(o.Policies) == 0 {
return errors.New("authorize: either `POLICY` or `POLICY_FILE` must be non-nil") return errors.New("missing setting: no policies defined")
}
if o.Policy != "" {
confBytes, err := base64.StdEncoding.DecodeString(o.Policy)
if err != nil {
return fmt.Errorf("authorize: `POLICY` is invalid base64 %v", err)
}
_, err = policy.FromConfig(confBytes)
if err != nil {
return fmt.Errorf("authorize: `POLICY` %v", err)
}
}
if o.PolicyFile != "" {
_, err = policy.FromConfigFile(o.PolicyFile)
if err != nil {
return fmt.Errorf("authorize: `POLICY_FILE` %v", err)
}
} }
return nil return nil
} }
@ -61,17 +46,10 @@ func New(opts *config.Options) (*Authorize, error) {
} }
// errors handled by validate // errors handled by validate
sharedKey, _ := base64.StdEncoding.DecodeString(opts.SharedKey) sharedKey, _ := base64.StdEncoding.DecodeString(opts.SharedKey)
var policies []policy.Policy
if opts.Policy != "" {
confBytes, _ := base64.StdEncoding.DecodeString(opts.Policy)
policies, _ = policy.FromConfig(confBytes)
} else {
policies, _ = policy.FromConfigFile(opts.PolicyFile)
}
return &Authorize{ return &Authorize{
SharedKey: string(sharedKey), SharedKey: string(sharedKey),
identityAccess: NewIdentityWhitelist(policies), identityAccess: NewIdentityWhitelist(opts.Policies),
}, nil }, nil
} }

View file

@ -1,52 +1,36 @@
package authorize package authorize
import ( import (
"io/ioutil"
"log"
"os"
"testing" "testing"
"github.com/pomerium/pomerium/internal/config" "github.com/pomerium/pomerium/internal/config"
"github.com/pomerium/pomerium/internal/policy"
) )
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
t.Parallel() t.Parallel()
content := []byte(`[{"from": "pomerium.io","to":"httpbin.org"}]`)
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write(content); err != nil { goodPolicy := policy.Policy{From: "pomerium.io", To: "httpbin.org"}
log.Fatal(err) goodPolicy.Validate()
} policies := []policy.Policy{
if err := tmpfile.Close(); err != nil { goodPolicy,
log.Fatal(err)
} }
tests := []struct { tests := []struct {
name string name string
SharedKey string SharedKey string
Policy string Policies []policy.Policy
PolicyFile string wantErr bool
wantErr bool
}{ }{
{"good", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "WwogIHsKICAgICJyb3V0ZXMiOiAiaHR0cDovL3BvbWVyaXVtLmlvIgogIH0KXQ==", "", false}, {"good", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", policies, false},
{"bad shared secret", "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==", "WwogIHsKICAgICJyb3V0ZXMiOiAiaHR0cDovL3BvbWVyaXVtLmlvIgogIH0KXQ==", "", true}, {"bad shared secret", "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==", policies, true},
{"really bad shared secret", "sup", "WwogIHsKICAgICJyb3V0ZXMiOiAiaHR0cDovL3BvbWVyaXVtLmlvIgogIH0KXQ==", "", true}, {"really bad shared secret", "sup", policies, true},
{"bad base64 policy", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "WwogIHsKICAgICJyb3V0ZXMiOiAiaHR0cDovL3BvbWVyaXVtLmlvIgogIH0KXQ^=", "", true}, {"validation error, short secret", "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==", policies, true},
{"bad json", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "e30=", "", true}, {"nil options", "", []policy.Policy{}, true}, // special case
{"no policies", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "", "", true},
{"good policy file", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "", "./testdata/basic.json", true},
{"bad policy file, directory", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "", "./testdata/", true},
{"good policy", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "WwogIHsKICAgICJyb3V0ZXMiOiAiaHR0cDovL3BvbWVyaXVtLmlvIgogIH0KXQ==", "", false},
{"good file", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", "", tmpfile.Name(), false},
{"validation error, short secret", "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==", "", "", true},
{"nil options", "", "", "", true}, // special case
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
o := &config.Options{SharedKey: tt.SharedKey, Policy: tt.Policy, PolicyFile: tt.PolicyFile} o := &config.Options{SharedKey: tt.SharedKey, Policies: tt.Policies}
if tt.name == "nil options" { if tt.name == "nil options" {
o = nil o = nil
} }

View file

@ -24,6 +24,7 @@ import (
) )
var versionFlag = flag.Bool("version", false, "prints the version") var versionFlag = flag.Bool("version", false, "prints the version")
var configFile = flag.String("config", "", "Specify configuration file location")
func main() { func main() {
flag.Parse() flag.Parse()
@ -31,7 +32,7 @@ func main() {
fmt.Println(version.FullVersion()) fmt.Println(version.FullVersion())
os.Exit(0) os.Exit(0)
} }
opt, err := config.OptionsFromEnvConfig() opt, err := parseOptions(*configFile)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("cmd/pomerium: options") log.Fatal().Err(err).Msg("cmd/pomerium: options")
} }
@ -169,8 +170,8 @@ func wrapMiddleware(o *config.Options, mux *http.ServeMux) http.Handler {
return c.Then(mux) return c.Then(mux)
} }
func parseOptions() (*config.Options, error) { func parseOptions(configFile string) (*config.Options, error) {
o, err := config.OptionsFromEnvConfig() o, err := config.OptionsFromViper(configFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -11,6 +11,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/pomerium/pomerium/internal/policy"
"github.com/pomerium/pomerium/internal/config" "github.com/pomerium/pomerium/internal/config"
"github.com/pomerium/pomerium/internal/middleware" "github.com/pomerium/pomerium/internal/middleware"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -110,7 +112,6 @@ func Test_newAuthorizeService(t *testing.T) {
}{ }{
{"wrong service", "proxy", "", "", false}, {"wrong service", "proxy", "", "", false},
{"bad option parsing", "authorize", "SharedKey", "false", true}, {"bad option parsing", "authorize", "SharedKey", "false", true},
{"bad env", "authorize", "Policy", "error!", true},
{"good", "authorize", "SharedKey", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", false}, {"good", "authorize", "SharedKey", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", false},
} }
for _, tt := range tests { for _, tt := range tests {
@ -118,7 +119,11 @@ func Test_newAuthorizeService(t *testing.T) {
testOpts := config.NewOptions() testOpts := config.NewOptions()
testOpts.Services = tt.s testOpts.Services = tt.s
testOpts.CookieSecret = "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=" testOpts.CookieSecret = "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM="
testOpts.Policy = "LSBmcm9tOiBodHRwYmluLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vaHR0cGJpbgogIGFsbG93ZWRfZG9tYWluczoKICAgIC0gcG9tZXJpdW0uaW8KICBjb3JzX2FsbG93X3ByZWZsaWdodDogdHJ1ZQogIHRpbWVvdXQ6IDMwcwotIGZyb206IGV4dGVybmFsLWh0dHBiaW4uY29ycC5iZXlvbmRwZXJpbWV0ZXIuY29tCiAgdG86IGh0dHBiaW4ub3JnCiAgYWxsb3dlZF9kb21haW5zOgogICAgLSBnbWFpbC5jb20KLSBmcm9tOiB3ZWlyZGx5c3NsLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vbmV2ZXJzc2wuY29tCiAgYWxsb3dlZF91c2VyczoKICAgIC0gYmRkQHBvbWVyaXVtLmlvCiAgYWxsb3dlZF9ncm91cHM6CiAgICAtIGFkbWlucwogICAgLSBkZXZlbG9wZXJzCi0gZnJvbTogaGVsbG8uY29ycC5iZXlvbmRwZXJpbWV0ZXIuY29tCiAgdG86IGh0dHA6Ly9oZWxsbzo4MDgwCiAgYWxsb3dlZF9ncm91cHM6CiAgICAtIGFkbWlucw==" testPolicy := policy.Policy{From: "pomerium.io", To: "httpbin.org"}
testPolicy.Validate()
testOpts.Policies = []policy.Policy{
testPolicy,
}
if tt.Field != "" { if tt.Field != "" {
testOptsField := reflect.ValueOf(testOpts).Elem().FieldByName(tt.Field) testOptsField := reflect.ValueOf(testOpts).Elem().FieldByName(tt.Field)
@ -146,16 +151,19 @@ func Test_newProxyeService(t *testing.T) {
}{ }{
{"wrong service", "authenticate", "", "", false}, {"wrong service", "authenticate", "", "", false},
{"bad option parsing", "proxy", "SharedKey", "false", true}, {"bad option parsing", "proxy", "SharedKey", "false", true},
{"bad env", "proxy", "Policy", "error!", true},
{"good", "proxy", "SharedKey", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", false}, {"good", "proxy", "SharedKey", "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=", false},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mux := http.NewServeMux() mux := http.NewServeMux()
testOpts := config.NewOptions() testOpts := config.NewOptions()
testPolicy := policy.Policy{From: "pomerium.io", To: "httpbin.org"}
testPolicy.Validate()
testOpts.Policies = []policy.Policy{
testPolicy,
}
testOpts.AuthenticateURL, _ = url.Parse("https://authenticate.example.com") testOpts.AuthenticateURL, _ = url.Parse("https://authenticate.example.com")
testOpts.AuthorizeURL, _ = url.Parse("https://authorize.example.com") testOpts.AuthorizeURL, _ = url.Parse("https://authorize.example.com")
testOpts.Policy = "LSBmcm9tOiBodHRwYmluLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vaHR0cGJpbgogIGFsbG93ZWRfZG9tYWluczoKICAgIC0gcG9tZXJpdW0uaW8KICBjb3JzX2FsbG93X3ByZWZsaWdodDogdHJ1ZQogIHRpbWVvdXQ6IDMwcwotIGZyb206IGV4dGVybmFsLWh0dHBiaW4uY29ycC5iZXlvbmRwZXJpbWV0ZXIuY29tCiAgdG86IGh0dHBiaW4ub3JnCiAgYWxsb3dlZF9kb21haW5zOgogICAgLSBnbWFpbC5jb20KLSBmcm9tOiB3ZWlyZGx5c3NsLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vbmV2ZXJzc2wuY29tCiAgYWxsb3dlZF91c2VyczoKICAgIC0gYmRkQHBvbWVyaXVtLmlvCiAgYWxsb3dlZF9ncm91cHM6CiAgICAtIGFkbWlucwogICAgLSBkZXZlbG9wZXJzCi0gZnJvbTogaGVsbG8uY29ycC5iZXlvbmRwZXJpbWV0ZXIuY29tCiAgdG86IGh0dHA6Ly9oZWxsbzo4MDgwCiAgYWxsb3dlZF9ncm91cHM6CiAgICAtIGFkbWlucw=="
testOpts.CookieSecret = "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=" testOpts.CookieSecret = "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM="
testOpts.Services = tt.s testOpts.Services = tt.s
@ -219,7 +227,7 @@ func Test_parseOptions(t *testing.T) {
os.Setenv(tt.envKey, tt.envValue) os.Setenv(tt.envKey, tt.envValue)
defer os.Unsetenv(tt.envKey) defer os.Unsetenv(tt.envKey)
got, err := parseOptions() got, err := parseOptions("")
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("parseOptions() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("parseOptions() error = %v, wantErr %v", err, tt.wantErr)
return return

25
config-policy-only.yaml Normal file
View file

@ -0,0 +1,25 @@
### Other keys configuration removed for clarity ###
# Proxied routes and per-route policies are defined in a policy block
policy: # Omit the 'policy' key if you are encoding it into an environment variable
- from: httpbin.corp.beyondperimeter.com
to: http://httpbin
allowed_domains:
- pomerium.io
cors_allow_preflight: true
timeout: 30s
- from: external-httpbin.corp.beyondperimeter.com
to: httpbin.org
allowed_domains:
- gmail.com
- from: weirdlyssl.corp.beyondperimeter.com
to: http://neverssl.com
allowed_users:
- bdd@pomerium.io
allowed_groups:
- admins
- developers
- from: hello.corp.beyondperimeter.com
to: http://hello:8080
allowed_groups:
- admins

89
config.example.yaml Normal file
View file

@ -0,0 +1,89 @@
# Main configuration flags
address: ":8443" # optional, default is 443
pomerium_debug: true # optional, default is false
service: "all" # optional, default is all
# log_level: "info" # optional, default is debug
authenticate_service_url: https://authenticate.corp.pomerium.io:8443
authorize_service_url: https://authorize.corp.pomerium.io:8443
# Certificates can be loaded as files or base64 encoded bytes. If neither is set, a
# pomerium will attempt to locate a pair in the root directory
certificate_file: "./cert.pem" # optional, defaults to `./cert.pem`
certificate_key_file: "./privkey.pem" # optional, defaults to `./certprivkey.pem`
certificate_authority_file: "./cert.pem"
# certificate: |
# "xxxxxx" # base64 encoded cert, eg. `base64 -i cert.pem`
# certificate_key: |
# "xxxx" # base64 encoded key, eg. `base64 -i privkey.pem`
# Generate 256 bit random keys e.g. `head -c32 /dev/urandom | base64`
shared_secret: hsJIQsx9KKx4qVlggg/T3AuLTmVu0uHhwTQgMPlVs7U=
cookie_secret: WwMtDXWaRDMBQCylle8OJ+w4kLIDIGd8W3cB4/zFFtg=
# If set, a JWT based signature is appended to each request header `x-pomerium-jwt-assertion`
# signing_key: "Replace with base64'd private key from ./scripts/self-signed-sign-key.sh"
# Identity Provider Settings
# Azure
# idp_provider: "azure"
# idp_provider_url: "https://login.microsoftonline.com/REPLACEME/v2.0"
# idp_client_id: "REPLACEME
# idp_client_secret: "REPLACEME"
# Gitlab
# idp_provider: "gitlab"
# idp_provider_url: "https://gitlab.onprem.example.com" # optional, defaults to `https://gitlab.com`
# idp_client_id: "REPLACEME
# idp_client_secret: "REPLACEME"
## GOOGLE
# idp_provider: "google"
# idp_provider_url: "https://accounts.google.com" # optional for google
# idp_client_id: "REPLACEME
# idp_client_secret: "REPLACEME
# IF GSUITE and you want to get user groups you will need to set a service account
# see identity provider docs for gooogle for more info :
# idp_service_account: $(echo '{"impersonate_user": "bdd@pomerium.io"}' | base64)
# OKTA
# idp_provider: "okta"
# idp_client_id: "REPLACEME"
# idp_client_secret: "replaceme"
# idp_provider_url: "https://REPLACEME.oktapreview.com/oauth2/default"
# OneLogin
# idp_provider: "onelogin"
# idp_client_id: "REPLACEME"
# idp_client_secret: "REPLACEME"
# idp_provider_url: "https://openid-connect.onelogin.com/oidc" #optional, defaults to `https://openid-connect.onelogin.com/oidc`
# scope: "openid email" # generally, you want the default OIDC scopes
# Proxied routes and per-route policies are defined in a policy block
policy:
- from: httpbin.corp.beyondperimeter.com
to: http://httpbin
allowed_domains:
- pomerium.io
cors_allow_preflight: true
timeout: 30s
- from: external-httpbin.corp.beyondperimeter.com
to: httpbin.org
allowed_domains:
- gmail.com
- from: weirdlyssl.corp.beyondperimeter.com
to: http://neverssl.com
allowed_users:
- bdd@pomerium.io
allowed_groups:
- admins
- developers
- from: hello.corp.beyondperimeter.com
to: http://hello:8080
allowed_groups:
- admins
- from: external-search.corp.beyondperimeter.com
to: google.com
allow_public_unauthenticated_access: true

View file

@ -2,14 +2,19 @@
sidebar: auto sidebar: auto
--- ---
# Configuration Variables # Configuration
Pomerium uses [environmental variables] to set configuration settings. If you are coming from a kubernetes or docker background this should feel familiar. If not, check out the following primers. Pomerium can use a combination of a YAML/JSON/TOML configuration file and [environmental variables] to set configuration settings.
If you are coming from a kubernetes or docker background this should feel familiar. If not, check out the following primers.
- [Store config in the environment](https://12factor.net/config) - [Store config in the environment](https://12factor.net/config)
- [Kubernetes: Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) - [Kubernetes: Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/)
- [Kubernetes: Config Maps](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/)
- [Docker: Environment variables](https://docs.docker.com/compose/environment-variables/) - [Docker: Environment variables](https://docs.docker.com/compose/environment-variables/)
In general, any setting specified by environment variable can also be present in the optional config file as the same name but lower cased. Environment variables take precedence.
## Global settings ## Global settings
These are configuration variables shared by all services, in all service modes. These are configuration variables shared by all services, in all service modes.
@ -17,6 +22,7 @@ These are configuration variables shared by all services, in all service modes.
### Service Mode ### Service Mode
- Environmental Variable: `SERVICES` - Environmental Variable: `SERVICES`
- Config File Key: `services`
- Type: `string` - Type: `string`
- Default: `all` - Default: `all`
- Options: `all` `authenticate` `authorize` or `proxy` - Options: `all` `authenticate` `authorize` or `proxy`
@ -26,6 +32,7 @@ Service mode sets the pomerium service(s) to run. If testing, you may want to se
### Address ### Address
- Environmental Variable: `ADDRESS` - Environmental Variable: `ADDRESS`
- Config File Key: `address`
- Type: `string` - Type: `string`
- Example: `:https`, `:443`, `:8443` - Example: `:https`, `:443`, `:8443`
- Default: `:https` - Default: `:https`
@ -36,6 +43,7 @@ Address specifies the host and port to serve HTTPS and gRPC requests from. If em
### Shared Secret ### Shared Secret
- Environmental Variable: `SHARED_SECRET` - Environmental Variable: `SHARED_SECRET`
- Config File Key: `shared_secret`
- Type: [base64 encoded] `string` - Type: [base64 encoded] `string`
- Required - Required
@ -48,6 +56,7 @@ head -c32 /dev/urandom | base64
### Debug ### Debug
- Environmental Variable: `POMERIUM_DEBUG` - Environmental Variable: `POMERIUM_DEBUG`
- Config File Key: `pomerium_debug`
- Type: `bool` - Type: `bool`
- Default: `false` - Default: `false`
@ -74,6 +83,7 @@ If `false`
### Log Level ### Log Level
- Environmental Variable: `LOG_LEVEL` - Environmental Variable: `LOG_LEVEL`
- Config File Key: `log_level`
- Type: `string` - Type: `string`
- Options: `debug` `info` `warn` `error` - Options: `debug` `info` `warn` `error`
- Default: `debug` - Default: `debug`
@ -83,6 +93,7 @@ Log level sets the global logging level for pomerium. Only logs of the desired l
### Certificate ### Certificate
- Environmental Variable: either `CERTIFICATE` or `CERTIFICATE_FILE` - Environmental Variable: either `CERTIFICATE` or `CERTIFICATE_FILE`
- Config File Key: `certificate` or `certificate_file`
- Type: [base64 encoded] `string` or relative file location - Type: [base64 encoded] `string` or relative file location
- Required - Required
@ -91,6 +102,7 @@ Certificate is the x509 _public-key_ used to establish secure HTTP and gRPC conn
### Certificate Key ### Certificate Key
- Environmental Variable: either `CERTIFICATE_KEY` or `CERTIFICATE_KEY_FILE` - Environmental Variable: either `CERTIFICATE_KEY` or `CERTIFICATE_KEY_FILE`
- Config File Key: `certificate_key` or `certificate_key_file`
- Type: [base64 encoded] `string` - Type: [base64 encoded] `string`
- Required - Required
@ -99,6 +111,7 @@ Certificate key is the x509 _private-key_ used to establish secure HTTP and gRPC
### Global Timeouts ### Global Timeouts
- Environmental Variables: `TIMEOUT_READ` `TIMEOUT_WRITE` `TIMEOUT_READ_HEADER` `TIMEOUT_IDLE` - Environmental Variables: `TIMEOUT_READ` `TIMEOUT_WRITE` `TIMEOUT_READ_HEADER` `TIMEOUT_IDLE`
- Config File Key: `timeout_read` `timeout_write` `timeout_read_header` `timeout_idle`
- Type: [Go Duration](https://golang.org/pkg/time/#Duration.String) `string` - Type: [Go Duration](https://golang.org/pkg/time/#Duration.String) `string`
- Example: `TIMEOUT_READ=30s` - Example: `TIMEOUT_READ=30s`
- Defaults: `TIMEOUT_READ_HEADER=10s` `TIMEOUT_READ=30s` `TIMEOUT_WRITE=0` `TIMEOUT_IDLE=5m` - Defaults: `TIMEOUT_READ_HEADER=10s` `TIMEOUT_READ=30s` `TIMEOUT_WRITE=0` `TIMEOUT_IDLE=5m`
@ -112,6 +125,7 @@ Timeouts set the global server timeouts. For route-specific timeouts, see [polic
### HTTP Redirect Address ### HTTP Redirect Address
- Environmental Variable: `HTTP_REDIRECT_ADDR` - Environmental Variable: `HTTP_REDIRECT_ADDR`
- Config File Key: `http_redirect_addr`
- Type: `string` - Type: `string`
- Example: `:80`, `:http`, `:8080` - Example: `:80`, `:http`, `:8080`
- Optional - Optional
@ -120,14 +134,14 @@ If set, the HTTP Redirect Address specifies the host and port to redirect http t
### Policy ### Policy
- Environmental Variable: either `POLICY` or `POLICY_FILE` - Environmental Variable: `POLICY`
- Type: [base64 encoded] `string` or relative file location - Config File Key: `policy`
- Filetype: `json` or `yaml` - Type: [base64 encoded] `string` or inline policy structure in config file
- Required - Required
Policy contains route specific settings, and access control details. For example, Policy contains route specific settings, and access control details. If you are configuring via POLICY environment variable, just the contents of the policy needs to be passed. If you are configuring via file, the policy should be present under the policy key. For example,
<<< @/policy.example.yaml <<< @/config-policy-only.yaml
A list of policy configuration variables follows. A list of policy configuration variables follows.
@ -211,6 +225,7 @@ Policy timeout establishes the per-route timeout value. Cannot exceed global tim
### Authenticate Service URL ### Authenticate Service URL
- Environmental Variable: `AUTHENTICATE_SERVICE_URL` - Environmental Variable: `AUTHENTICATE_SERVICE_URL`
- Config File Key: `authenticate_service_url`
- Type: `URL` - Type: `URL`
- Required - Required
- Example: `https://authenticate.corp.example.com` - Example: `https://authenticate.corp.example.com`
@ -220,6 +235,7 @@ Authenticate Service URL is the externally accessible URL for the authenticate s
### Identity Provider Name ### Identity Provider Name
- Environmental Variable: `IDP_PROVIDER` - Environmental Variable: `IDP_PROVIDER`
- Config File Key: `idp_provider`
- Type: `string` - Type: `string`
- Required - Required
- Options: `azure` `google` `okta` `gitlab` `onelogin` or `oidc` - Options: `azure` `google` `okta` `gitlab` `onelogin` or `oidc`
@ -231,6 +247,7 @@ See [identity provider] for details.
### Identity Provider Client ID ### Identity Provider Client ID
- Environmental Variable: `IDP_CLIENT_ID` - Environmental Variable: `IDP_CLIENT_ID`
- Config File Key: `idp_client_id`
- Type: `string` - Type: `string`
- Required - Required
@ -239,6 +256,7 @@ Client ID is the OAuth 2.0 Client Identifier retrieved from your identity provid
### Identity Provider Client Secret ### Identity Provider Client Secret
- Environmental Variable: `IDP_CLIENT_SECRET` - Environmental Variable: `IDP_CLIENT_SECRET`
- Config File Key: `idp_client_secret`
- Type: `string` - Type: `string`
- Required - Required
@ -247,6 +265,7 @@ Client Secret is the OAuth 2.0 Secret Identifier retrieved from your identity pr
### Identity Provider URL ### Identity Provider URL
- Environmental Variable: `IDP_PROVIDER_URL` - Environmental Variable: `IDP_PROVIDER_URL`
- Config File Key: `idp_provider_url`
- Type: `string` - Type: `string`
- Required, depending on provider - Required, depending on provider
@ -255,6 +274,7 @@ Provider URL is the base path to an identity provider's [OpenID connect discover
### Identity Provider Scopes ### Identity Provider Scopes
- Environmental Variable: `IDP_SCOPES` - Environmental Variable: `IDP_SCOPES`
- Config File Key: `idp_scopes`
- Type: `[]string` comma separated list of oauth scopes. - Type: `[]string` comma separated list of oauth scopes.
- Default: `oidc`,`profile`, `email`, `offline_access` (typically) - Default: `oidc`,`profile`, `email`, `offline_access` (typically)
- Optional for built-in identity providers. - Optional for built-in identity providers.
@ -264,6 +284,7 @@ Identity provider scopes correspond to access privilege scopes as defined in Sec
### Identity Provider Service Account ### Identity Provider Service Account
- Environmental Variable: `IDP_SERVICE_ACCOUNT` - Environmental Variable: `IDP_SERVICE_ACCOUNT`
- Config File Key: `idp_service_account`
- Type: `string` - Type: `string`
- Required, depending on provider - Required, depending on provider
@ -274,6 +295,7 @@ Identity Provider Service Account is field used to configure any additional user
### Signing Key ### Signing Key
- Environmental Variable: `SIGNING_KEY` - Environmental Variable: `SIGNING_KEY`
- Config File Key: `signing_key`
- Type: [base64 encoded] `string` - Type: [base64 encoded] `string`
- Optional - Optional
@ -282,6 +304,7 @@ Signing key is the base64 encoded key used to sign outbound requests. For more i
### Authenticate Service URL ### Authenticate Service URL
- Environmental Variable: `AUTHENTICATE_SERVICE_URL` - Environmental Variable: `AUTHENTICATE_SERVICE_URL`
- Config File Key: `authenticate_service_url`
- Type: `URL` - Type: `URL`
- Required - Required
- Example: `https://authenticate.corp.example.com` - Example: `https://authenticate.corp.example.com`
@ -291,6 +314,7 @@ Authenticate Service URL is the externally accessible URL for the authenticate s
### Authenticate Internal Service URL ### Authenticate Internal Service URL
- Environmental Variable: `AUTHENTICATE_INTERNAL_URL` - Environmental Variable: `AUTHENTICATE_INTERNAL_URL`
- Config File Key: `authenticate_internal_url`
- Type: `string` - Type: `string`
- Optional - Optional
- Example: `pomerium-authenticate-service.pomerium.svc.cluster.local` - Example: `pomerium-authenticate-service.pomerium.svc.cluster.local`
@ -300,6 +324,7 @@ Authenticate Internal Service URL is the internally routed dns name of the authe
### Authorize Service URL ### Authorize Service URL
- Environmental Variable: `AUTHORIZE_SERVICE_URL` - Environmental Variable: `AUTHORIZE_SERVICE_URL`
- Config File Key: `authorize_service_url`
- Type: `URL` - Type: `URL`
- Required - Required
- Example: `https://access.corp.example.com` or `pomerium-authorize-service.pomerium.svc.cluster.local` - Example: `https://access.corp.example.com` or `pomerium-authorize-service.pomerium.svc.cluster.local`
@ -311,6 +336,7 @@ If your load balancer does not support gRPC pass-through you'll need to set this
### Override Certificate Name ### Override Certificate Name
- Environmental Variable: `OVERRIDE_CERTIFICATE_NAME` - Environmental Variable: `OVERRIDE_CERTIFICATE_NAME`
- Config File Key: `override_certificate_name`
- Type: `int` - Type: `int`
- Optional (but typically required if Authenticate Internal Service Address is set) - Optional (but typically required if Authenticate Internal Service Address is set)
- Example: `*.corp.example.com` if wild card or `authenticate.corp.example.com`/`authorize.corp.example.com` - Example: `*.corp.example.com` if wild card or `authenticate.corp.example.com`/`authorize.corp.example.com`
@ -320,6 +346,7 @@ When Authenticate Internal Service Address is set, secure service communication
### Certificate Authority ### Certificate Authority
- Environmental Variable: `CERTIFICATE_AUTHORITY` or `CERTIFICATE_AUTHORITY_FILE` - Environmental Variable: `CERTIFICATE_AUTHORITY` or `CERTIFICATE_AUTHORITY_FILE`
- Config File Key: `certificate_authority` or `certificate_authority_file`
- Type: [base64 encoded] `string` or relative file location - Type: [base64 encoded] `string` or relative file location
- Optional - Optional
@ -328,6 +355,7 @@ Certificate Authority is set when behind-the-ingress service communication uses
### Headers ### Headers
- Environmental Variable: `HEADERS` - Environmental Variable: `HEADERS`
- Config File Key: `headers`
- Type: map of `strings` key value pairs - Type: map of `strings` key value pairs
- Example: `X-Content-Type-Options:nosniff,X-Frame-Options:SAMEORIGIN` - Example: `X-Content-Type-Options:nosniff,X-Frame-Options:SAMEORIGIN`
- To disable: `disable:true` - To disable: `disable:true`

View file

@ -61,6 +61,6 @@ export IDP_CLIENT_SECRET="REPLACEME"
# export SCOPE="openid email" # generally, you want the default OIDC scopes # export SCOPE="openid email" # generally, you want the default OIDC scopes
# Proxied routes and per-route policies are defined in a policy provided either # Proxied routes and per-route policies are defined in a policy provided either
# directly as a base64 encoded yaml/json file, or as a path pointing to a # directly as a base64 encoded yaml/json file, or as the policy key in the configuration
# policy file (`POLICY_FILE`) # file
export POLICY_FILE="./policy.example.yml" export POLICY="$(base64 ./policy.yaml)"

2
go.mod
View file

@ -3,13 +3,13 @@ module github.com/pomerium/pomerium
go 1.12 go 1.12
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/mock v1.2.0 github.com/golang/mock v1.2.0
github.com/golang/protobuf v1.3.1 github.com/golang/protobuf v1.3.1
github.com/pomerium/envconfig v1.5.0 github.com/pomerium/envconfig v1.5.0
github.com/pomerium/go-oidc v2.0.0+incompatible github.com/pomerium/go-oidc v2.0.0+incompatible
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/rs/zerolog v1.12.0 github.com/rs/zerolog v1.12.0
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.3.0 // indirect github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7 golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7

105
go.sum
View file

@ -2,16 +2,40 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
@ -19,11 +43,35 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pomerium/envconfig v1.5.0 h1:OeYS/p6AUxKFqCZHM5BG7pUb0m3MkaC1ZhRLPTHbk8g= github.com/pomerium/envconfig v1.5.0 h1:OeYS/p6AUxKFqCZHM5BG7pUb0m3MkaC1ZhRLPTHbk8g=
@ -33,26 +81,69 @@ github.com/pomerium/go-oidc v2.0.0+incompatible/go.mod h1:DRsGVw6MOgxbfq4Y57jKOE
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rs/zerolog v1.12.0 h1:aqZ1XRadoS8IBknR5IDFvGzbHly1X9ApIqOroooQF/c= github.com/rs/zerolog v1.12.0 h1:aqZ1XRadoS8IBknR5IDFvGzbHly1X9ApIqOroooQF/c=
github.com/rs/zerolog v1.12.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.12.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7 h1:Qe/u+eY379X4He4GBMFZYu3pmh1ML5yT1aL1ndNM1zQ= golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7 h1:Qe/u+eY379X4He4GBMFZYu3pmh1ML5yT1aL1ndNM1zQ=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
@ -62,16 +153,23 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
@ -86,14 +184,21 @@ google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM= google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -1,16 +1,20 @@
package config package config
import ( import (
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"time" "time"
"github.com/pomerium/envconfig" "gopkg.in/yaml.v2"
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/policy" "github.com/pomerium/pomerium/internal/policy"
"github.com/spf13/viper"
) )
// DisableHeaderKey is the key used to check whether to disable setting header // DisableHeaderKey is the key used to check whether to disable setting header
@ -22,97 +26,99 @@ const DisableHeaderKey = "disable"
// in the local directory as `./cert.pem` and `./privkey.pem` respectively. // in the local directory as `./cert.pem` and `./privkey.pem` respectively.
type Options struct { type Options struct {
// Debug outputs human-readable logs to Stdout. // Debug outputs human-readable logs to Stdout.
Debug bool `envconfig:"POMERIUM_DEBUG"` Debug bool `mapstructure:"pomerium_debug"`
// LogLevel sets the global override for log level. All Loggers will use at least this value. // LogLevel sets the global override for log level. All Loggers will use at least this value.
// Possible options are "info","warn", and "error". Defaults to "debug". // Possible options are "info","warn", and "error". Defaults to "debug".
LogLevel string `envconfig:"LOG_LEVEL"` LogLevel string `mapstructure:"log_level"`
// SharedKey is the shared secret authorization key used to mutually authenticate // SharedKey is the shared secret authorization key used to mutually authenticate
// requests between services. // requests between services.
SharedKey string `envconfig:"SHARED_SECRET"` SharedKey string `mapstructure:"shared_secret"`
// Services is a list enabled service mode. If none are selected, "all" is used. // Services is a list enabled service mode. If none are selected, "all" is used.
// Available options are : "all", "authenticate", "proxy". // Available options are : "all", "authenticate", "proxy".
Services string `envconfig:"SERVICES"` Services string `mapstructure:"services"`
// Addr specifies the host and port on which the server should serve // Addr specifies the host and port on which the server should serve
// HTTPS requests. If empty, ":https" is used. // HTTPS requests. If empty, ":https" is used.
Addr string `envconfig:"ADDRESS"` Addr string `mapstructure:"address"`
// Cert and Key specifies the base64 encoded TLS certificates to use. // Cert and Key specifies the base64 encoded TLS certificates to use.
Cert string `envconfig:"CERTIFICATE"` Cert string `mapstructure:"certificate"`
Key string `envconfig:"CERTIFICATE_KEY"` Key string `mapstructure:"certificate_key"`
// CertFile and KeyFile specifies the TLS certificates to use. // CertFile and KeyFile specifies the TLS certificates to use.
CertFile string `envconfig:"CERTIFICATE_FILE"` CertFile string `mapstructure:"certificate_file"`
KeyFile string `envconfig:"CERTIFICATE_KEY_FILE"` KeyFile string `mapstructure:"certificate_key_file"`
// HttpRedirectAddr, if set, specifies the host and port to run the HTTP // HttpRedirectAddr, if set, specifies the host and port to run the HTTP
// to HTTPS redirect server on. For example, ":http" would start a server // to HTTPS redirect server on. For example, ":http" would start a server
// on port 80. If empty, no redirect server is started. // on port 80. If empty, no redirect server is started.
HTTPRedirectAddr string `envconfig:"HTTP_REDIRECT_ADDR"` HTTPRedirectAddr string `mapstructure:"http_redirect_addr"`
// Timeout settings : https://github.com/pomerium/pomerium/issues/40 // Timeout settings : https://github.com/pomerium/pomerium/issues/40
ReadTimeout time.Duration `envconfig:"TIMEOUT_READ"` ReadTimeout time.Duration `mapstructure:"timeout_read"`
WriteTimeout time.Duration `envconfig:"TIMEOUT_WRITE"` WriteTimeout time.Duration `mapstructure:"timeout_write"`
ReadHeaderTimeout time.Duration `envconfig:"TIMEOUT_READ_HEADER"` ReadHeaderTimeout time.Duration `mapstructure:"timeout_read_header"`
IdleTimeout time.Duration `envconfig:"TIMEOUT_IDLE"` IdleTimeout time.Duration `mapstructure:"timeout_idle"`
// Policy is a base64 encoded yaml blob which enumerates // Policy is a base64 encoded yaml blob which enumerates
// per-route access control policies. // per-route access control policies.
Policy string `envconfig:"POLICY"` PolicyEnv string
PolicyFile string `envconfig:"POLICY_FILE"` PolicyFile string `mapstructure:"policy_file"`
// AuthenticateURL represents the externally accessible http endpoints // AuthenticateURL represents the externally accessible http endpoints
// used for authentication requests and callbacks // used for authentication requests and callbacks
AuthenticateURL *url.URL `envconfig:"AUTHENTICATE_SERVICE_URL"` AuthenticateURLString string `mapstructure:"authenticate_service_url"`
AuthenticateURL *url.URL
// Session/Cookie management // Session/Cookie management
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
CookieName string `envconfig:"COOKIE_NAME"` CookieName string `mapstructure:"cookie_name"`
CookieSecret string `envconfig:"COOKIE_SECRET"` CookieSecret string `mapstructure:"cookie_secret"`
CookieDomain string `envconfig:"COOKIE_DOMAIN"` CookieDomain string `mapstructure:"cookie_domain"`
CookieSecure bool `envconfig:"COOKIE_SECURE"` CookieSecure bool `mapstructure:"cookie_secure"`
CookieHTTPOnly bool `envconfig:"COOKIE_HTTP_ONLY"` CookieHTTPOnly bool `mapstructure:"cookie_http_only"`
CookieExpire time.Duration `envconfig:"COOKIE_EXPIRE"` CookieExpire time.Duration `mapstructure:"cookie_expire"`
CookieRefresh time.Duration `envconfig:"COOKIE_REFRESH"` CookieRefresh time.Duration `mapstructure:"cookie_refresh"`
// Identity provider configuration variables as specified by RFC6749 // Identity provider configuration variables as specified by RFC6749
// https://openid.net/specs/openid-connect-basic-1_0.html#RFC6749 // https://openid.net/specs/openid-connect-basic-1_0.html#RFC6749
ClientID string `envconfig:"IDP_CLIENT_ID"` ClientID string `mapstructure:"idp_client_id"`
ClientSecret string `envconfig:"IDP_CLIENT_SECRET"` ClientSecret string `mapstructure:"idp_client_secret"`
Provider string `envconfig:"IDP_PROVIDER"` Provider string `mapstructure:"idp_provider"`
ProviderURL string `envconfig:"IDP_PROVIDER_URL"` ProviderURL string `mapstructure:"idp_provider_url"`
Scopes []string `envconfig:"IDP_SCOPES"` Scopes []string `mapstructure:"idp_scopes"`
ServiceAccount string `envconfig:"IDP_SERVICE_ACCOUNT"` ServiceAccount string `mapstructure:"idp_service_account"`
Policies []policy.Policy `envconfig:"POLICY"` Policies []policy.Policy
// AuthenticateInternalAddr is used as an override when using a load balancer // AuthenticateInternalAddr is used as an override when using a load balancer
// or ingress that does not natively support routing gRPC. // or ingress that does not natively support routing gRPC.
AuthenticateInternalAddr string `envconfig:"AUTHENTICATE_INTERNAL_URL"` AuthenticateInternalAddr string `mapstructure:"authenticate_internal_url"`
// AuthorizeURL is the routable destination of the authorize service's // AuthorizeURL is the routable destination of the authorize service's
// gRPC endpoint. NOTE: As above, many load balancers do not support // gRPC endpoint. NOTE: As above, many load balancers do not support
// externally routed gRPC so this may be an internal location. // externally routed gRPC so this may be an internal location.
AuthorizeURL *url.URL `envconfig:"AUTHORIZE_SERVICE_URL"` AuthorizeURLString string `mapstructure:"authorize_service_url"`
AuthorizeURL *url.URL
// Settings to enable custom behind-the-ingress service communication // Settings to enable custom behind-the-ingress service communication
OverrideCertificateName string `envconfig:"OVERRIDE_CERTIFICATE_NAME"` OverrideCertificateName string `mapstructure:"override_certificate_name"`
CA string `envconfig:"CERTIFICATE_AUTHORITY"` CA string `mapstructure:"certificate_authority"`
CAFile string `envconfig:"CERTIFICATE_AUTHORITY_FILE"` CAFile string `mapstructure:"certificate_authority_file"`
// SigningKey is a base64 encoded private key used to add a JWT-signature. // SigningKey is a base64 encoded private key used to add a JWT-signature.
// https://www.pomerium.io/docs/signed-headers.html // https://www.pomerium.io/docs/signed-headers.html
SigningKey string `envconfig:"SIGNING_KEY"` SigningKey string `mapstructure:"signing_key"`
// Headers to set on all proxied requests. Add a 'disable' key map to turn off. // Headers to set on all proxied requests. Add a 'disable' key map to turn off.
Headers map[string]string `envconfig:"HEADERS"` Headers map[string]string `mapstructure:"headers"`
// Sub-routes // Sub-routes
Routes map[string]string `envconfig:"ROUTES"` Routes map[string]string `mapstructure:"routes"`
DefaultUpstreamTimeout time.Duration `envconfig:"DEFAULT_UPSTREAM_TIMEOUT"` DefaultUpstreamTimeout time.Duration `mapstructure:"default_upstream_timeout"`
} }
// NewOptions returns a new options struct with default vaules // NewOptions returns a new options struct with default vaules
@ -146,19 +152,39 @@ func NewOptions() *Options {
return o return o
} }
// OptionsFromEnvConfig builds the main binary's configuration // OptionsFromViper builds the main binary's configuration
// options from provided environmental variables // options by parsing environmental variables and config file
func OptionsFromEnvConfig() (*Options, error) { func OptionsFromViper(configFile string) (*Options, error) {
o := NewOptions() o := NewOptions()
if err := envconfig.Process("", o); err != nil {
return nil, err // Load up config
o.bindEnvs()
if configFile != "" {
log.Info().Msgf("Loading config from '%s'", configFile)
viper.SetConfigFile(configFile)
err := viper.ReadInConfig()
if err != nil {
return nil, fmt.Errorf("Failed to read config: %s", err)
}
} }
if !IsValidService(o.Services) {
return nil, fmt.Errorf("%s is an invalid service type", o.Services) err := viper.Unmarshal(o)
if err != nil {
return nil, fmt.Errorf("Failed to load options from config: %s", err)
} }
if o.SharedKey == "" {
return nil, errors.New("shared-key cannot be empty") // Turn URL strings into url structs
err = o.parseURLs()
if err != nil {
return nil, fmt.Errorf("Failed to parse URLs: %s", err)
} }
// Load and initialize policy
err = o.parsePolicy()
if err != nil {
return nil, fmt.Errorf("Failed to parse Policy: %s", err)
}
if o.Debug { if o.Debug {
log.SetDebugMode() log.SetDebugMode()
} }
@ -168,9 +194,99 @@ func OptionsFromEnvConfig() (*Options, error) {
if _, disable := o.Headers[DisableHeaderKey]; disable { if _, disable := o.Headers[DisableHeaderKey]; disable {
o.Headers = make(map[string]string) o.Headers = make(map[string]string)
} }
err = o.validate()
if err != nil {
return nil, err
}
return o, nil return o, nil
} }
// validate ensures the Options fields are properly formed and present
func (o *Options) validate() error {
if !IsValidService(o.Services) {
return fmt.Errorf("%s is an invalid service type", o.Services)
}
if o.SharedKey == "" {
return errors.New("shared-key cannot be empty")
}
if len(o.Routes) != 0 {
return errors.New("routes setting is deprecated, use policy instead")
}
if o.PolicyFile != "" {
return errors.New("Setting POLICY_FILE is deprecated, use policy env var or config file instead")
}
return nil
}
// parsePolicy initializes policy
func (o *Options) parsePolicy() error {
var policies []policy.Policy
// Parse from base64 env var
if o.PolicyEnv != "" {
policyBytes, err := base64.StdEncoding.DecodeString(o.PolicyEnv)
if err != nil {
return fmt.Errorf("Could not decode POLICY env var: %s", err)
}
if err := yaml.Unmarshal(policyBytes, &policies); err != nil {
return fmt.Errorf("Could not parse POLICY env var: %s", err)
}
// Parse from file
} else {
err := viper.UnmarshalKey("policy", &policies)
if err != nil {
return err
}
}
// Finish initializing policies
for i := range policies {
err := (&policies[i]).Validate()
if err != nil {
return err
}
}
o.Policies = policies
return nil
}
// parseURLs parses URL strings into actual URL pointers
func (o *Options) parseURLs() error {
AuthenticateURL, err := url.Parse(o.AuthenticateURLString)
if err != nil {
return err
}
AuthorizeURL, err := url.Parse(o.AuthorizeURLString)
if err != nil {
return err
}
o.AuthenticateURL = AuthenticateURL
o.AuthorizeURL = AuthorizeURL
return nil
}
// bindEnvs makes sure viper binds to each env var based on the mapstructure tag
func (o *Options) bindEnvs() {
tagName := `mapstructure`
t := reflect.TypeOf(*o)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
envName := field.Tag.Get(tagName)
viper.BindEnv(envName)
}
// Statically bind fields
viper.BindEnv("PolicyEnv", "POLICY")
}
// findPwd returns best guess at current working directory // findPwd returns best guess at current working directory
func findPwd() string { func findPwd() string {
p, err := os.Getwd() p, err := os.Getwd()
@ -180,7 +296,7 @@ func findPwd() string {
return p return p
} }
// isValidService checks to see if a service is a valid service mode // IsValidService checks to see if a service is a valid service mode
func IsValidService(s string) bool { func IsValidService(s string) bool {
switch s { switch s {
case case
@ -193,6 +309,7 @@ func IsValidService(s string) bool {
return false return false
} }
// IsAuthenticate checks to see if we should be running the authenticate service
func IsAuthenticate(s string) bool { func IsAuthenticate(s string) bool {
switch s { switch s {
case case
@ -203,6 +320,7 @@ func IsAuthenticate(s string) bool {
return false return false
} }
// IsAuthorize checks to see if we should be running the authorize service
func IsAuthorize(s string) bool { func IsAuthorize(s string) bool {
switch s { switch s {
case case
@ -213,6 +331,7 @@ func IsAuthorize(s string) bool {
return false return false
} }
// IsProxy checks to see if we should be running the proxy service
func IsProxy(s string) bool { func IsProxy(s string) bool {
switch s { switch s {
case case

View file

@ -1,46 +1,54 @@
package config package config
import ( import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/url"
"os" "os"
"reflect" "reflect"
"testing" "testing"
"github.com/pomerium/pomerium/internal/policy"
"github.com/spf13/viper"
) )
func Test_optionsFromEnvConfig(t *testing.T) { func Test_validate(t *testing.T) {
good := NewOptions()
good.SharedKey = "test" testOptions := func() *Options {
o := NewOptions()
o.SharedKey = "test"
o.Services = "all"
return o
}
good := testOptions()
badServices := testOptions()
badServices.Services = "blue"
badSecret := testOptions()
badSecret.SharedKey = ""
badRoutes := testOptions()
badRoutes.Routes = map[string]string{"foo": "bar"}
badPolicyFile := testOptions()
badPolicyFile.PolicyFile = "file"
tests := []struct { tests := []struct {
name string name string
want *Options testOpts *Options
envKey string
envValue string
wantErr bool wantErr bool
}{ }{
{"good default with no env settings", good, "", "", false}, {"good default with no env settings", good, false},
{"invalid service type", nil, "SERVICES", "invalid", true}, {"invalid service type", badServices, true},
{"good service", good, "SERVICES", "all", false}, {"missing shared secret", badSecret, true},
{"bad debug boolean", nil, "POMERIUM_DEBUG", "yes", true}, {"routes present", badRoutes, true},
{"missing shared secret", nil, "SHARED_SECRET", "", true}, {"policy file specified", badPolicyFile, true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
os.Clearenv() err := tt.testOpts.validate()
if tt.envKey != "" {
os.Setenv(tt.envKey, tt.envValue)
}
if tt.envKey != "SHARED_SECRET" {
os.Setenv("SHARED_SECRET", "test")
}
got, err := OptionsFromEnvConfig()
os.Unsetenv(tt.envKey)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("optionsFromEnvConfig() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("optionsFromEnvConfig() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("optionsFromEnvConfig() = got %#v,\n want %#v", got, tt.want)
}
}) })
} }
} }
@ -89,3 +97,220 @@ func Test_isAuthenticate(t *testing.T) {
}) })
} }
} }
func Test_isAuthorize(t *testing.T) {
tests := []struct {
name string
service string
want bool
}{
{"proxy", "proxy", false},
{"all", "all", true},
{"authorize", "authorize", true},
{"authorize bad case", "AuThorize", false},
{"authenticate implemented", "authenticate", false},
{"jiberish", "xd23", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsAuthorize(tt.service); got != tt.want {
t.Errorf("isAuthenticate() = %v, want %v", got, tt.want)
}
})
}
}
func Test_bindEnvs(t *testing.T) {
o := &Options{}
os.Clearenv()
defer os.Unsetenv("POMERIUM_DEBUG")
defer os.Unsetenv("POLICY")
os.Setenv("POMERIUM_DEBUG", "true")
os.Setenv("POLICY", "mypolicy")
o.bindEnvs()
err := viper.Unmarshal(o)
if err != nil {
t.Errorf("Could not unmarshal %#v: %s", o, err)
}
if !o.Debug {
t.Errorf("Failed to load POMERIUM_DEBUG from environment")
}
if o.Services != "" {
t.Errorf("Somehow got SERVICES from environment without configuring it")
}
if o.PolicyEnv != "mypolicy" {
t.Errorf("Failed to bind policy env var to PolicyEnv")
}
}
func Test_parseURLs(t *testing.T) {
tests := []struct {
name string
authorizeURL string
authenticateURL string
wantErr bool
}{
{"good", "https://authz.mydomain.tld", "https://authn.mydomain.tld", false},
{"bad authorize", "notaurl", "https://authn.mydomain.tld", true},
{"bad authenticate", "https://authz.mydomain.tld", "notaurl", true},
{"only authn", "", "https://authn.mydomain.tld", false},
{"only authz", "https://authz.mydomain.tld", "", false},
}
for _, test := range tests {
o := &Options{
AuthenticateURLString: test.authenticateURL,
AuthorizeURLString: test.authorizeURL,
}
err := o.parseURLs()
if !test.wantErr && err != nil {
t.Errorf("Failed to parse URLs %v: %s", test, err)
}
if o.AuthenticateURL.String() != test.authenticateURL {
t.Errorf("Failed to update AuthenticateURL: %v", test)
}
if o.AuthorizeURL.String() != test.authorizeURL {
t.Errorf("Failed to update AuthorizeURL: %v", test)
}
}
}
func Test_OptionsFromViper(t *testing.T) {
testPolicy := policy.Policy{
To: "https://httpbin.org",
From: "https://pomerium.io",
}
testPolicy.Validate()
testPolicies := []policy.Policy{
testPolicy,
}
goodConfigBytes := []byte(`{"shared_secret":"Setec Astronomy","service":"all","policy":[{"from":"https://pomerium.io","to":"https://httpbin.org"}]}`)
goodOptions := NewOptions()
goodOptions.SharedKey = "Setec Astronomy"
goodOptions.Services = "all"
goodOptions.Policies = testPolicies
goodOptions.CookieName = "oatmeal"
badConfigBytes := []byte("badjson!")
badUnmarshalConfigBytes := []byte(`"debug": "blue"`)
tests := []struct {
name string
configBytes []byte
want *Options
wantErr bool
}{
{"good", goodConfigBytes, goodOptions, false},
{"bad json", badConfigBytes, nil, true},
{"bad unmarshal", badUnmarshalConfigBytes, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
viper.Reset()
os.Clearenv()
os.Setenv("COOKIE_NAME", "oatmeal")
defer os.Unsetenv("COOKIE_NAME")
tempFile, _ := ioutil.TempFile("", "*.json")
defer tempFile.Close()
defer os.Remove(tempFile.Name())
tempFile.Write(tt.configBytes)
got, err := OptionsFromViper(tempFile.Name())
if (err != nil) != tt.wantErr {
t.Errorf("OptionsFromViper() error = %v, wantErr %v", err, tt.wantErr)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("OptionsFromViper() = %v, want %v", got, tt.want)
}
})
}
// Test for missing config file
o, err := OptionsFromViper("filedoesnotexist")
if o != nil || err == nil {
t.Errorf("OptionsFromViper(): Did when loading missing file")
}
}
func Test_parsePolicyEnv(t *testing.T) {
t.Parallel()
source := "https://pomerium.io"
sourceURL, _ := url.ParseRequestURI(source)
dest := "https://httpbin.org"
destURL, _ := url.ParseRequestURI(dest)
tests := []struct {
name string
policyBytes []byte
want []policy.Policy
wantErr bool
}{
{"simple json", []byte(fmt.Sprintf(`[{"from": "%s","to":"%s"}]`, source, dest)), []policy.Policy{{From: source, To: dest, Source: sourceURL, Destination: destURL}}, false},
{"bad from", []byte(`[{"from": "%","to":"httpbin.org"}]`), nil, true},
{"bad to", []byte(`[{"from": "pomerium.io","to":"%"}]`), nil, true},
{"simple error", []byte(`{}`), nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := new(Options)
o.PolicyEnv = base64.StdEncoding.EncodeToString(tt.policyBytes)
err := o.parsePolicy()
if (err != nil) != tt.wantErr {
t.Errorf("parasePolicy() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(o.Policies, tt.want) {
t.Errorf("parasePolicy() = \n%v, want \n%v", o, tt.want)
}
})
}
// Catch bad base64
o := new(Options)
o.PolicyEnv = "foo"
err := o.parsePolicy()
if err == nil {
t.Errorf("parasePolicy() did not catch bad base64 %v", o)
}
}
func Test_parsePolicyFile(t *testing.T) {
source := "https://pomerium.io"
sourceURL, _ := url.ParseRequestURI(source)
dest := "https://httpbin.org"
destURL, _ := url.ParseRequestURI(dest)
tests := []struct {
name string
policyBytes []byte
want []policy.Policy
wantErr bool
}{
{"simple json", []byte(fmt.Sprintf(`{"policy":[{"from": "%s","to":"%s"}]}`, source, dest)), []policy.Policy{{From: source, To: dest, Source: sourceURL, Destination: destURL}}, false},
{"bad from", []byte(`{"policy":[{"from": "%","to":"httpbin.org"}]}`), nil, true},
{"bad to", []byte(`{"policy":[{"from": "pomerium.io","to":"%"}]}`), nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := new(Options)
tempFile, _ := ioutil.TempFile("", "*.json")
defer tempFile.Close()
defer os.Remove(tempFile.Name())
tempFile.Write(tt.policyBytes)
o = new(Options)
viper.SetConfigFile(tempFile.Name())
err := viper.ReadInConfig()
err = o.parsePolicy()
if (err != nil) != tt.wantErr {
t.Errorf("parasePolicy() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(o.Policies, tt.want) {
t.Errorf("parasePolicy() = \n%v, want \n%v", o, tt.want)
}
})
}
}

View file

@ -3,42 +3,39 @@ package policy // import "github.com/pomerium/pomerium/internal/policy"
import ( import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net/url" "net/url"
"strings" "strings"
"time" "time"
"github.com/pomerium/pomerium/internal/fileutil"
yaml "gopkg.in/yaml.v2"
) )
// Policy contains authorization policy information. // Policy contains authorization policy information.
// todo(bdd) : add upstream timeout and configuration settings // todo(bdd) : add upstream timeout and configuration settings
type Policy struct { type Policy struct {
// //
From string `yaml:"from"` From string `mapstructure:"from" yaml:"from"`
To string `yaml:"to"` To string `mapstructure:"to" yaml:"to"`
// Identity related policy // Identity related policy
AllowedEmails []string `yaml:"allowed_users"` AllowedEmails []string `mapstructure:"allowed_users" yaml:"allowed_users"`
AllowedGroups []string `yaml:"allowed_groups"` AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups"`
AllowedDomains []string `yaml:"allowed_domains"` AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains"`
Source *url.URL Source *url.URL
Destination *url.URL Destination *url.URL
// Allow unauthenticated HTTP OPTIONS requests as per the CORS spec // Allow unauthenticated HTTP OPTIONS requests as per the CORS spec
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
CORSAllowPreflight bool `yaml:"cors_allow_preflight"` CORSAllowPreflight bool `mapstructure:"cors_allow_preflight" yaml:"cors_allow_preflight"`
// Allow any public request to access this route. **Bypasses authentication** // Allow any public request to access this route. **Bypasses authentication**
AllowPublicUnauthenticatedAccess bool `yaml:"allow_public_unauthenticated_access"` AllowPublicUnauthenticatedAccess bool `mapstructure:"allow_public_unauthenticated_access" yaml:"allow_public_unauthenticated_access"`
// UpstreamTimeout is the route specific timeout. Must be less than the global // UpstreamTimeout is the route specific timeout. Must be less than the global
// timeout. If unset, route will fallback to the proxy's DefaultUpstreamTimeout. // timeout. If unset, route will fallback to the proxy's DefaultUpstreamTimeout.
UpstreamTimeout time.Duration `yaml:"timeout"` UpstreamTimeout time.Duration `mapstructure:"timeout" yaml:"timeout"`
} }
func (p *Policy) validate() (err error) { // Validate parses the source and destination URLs in the Policy
func (p *Policy) Validate() (err error) {
p.Source, err = urlParse(p.From) p.Source, err = urlParse(p.From)
if err != nil { if err != nil {
return err return err
@ -57,37 +54,7 @@ func (p *Policy) validate() (err error) {
return nil return nil
} }
// FromConfig parses configuration file as bytes and returns authorization // URLParse wraps url.Parse to add a scheme if none-exists.
// policies. Supports yaml, json.
func FromConfig(confBytes []byte) ([]Policy, error) {
var f []Policy
if err := yaml.Unmarshal(confBytes, &f); err != nil {
return nil, err
}
// build source and destination urls
for i := range f {
if err := (&f[i]).validate(); err != nil {
return nil, fmt.Errorf("route at index %d: %v", i, err)
}
}
return f, nil
}
// FromConfigFile parses configuration file from a path and returns
// authorization policies. Supports yaml, json.
func FromConfigFile(f string) ([]Policy, error) {
exists, err := fileutil.IsReadableFile(f)
if err != nil || !exists {
return nil, fmt.Errorf("policy file %v: %v exists? %v", f, err, exists)
}
confBytes, err := ioutil.ReadFile(f)
if err != nil {
return nil, err
}
return FromConfig(confBytes)
}
// urlParse wraps url.Parse to add a scheme if none-exists.
// https://github.com/golang/go/issues/12585 // https://github.com/golang/go/issues/12585
func urlParse(uri string) (*url.URL, error) { func urlParse(uri string) (*url.URL, error) {
if !strings.Contains(uri, "://") { if !strings.Contains(uri, "://") {

View file

@ -6,36 +6,6 @@ import (
"testing" "testing"
) )
func TestFromConfig(t *testing.T) {
t.Parallel()
source, _ := urlParse("pomerium.io")
dest, _ := urlParse("httpbin.org")
tests := []struct {
name string
yamlBytes []byte
want []Policy
wantErr bool
}{
{"simple json", []byte(`[{"from": "pomerium.io","to":"httpbin.org"}]`), []Policy{{From: "pomerium.io", To: "httpbin.org", Source: source, Destination: dest}}, false},
{"bad from", []byte(`[{"from": "%","to":"httpbin.org"}]`), nil, true},
{"bad to", []byte(`[{"from": "pomerium.io","to":"%"}]`), nil, true},
{"simple error", []byte(`{}`), nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := FromConfig(tt.yamlBytes)
if (err != nil) != tt.wantErr {
t.Errorf("FromConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("FromConfig() = \n%v, want \n%v", got, tt.want)
}
})
}
}
func Test_urlParse(t *testing.T) { func Test_urlParse(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
@ -62,30 +32,34 @@ func Test_urlParse(t *testing.T) {
} }
} }
func TestFromConfigFile(t *testing.T) { func Test_Validate(t *testing.T) {
t.Parallel() t.Parallel()
source, _ := urlParse("pomerium.io") basePolicy := Policy{From: "httpbin.corp.example", To: "httpbin.corp.notatld"}
dest, _ := urlParse("httpbin.org")
corsPolicy := basePolicy
corsPolicy.CORSAllowPreflight = true
publicPolicy := basePolicy
publicPolicy.AllowPublicUnauthenticatedAccess = true
publicAndWhitelistPolicy := publicPolicy
publicAndWhitelistPolicy.AllowedEmails = []string{"test@gmail.com"}
tests := []struct { tests := []struct {
name string name string
f string policy Policy
want []Policy
wantErr bool wantErr bool
}{ }{
{"simple json", "./testdata/basic.json", []Policy{{From: "pomerium.io", To: "httpbin.org", Source: source, Destination: dest}}, false}, {"good", basePolicy, false},
{"simple yaml", "./testdata/basic.yaml", []Policy{{From: "pomerium.io", To: "httpbin.org", Source: source, Destination: dest}}, false}, {"cors policy", corsPolicy, false},
{"failed dir", "./testdata/", nil, true}, {"public policy", publicPolicy, false},
{"public and whitelist", publicAndWhitelistPolicy, true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := FromConfigFile(tt.f) err := tt.policy.Validate()
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("FromConfigFile() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Validate() error = %v, want %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("FromConfigFile() = %v, want %v", got, tt.want)
} }
}) })
} }

View file

@ -18,7 +18,4 @@
- from: hello.corp.beyondperimeter.com - from: hello.corp.beyondperimeter.com
to: http://hello:8080 to: http://hello:8080
allowed_groups: allowed_groups:
- admins - admins
- from: external-search.corp.beyondperimeter.com
to: google.com
allow_public_unauthenticated_access: true

View file

@ -1,7 +1,6 @@
package proxy package proxy
import ( import (
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -13,6 +12,7 @@ import (
"time" "time"
"github.com/pomerium/pomerium/internal/config" "github.com/pomerium/pomerium/internal/config"
"github.com/pomerium/pomerium/internal/policy"
"github.com/pomerium/pomerium/internal/sessions" "github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/proxy/clients" "github.com/pomerium/pomerium/proxy/clients"
) )
@ -292,26 +292,27 @@ func Test_extendDeadline(t *testing.T) {
} }
func TestProxy_router(t *testing.T) { func TestProxy_router(t *testing.T) {
configBlob := `[{"from":"corp.example.com","to":"example.com"}]` //valid yaml testPolicy := policy.Policy{From: "corp.example.com", To: "example.com"}
policy := base64.URLEncoding.EncodeToString([]byte(configBlob)) testPolicy.Validate()
policies := []policy.Policy{testPolicy}
tests := []struct { tests := []struct {
name string name string
host string host string
mux string mux []policy.Policy
route http.Handler route http.Handler
wantOk bool wantOk bool
}{ }{
{"good corp", "https://corp.example.com", policy, nil, true}, {"good corp", "https://corp.example.com", policies, nil, true},
{"good with slash", "https://corp.example.com/", policy, nil, true}, {"good with slash", "https://corp.example.com/", policies, nil, true},
{"good with path", "https://corp.example.com/123", policy, nil, true}, {"good with path", "https://corp.example.com/123", policies, nil, true},
// {"multiple", "https://corp.example.com/", map[string]string{"corp.unrelated.com": "unrelated.com", "corp.example.com": "example.com"}, nil, true}, // {"multiple", "https://corp.example.com/", map[string]string{"corp.unrelated.com": "unrelated.com", "corp.example.com": "example.com"}, nil, true},
{"bad corp", "https://notcorp.example.com/123", policy, nil, false}, {"bad corp", "https://notcorp.example.com/123", policies, nil, false},
{"bad sub-sub", "https://notcorp.corp.example.com/123", policy, nil, false}, {"bad sub-sub", "https://notcorp.corp.example.com/123", policies, nil, false},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
opts := testOptions() opts := testOptions()
opts.Policy = tt.mux opts.Policies = tt.mux
p, err := New(opts) p, err := New(opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -403,6 +404,7 @@ func TestProxy_Proxy(t *testing.T) {
t.Errorf("\n%+v", opts) t.Errorf("\n%+v", opts)
t.Errorf("\n%+v", ts.URL) t.Errorf("\n%+v", ts.URL)
t.Errorf("handler returned wrong status code: got %v want %v \n body %s", status, tt.wantStatus, w.Body.String())
} }
}) })

View file

@ -42,29 +42,9 @@ func ValidateOptions(o *config.Options) error {
if len(decoded) != 32 { if len(decoded) != 32 {
return fmt.Errorf("authorize: `SHARED_SECRET` want 32 but got %d bytes", len(decoded)) return fmt.Errorf("authorize: `SHARED_SECRET` want 32 but got %d bytes", len(decoded))
} }
if len(o.Routes) != 0 { if len(o.Policies) == 0 {
return errors.New("routes setting is deprecated, use policy instead") return errors.New("missing setting: no policies defined")
} }
if o.Policy == "" && o.PolicyFile == "" {
return errors.New("proxy: either `POLICY` or `POLICY_FILE` must be non-nil")
}
if o.Policy != "" {
confBytes, err := base64.StdEncoding.DecodeString(o.Policy)
if err != nil {
return fmt.Errorf("proxy: `POLICY` is invalid base64 %v", err)
}
_, err = policy.FromConfig(confBytes)
if err != nil {
return fmt.Errorf("proxy: `POLICY` %v", err)
}
}
if o.PolicyFile != "" {
_, err = policy.FromConfigFile(o.PolicyFile)
if err != nil {
return fmt.Errorf("proxy: `POLICY_FILE` %v", err)
}
}
if o.AuthenticateURL == nil { if o.AuthenticateURL == nil {
return errors.New("missing setting: authenticate-service-url") return errors.New("missing setting: authenticate-service-url")
} }
@ -164,14 +144,8 @@ func New(opts *config.Options) (*Proxy, error) {
redirectURL: &url.URL{Path: "/.pomerium/callback"}, redirectURL: &url.URL{Path: "/.pomerium/callback"},
templates: templates.New(), templates: templates.New(),
} }
var policies []policy.Policy
if opts.Policy != "" { for _, route := range opts.Policies {
confBytes, _ := base64.StdEncoding.DecodeString(opts.Policy)
policies, _ = policy.FromConfig(confBytes)
} else {
policies, _ = policy.FromConfigFile(opts.PolicyFile)
}
for _, route := range policies {
proxy := NewReverseProxy(route.Destination) proxy := NewReverseProxy(route.Destination)
handler, err := NewReverseProxyHandler(opts, proxy, &route) handler, err := NewReverseProxyHandler(opts, proxy, &route)
if err != nil { if err != nil {

View file

@ -1,8 +1,6 @@
package proxy // import "github.com/pomerium/pomerium/proxy" package proxy // import "github.com/pomerium/pomerium/proxy"
import ( import (
"encoding/base64"
"fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
@ -58,11 +56,10 @@ func TestNewReverseProxyHandler(t *testing.T) {
proxyHandler := NewReverseProxy(proxyURL) proxyHandler := NewReverseProxy(proxyURL)
opts := config.NewOptions() opts := config.NewOptions()
opts.SigningKey = "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU0zbXBaSVdYQ1g5eUVneFU2czU3Q2J0YlVOREJTQ0VBdFFGNWZVV0hwY1FvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFaFBRditMQUNQVk5tQlRLMHhTVHpicEVQa1JyazFlVXQxQk9hMzJTRWZVUHpOaTRJV2VaLwpLS0lUdDJxMUlxcFYyS01TYlZEeXI5aWp2L1hoOThpeUV3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=" opts.SigningKey = "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU0zbXBaSVdYQ1g5eUVneFU2czU3Q2J0YlVOREJTQ0VBdFFGNWZVV0hwY1FvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFaFBRditMQUNQVk5tQlRLMHhTVHpicEVQa1JyazFlVXQxQk9hMzJTRWZVUHpOaTRJV2VaLwpLS0lUdDJxMUlxcFYyS01TYlZEeXI5aWp2L1hoOThpeUV3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
route, err := policy.FromConfig([]byte(`[{"from":"corp.example.com","to":"example.com","timeout":"1s"}]`)) testPolicy := policy.Policy{From: "corp.example.com", To: "example.com", UpstreamTimeout: 1 * time.Second}
if err != nil { testPolicy.Validate()
t.Fatal(err)
} handle, err := NewReverseProxyHandler(opts, proxyHandler, &testPolicy)
handle, err := NewReverseProxyHandler(opts, proxyHandler, &route[0])
if err != nil { if err != nil {
t.Errorf("got %q", err) t.Errorf("got %q", err)
} }
@ -83,11 +80,11 @@ func TestNewReverseProxyHandler(t *testing.T) {
func testOptions() *config.Options { func testOptions() *config.Options {
authenticateService, _ := url.Parse("https://authenticate.corp.beyondperimeter.com") authenticateService, _ := url.Parse("https://authenticate.corp.beyondperimeter.com")
authorizeService, _ := url.Parse("https://authorize.corp.beyondperimeter.com") authorizeService, _ := url.Parse("https://authorize.corp.beyondperimeter.com")
configBlob := `[{"from":"corp.example.notatld","to":"example.notatld"}]`
policy := base64.URLEncoding.EncodeToString([]byte(configBlob))
opts := config.NewOptions() opts := config.NewOptions()
opts.Policy = policy testPolicy := policy.Policy{From: "corp.example.notatld", To: "example.notatld"}
testPolicy.Validate()
opts.Policies = []policy.Policy{testPolicy}
opts.AuthenticateURL = authenticateService opts.AuthenticateURL = authenticateService
opts.AuthorizeURL = authorizeService opts.AuthorizeURL = authorizeService
opts.SharedKey = "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ=" opts.SharedKey = "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ="
@ -100,11 +97,13 @@ func testOptionsTestServer(uri string) *config.Options {
authenticateService, _ := url.Parse("https://authenticate.corp.beyondperimeter.com") authenticateService, _ := url.Parse("https://authenticate.corp.beyondperimeter.com")
authorizeService, _ := url.Parse("https://authorize.corp.beyondperimeter.com") authorizeService, _ := url.Parse("https://authorize.corp.beyondperimeter.com")
// RFC 2606 // RFC 2606
configBlob := fmt.Sprintf(`[{"from":"httpbin.corp.example","to":"%s"}]`, uri) testPolicy := policy.Policy{
policy := base64.URLEncoding.EncodeToString([]byte(configBlob)) From: "httpbin.corp.example",
To: uri,
}
testPolicy.Validate()
opts := config.NewOptions() opts := config.NewOptions()
opts.Policy = policy opts.Policies = []policy.Policy{testPolicy}
opts.AuthenticateURL = authenticateService opts.AuthenticateURL = authenticateService
opts.AuthorizeURL = authorizeService opts.AuthorizeURL = authorizeService
opts.SharedKey = "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ=" opts.SharedKey = "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ="
@ -114,33 +113,44 @@ func testOptionsTestServer(uri string) *config.Options {
} }
func testOptionsWithCORS(uri string) *config.Options { func testOptionsWithCORS(uri string) *config.Options {
configBlob := fmt.Sprintf(`[{"from":"httpbin.corp.example","to":"%s","cors_allow_preflight":true}]`, uri) testPolicy := policy.Policy{
From: "httpbin.corp.example",
To: uri,
CORSAllowPreflight: true,
}
testPolicy.Validate()
opts := testOptionsTestServer(uri) opts := testOptionsTestServer(uri)
opts.Policy = base64.URLEncoding.EncodeToString([]byte(configBlob)) opts.Policies = []policy.Policy{testPolicy}
return opts return opts
} }
func testOptionsWithPublicAccess(uri string) *config.Options { func testOptionsWithPublicAccess(uri string) *config.Options {
configBlob := fmt.Sprintf(`[{"from":"httpbin.corp.example","to":"%s","allow_public_unauthenticated_access":true}]`, uri) testPolicy := policy.Policy{
From: "httpbin.corp.example",
To: uri,
AllowPublicUnauthenticatedAccess: true,
}
testPolicy.Validate()
opts := testOptions() opts := testOptions()
opts.Policy = base64.URLEncoding.EncodeToString([]byte(configBlob)) opts.Policies = []policy.Policy{testPolicy}
return opts return opts
} }
func testOptionsWithPublicAccessAndWhitelist(uri string) *config.Options { func testOptionsWithPublicAccessAndWhitelist(uri string) *config.Options {
configBlob := fmt.Sprintf(`[{"from":"httpbin.corp.example","to":"%s","allow_public_unauthenticated_access":true,"allowed_users":["test@gmail.com"]}]`, uri) testPolicy := policy.Policy{
From: "httpbin.corp.example",
To: uri,
AllowPublicUnauthenticatedAccess: true,
AllowedEmails: []string{"test@gmail.com"},
}
testPolicy.Validate()
opts := testOptions() opts := testOptions()
opts.Policy = base64.URLEncoding.EncodeToString([]byte(configBlob)) opts.Policies = []policy.Policy{testPolicy}
return opts return opts
} }
func TestOptions_Validate(t *testing.T) { func TestOptions_Validate(t *testing.T) {
good := testOptions() good := testOptions()
badFromRoute := testOptions()
badFromRoute.Routes = map[string]string{"example.com": "^"}
badToRoute := testOptions()
badToRoute.Routes = map[string]string{"^": "example.com"}
badAuthURL := testOptions() badAuthURL := testOptions()
badAuthURL.AuthenticateURL = nil badAuthURL.AuthenticateURL = nil
authurl, _ := url.Parse("http://authenticate.corp.beyondperimeter.com") authurl, _ := url.Parse("http://authenticate.corp.beyondperimeter.com")
@ -160,15 +170,8 @@ func TestOptions_Validate(t *testing.T) {
invalidSignKey.SigningKey = "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw^" invalidSignKey.SigningKey = "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw^"
badSharedKey := testOptions() badSharedKey := testOptions()
badSharedKey.SharedKey = "" badSharedKey.SharedKey = ""
policyBadBase64 := testOptions() missingPolicy := testOptions()
policyBadBase64.Policy = "^" missingPolicy.Policies = []policy.Policy{}
badPolicyToURL := testOptions()
badPolicyToURL.Policy = "LSBmcm9tOiBodHRwYmluLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vaHR0cGJpbl4KICBhbGxvd2VkX2RvbWFpbnM6CiAgICAtIHBvbWVyaXVtLmlv"
badPolicyFromURL := testOptions()
badPolicyFromURL.Policy = "LSBmcm9tOiBodHRwYmluLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vaHR0cGJpbl4KICBhbGxvd2VkX2RvbWFpbnM6CiAgICAtIHBvbWVyaXVtLmlv"
corsPolicy := testOptionsWithCORS("example.notatld")
publicPolicy := testOptionsWithPublicAccess("example.notatld")
publicWithWhitelistPolicy := testOptionsWithPublicAccessAndWhitelist("example.notatld")
tests := []struct { tests := []struct {
name string name string
@ -177,8 +180,6 @@ func TestOptions_Validate(t *testing.T) {
}{ }{
{"good - minimum options", good, false}, {"good - minimum options", good, false},
{"nil options", &config.Options{}, true}, {"nil options", &config.Options{}, true},
{"from route", badFromRoute, true},
{"to route", badToRoute, true},
{"authenticate service url", badAuthURL, true}, {"authenticate service url", badAuthURL, true},
{"authenticate service url not https", authenticateBadScheme, true}, {"authenticate service url not https", authenticateBadScheme, true},
{"authorize service url not https", authorizeBadSCheme, true}, {"authorize service url not https", authorizeBadSCheme, true},
@ -188,12 +189,7 @@ func TestOptions_Validate(t *testing.T) {
{"short cookie secret", shortCookieLength, true}, {"short cookie secret", shortCookieLength, true},
{"no shared secret", badSharedKey, true}, {"no shared secret", badSharedKey, true},
{"invalid signing key", invalidSignKey, true}, {"invalid signing key", invalidSignKey, true},
{"policy invalid base64", policyBadBase64, true}, {"missing policy", missingPolicy, true},
{"policy bad to url", badPolicyFromURL, true},
{"policy bad from url", badPolicyFromURL, true},
{"CORS policy good", corsPolicy, false},
{"policy public good", publicPolicy, false},
{"policy public and whitelist bad", publicWithWhitelistPolicy, true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {