diff --git a/CHANGELOG.md b/CHANGELOG.md index 33893fb36..c7c783761 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ ### CHANGED -- Removed `PROXY_ROOT_DOMAIN` config option which is now inferred from `AUTHENTICATE_SERVICE_URL`. Only callback requests originating from a URL on the same sub-domain are permitted. -- Removed `REDIRECT_URL` config option which is now inferred from `AUTHENTICATE_SERVICE_URL` (e.g. `https://$AUTHENTICATE_SERVICE_URL/oauth2/callback`). +- Removed extraneous `AUTHORIZE_INTERNAL_URL` config option since authorization has no publica http handlers, only a gRPC service endpoint. [GH-93] +- Removed `PROXY_ROOT_DOMAIN` config option which is now inferred from `AUTHENTICATE_SERVICE_URL`. Only callback requests originating from a URL on the same sub-domain are permitted. [GH-83] +- Removed `REDIRECT_URL` config option which is now inferred from `AUTHENTICATE_SERVICE_URL` (e.g. `https://$AUTHENTICATE_SERVICE_URL/oauth2/callback`). [GH-83] ### FIXED diff --git a/Dockerfile b/Dockerfile index 8d488f456..a87676b02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,11 @@ ENV CGO_ENABLED=0 ENV GO111MODULE=on WORKDIR /go/src/github.com/pomerium/pomerium + +COPY go.mod . +COPY go.sum . +RUN go mod download + COPY . . RUN make diff --git a/docs/docs/config-reference.md b/docs/docs/config-reference.md index a4e97bb95..32c4bf8b7 100644 --- a/docs/docs/config-reference.md +++ b/docs/docs/config-reference.md @@ -12,7 +12,7 @@ Pomerium uses [environmental variables] to set configuration settings. If you ar ## Global settings -Global settings are configuration variables that are shared by all services. +These are configuration variables shared by all services, in all service modes. ### Service Mode @@ -21,7 +21,7 @@ Global settings are configuration variables that are shared by all services. - Default: `all` - Options: `all` `authenticate` `authorize` or `proxy` -Service mode sets the pomerium service(s) to run. If testing, you may want to set to `all` and run pomerium in "all-in-one mode." In production, you'll likely want to spin of several instances of each service mode for high availability. +Service mode sets the pomerium service(s) to run. If testing, you may want to set to `all` and run pomerium in "all-in-one mode." In production, you'll likely want to spin up several instances of each service mode for high availability. ### Address @@ -29,7 +29,7 @@ Service mode sets the pomerium service(s) to run. If testing, you may want to se - Type: `string` - Default: `:https` -Address specifies the host and port to serve HTTPS and gRPC requests from. If empty, `:https` is used. +Address specifies the host and port to serve HTTPS and gRPC requests from. If empty, `:https`/`:443` is used. ### Shared Secret @@ -194,25 +194,18 @@ Authenticate Service URL is the externally accessible URL for the authenticate s - Optional - Example: `pomerium-authenticate-service.pomerium.svc.cluster.local` -Authenticate Internal Service URL is the internally routed dns name of the authenticate service. This setting is used to override the authenticate service url for when you need to do "behind-the-ingress" inter-service communication. This is typically required for ingresses and load balancers that do not support HTTP/2 or gRPC termination. +Authenticate Internal Service URL is the internally routed dns name of the authenticate service. This setting is typically used with load balancers that do not gRPC, thus allowying you to specificy an internally routable name. ### Authorize Service URL - Environmental Variable: `AUTHORIZE_SERVICE_URL` - Type: `URL` - Required -- Example: `https://access.corp.example.com` +- Example: `https://access.corp.example.com` or `pomerium-authorize-service.pomerium.svc.cluster.local` -Authorize Service URL is the externally accessible URL for the authorize service. +Authorize Service URL is the location of the internally routable authorize service. NOTE: Unlike authenticate, authorize has no publically acccessible http handlers so this setting is purely for gRPC communicaiton. -### Authorize Internal Service URL - -- Environmental Variable: `AUTHORIZE_INTERNAL_URL` -- Type: `string` -- Optional -- Example: `pomerium-authorize-service.pomerium.svc.cluster.local` - -Authorize Internal Service URL is the internally routed dns name of the authorize service. This setting is used to override the authorize service url for when you need to do "behind-the-ingress" inter-service communication. This is typically required for ingresses and load balancers that do not support HTTP/2 or gRPC termination. +If your load balancer does not support gRPC passthrough you'll need to set this value to an internally routable location (`pomerium-authorize-service.pomerium.svc.cluster.local`) instead of an externally routable one (`https://access.corp.example.com`). ### Override Certificate Name diff --git a/docs/docs/examples.md b/docs/docs/examples.md index 76fd57724..ebd5e7528 100644 --- a/docs/docs/examples.md +++ b/docs/docs/examples.md @@ -60,8 +60,9 @@ Customize for your identity provider run `docker-compose up -f nginx.docker-comp - Uses Google Kubernetes Engine's built-in ingress to do [HTTPS load balancing] - HTTPS (TLS) between client, load balancer, and services +- gRPC requests are routed behind the load balancer - Routes default to hosted version of httpbin.org -- Includes all-in-one script +- Includes installer script #### helm_gke.sh @@ -71,8 +72,9 @@ Customize for your identity provider run `docker-compose up -f nginx.docker-comp - Uses Google Kubernetes Engine's built-in ingress to do [HTTPS load balancing] - HTTPS (TLS) between client, load balancer, and services +- gRPC requests are routed behind the load balancer - Routes default to hosted version of httpbin.org -- Includes all-in-one script +- Includes installer script #### kubernetes_gke diff --git a/docs/docs/examples/docker/nginx.docker-compose.yml b/docs/docs/examples/docker/nginx.docker-compose.yml index 581635ece..87d263b24 100644 --- a/docs/docs/examples/docker/nginx.docker-compose.yml +++ b/docs/docs/examples/docker/nginx.docker-compose.yml @@ -44,11 +44,10 @@ services: - SERVICES=proxy - POLICY_FILE=policy.yaml - AUTHENTICATE_SERVICE_URL=https://authenticate.corp.beyondperimeter.com - - AUTHORIZE_SERVICE_URL=https://authorize.corp.beyondperimeter.com # IMPORTANT! If you are running pomerium behind another ingress (loadbalancer/firewall/etc) # you must tell pomerium proxy how to communicate using an internal hostname for RPC - - AUTHENTICATE_INTERNAL_URL=pomerium-authenticate:443 - - AUTHORIZE_INTERNAL_URL=pomerium-authorize:443 + - AUTHENTICATE_INTERNAL_URL=pomerium-authenticate + - AUTHORIZE_SERVICE_URL=https://pomerium-authorize # When communicating internally, rPC is going to get a name conflict expecting an external # facing certificate name (i.e. authenticate-service.local vs *.corp.example.com). - OVERRIDE_CERTIFICATE_NAME=*.corp.beyondperimeter.com diff --git a/docs/docs/examples/kubernetes/proxy.deploy.yml b/docs/docs/examples/kubernetes/proxy.deploy.yml index 633741280..17c0c5bbe 100644 --- a/docs/docs/examples/kubernetes/proxy.deploy.yml +++ b/docs/docs/examples/kubernetes/proxy.deploy.yml @@ -26,9 +26,7 @@ spec: - name: SERVICES value: proxy - name: AUTHORIZE_SERVICE_URL - value: https://authorize.corp.beyondperimeter.com - - name: AUTHORIZE_INTERNAL_URL - value: "pomerium-authorize-service.pomerium.svc.cluster.local" + value: https://pomerium-authorize-service.pomerium.svc.cluster.local - name: AUTHENTICATE_SERVICE_URL value: https://authenticate.corp.beyondperimeter.com - name: AUTHENTICATE_INTERNAL_URL diff --git a/docs/guide/synology.md b/docs/guide/synology.md index e1db299cd..451462515 100644 --- a/docs/guide/synology.md +++ b/docs/guide/synology.md @@ -175,10 +175,9 @@ IDP_CLIENT_ID | Values from setting up your [identity provider] IDP_PROVIDER | Values from setting up your [identity provider] (e.g. `google`) COOKIE_SECRET | output of `head -c32 /dev/urandom | base64` SHARED_SECRET | output of `head -c32 /dev/urandom | base64` -AUTHORIZE_SERVICE_URL | `https://authorize.int.nas.example.com` +AUTHORIZE_SERVICE_URL | `https://localhost` AUTHENTICATE_SERVICE_URL | `https://authenticate.int.nas.example.com` -AUTHORIZE_INTERNAL_URL | `localhost:443` -AUTHENTICATE_INTERNAL_URL | `localhost:443` +AUTHENTICATE_INTERNAL_URL | `localhost` For a detailed explanation, and additional options, please refer to the [configuration variable docs]. diff --git a/proxy/handlers.go b/proxy/handlers.go index f1c764d9a..3305429ed 100644 --- a/proxy/handlers.go +++ b/proxy/handlers.go @@ -35,7 +35,7 @@ type StateParameter struct { RedirectURI string `json:"redirect_uri"` } -// Handler returns a http handler for an Proxy +// Handler returns a http handler for a Proxy func (p *Proxy) Handler() http.Handler { // routes mux := http.NewServeMux() @@ -71,10 +71,9 @@ func (p *Proxy) Handler() http.Handler { c = c.Append(middleware.ValidateHost(p.mux)) // serve the middleware and mux - h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + return c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.ServeHTTP(w, r) })) - return h } // RobotsTxt sets the User-Agent header in the response to be "Disallow" diff --git a/proxy/proxy.go b/proxy/proxy.go index 66d4b112a..8cbff7670 100755 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -38,12 +38,18 @@ type Options struct { Policy string `envconfig:"POLICY"` PolicyFile string `envconfig:"POLICY_FILE"` - // Authenticate service settings - AuthenticateURL *url.URL `envconfig:"AUTHENTICATE_SERVICE_URL"` - AuthenticateInternalAddr string `envconfig:"AUTHENTICATE_INTERNAL_URL"` - // Authorize service settings - AuthorizeURL *url.URL `envconfig:"AUTHORIZE_SERVICE_URL"` - AuthorizeInternalAddr string `envconfig:"AUTHORIZE_INTERNAL_URL"` + // AuthenticateURL represents the externally accessible http endpoints + // used for authentication requests and callbacks + AuthenticateURL *url.URL `envconfig:"AUTHENTICATE_SERVICE_URL"` + // AuthenticateInternalAddr is used as an override when using a load balancer + // or ingress that does not natively support routing gRPC. + AuthenticateInternalAddr string `envconfig:"AUTHENTICATE_INTERNAL_URL"` + + // AuthorizeURL is the routable destination of the authorize service's + // gRPC endpoint. NOTE: As above, many load balancers do not support + // externally routed gRPC so this may be an internal location. + AuthorizeURL *url.URL `envconfig:"AUTHORIZE_SERVICE_URL"` + // Settings to enable custom behind-the-ingress service communication OverrideCertificateName string `envconfig:"OVERRIDE_CERTIFICATE_NAME"` CA string `envconfig:"CERTIFICATE_AUTHORITY"` @@ -98,32 +104,24 @@ func (o *Options) Validate() error { if o.Policy == "" && o.PolicyFile == "" { return errors.New("proxy: either `POLICY` or `POLICY_FILE` must be non-nil") } - var policies []policy.Policy var err error if o.Policy != "" { confBytes, err := base64.StdEncoding.DecodeString(o.Policy) if err != nil { return fmt.Errorf("proxy: `POLICY` is invalid base64 %v", err) } - policies, err = policy.FromConfig(confBytes) + _, err = policy.FromConfig(confBytes) if err != nil { return fmt.Errorf("proxy: `POLICY` %v", err) } } if o.PolicyFile != "" { - policies, err = policy.FromConfigFile(o.PolicyFile) + _, err = policy.FromConfigFile(o.PolicyFile) if err != nil { return fmt.Errorf("proxy: `POLICY_FILE` %v", err) } } - for _, p := range policies { - if _, err := urlParse(p.To); err != nil { - return fmt.Errorf("could not parse source %s url: %v", p.To, err) - } - if _, err := urlParse(p.From); err != nil { - return fmt.Errorf("could not parse destination %s url: %v", p.From, err) - } - } + if o.AuthenticateURL == nil { return errors.New("missing setting: authenticate-service-url") } @@ -253,7 +251,6 @@ func New(opts *Options) (*Proxy, error) { p.AuthorizeClient, err = clients.NewAuthorizeClient("grpc", &clients.Options{ Addr: opts.AuthorizeURL.Host, - InternalAddr: opts.AuthorizeInternalAddr, OverrideCertificateName: opts.OverrideCertificateName, SharedSecret: opts.SharedKey, CA: opts.CA, @@ -328,10 +325,7 @@ func NewReverseProxyHandler(o *Options, proxy *httputil.ReverseProxy, route *pol cookieName: o.CookieName, } if len(o.SigningKey) != 0 { - decodedSigningKey, err := base64.StdEncoding.DecodeString(o.SigningKey) - if err != nil { - return nil, err - } + decodedSigningKey, _ := base64.StdEncoding.DecodeString(o.SigningKey) signer, err := cryptutil.NewES256Signer(decodedSigningKey, route.Source.Host) if err != nil { return nil, err diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 090ce52d7..bb3638eee 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -136,8 +136,12 @@ func TestOptions_Validate(t *testing.T) { badAuthURL := testOptions() badAuthURL.AuthenticateURL = nil authurl, _ := url.Parse("http://authenticate.corp.beyondperimeter.com") - httpAuthURL := testOptions() - httpAuthURL.AuthenticateURL = authurl + authenticateBadScheme := testOptions() + authenticateBadScheme.AuthenticateURL = authurl + authorizeBadSCheme := testOptions() + authorizeBadSCheme.AuthorizeURL = authurl + authorizeNil := testOptions() + authorizeNil.AuthorizeURL = nil emptyCookieSecret := testOptions() emptyCookieSecret.CookieSecret = "" invalidCookieSecret := testOptions() @@ -148,6 +152,12 @@ func TestOptions_Validate(t *testing.T) { invalidSignKey.SigningKey = "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw^" badSharedKey := testOptions() badSharedKey.SharedKey = "" + policyBadBase64 := testOptions() + policyBadBase64.Policy = "^" + badPolicyToURL := testOptions() + badPolicyToURL.Policy = "LSBmcm9tOiBodHRwYmluLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vaHR0cGJpbl4KICBhbGxvd2VkX2RvbWFpbnM6CiAgICAtIHBvbWVyaXVtLmlv" + badPolicyFromURL := testOptions() + badPolicyFromURL.Policy = "LSBmcm9tOiBodHRwYmluLmNvcnAuYmV5b25kcGVyaW1ldGVyLmNvbQogIHRvOiBodHRwOi8vaHR0cGJpbl4KICBhbGxvd2VkX2RvbWFpbnM6CiAgICAtIHBvbWVyaXVtLmlv" tests := []struct { name string @@ -158,13 +168,18 @@ func TestOptions_Validate(t *testing.T) { {"nil options", &Options{}, true}, {"from route", badFromRoute, true}, {"to route", badToRoute, true}, - {"auth service url", badAuthURL, true}, - {"auth service url not https", httpAuthURL, true}, + {"authenticate service url", badAuthURL, true}, + {"authenticate service url not https", authenticateBadScheme, true}, + {"authorize service url not https", authorizeBadSCheme, true}, + {"authorize service cannot be nil", authorizeNil, true}, {"no cookie secret", emptyCookieSecret, true}, {"invalid cookie secret", invalidCookieSecret, true}, {"short cookie secret", shortCookieLength, true}, {"no shared secret", badSharedKey, true}, {"invalid signing key", invalidSignKey, true}, + {"policy invalid base64", policyBadBase64, true}, + {"policy bad to url", badPolicyFromURL, true}, + {"policy bad from url", badPolicyFromURL, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {