mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-30 19:06:33 +02:00
Switch options parsing to viper
This commit is contained in:
parent
702cc30b77
commit
febf9464a4
18 changed files with 798 additions and 326 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
25
config-policy-only.yaml
Normal 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
89
config.example.yaml
Normal 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
|
|
@ -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`
|
||||||
|
|
|
@ -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
2
go.mod
|
@ -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
105
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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, "://") {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue