Pomerium: authorize does not check 'to' field in policy configuration

Created on 30 Aug 2019  Â·  9Comments  Â·  Source: pomerium/pomerium

Describe the solution you'd like

Currently the authorizer requires a minimal config as follows

policy:
  - from: https://httpbin.domain.tld
    to: http://httpbin
    allowed_domains:
      - domain.tld
  - from: https://external-httpbin.domain.tld
    to: http://httpbin
    allow_public_unauthenticated_access: true

If to: section is removed, it will complain that it is missing. Ideally, it should allow to be empty as https://github.com/pomerium/pomerium/blob/master/authorize/identity.go#L64-L77 does not use the to: field (which makes sense as policies are usually set for external domains).

This would allow for this sort of config, which simplifies and detaches authz and authn responsibilities completely, as they dont have to know about each other.

  • authz.yml
policy:
  - from: https://httpbin.domain.tld
    allowed_domains:
      - domain.tld
  • proxy.yml
authenticate_service_url: https://authn.domain.tld
authorize_service_url: https://authz.domain.tld
policy:
  - from: https://httpbin.domain.tld
    to: http://httpbin
  - from: https://external-httpbin.domain.tld
    to: http://httpbin
    allow_public_unauthenticated_access: true
  • authn.yml
authenticate_service_url: https://authn.domain.tld
idp_provider: google
idp_client_id: REPLACE_ME
idp_client_secret: REPLACE_ME
# idp_service_account: REPLACE_ME

Workaround

As a workaround, we can just set authz configuration to dummy values

policy:
  - from: https://httpbin.domain.tld
    to: https://domain.tld
    allowed_domains:
      - domain.tld
WaitingForInfo

All 9 comments

Hi @pecigonzalo,

Thanks for opening this.

Operationally, I think it makes more sense to keep policy as a format across components, even if they don't all need every field. It's easier to validate you're consistent and you can share/review configurations directly. Less chance of error.

That said, I think you've identified something we should actually improve. to is the backend service, and I think the backend target of a policy entry is as important - if not more important - than the from that a user hit on the frontend.

By only checking from, we open the door for some mistakes, unintended behavior, and potential inconsistency in access to a given service. In a situation where different teams are responsible for proxy mappings and service access policies, the latter group would not have any way of knowing what service is actually behind a given proxy route without knowing the to of a route. Arguably, they might _only_ care about to.

I think Authorize should really be checking both to and from to make sure the right mapping is being examined, and make it clear to a reader of the policy exactly what is being authorized. It also ensures everyone is on the same page with the config state.

@travisgroth I think its not a bad idea to check both, but in my opinion, it should be optional. If you think of a running Kube Pod, you might be pointing to localhost to proxy the service or some other internal DNS name that the Authorizer should not need to care about.

I would even say that optionally it would be good to allow proxy to just have a to: without a from: as it would allow for simpler configuration similar to what you can with NGINX or any other web proxy.

While I understand your point of view regarding strict to and from, I think enforcing this at the application level only limits the use cases or increases the complexity of deployment for an opinionated security limitation (no matter if wrong or right).

I would even say splitting proxy into its own section, irrelevant from policy is even better, for a small code change to support 2 formats, we decompose what is truly 2 completely separate configurations.

I would even love to run this without the proxy component, as there are other well known services for doing this already, and just use nging with auth_request or https://github.com/tarent/loginsrv#httpupstream with caddy.

I think its not a bad idea to check both, but in my opinion, it should be optional. If you think of a running Kube Pod, you might be pointing to localhost to proxy the service or some other internal DNS name that the Authorizer should not need to care about.

One question - what's your deployment model that you'd rather have two distinct subsets of policy, rather than using the same file across your proxy and authorize services? Most (all?) of our documentation and examples use the same configuration across all services so I'm curious if there's use cases that this doesn't account for. Are you deploying proxies coupled with your individual services?

Making it an operational decision as to which parts of the route to check might make sense. I think from a best practices perspective you should be checking both to ensure that the external route is indeed pointed at the target you intended, even if it isn't extremely unique. Making sure that myservice.corp.beyondcorp.io actually goes to internalservice.intranet and not another sensitive application still provides value and security. Even if that target is localhost, Authorize should be able to validate that the proxy is sending traffic to what the proxy considers localhost at request time.

Enabling destination checking, but not requiring it, is a question of how much rope to leave the operator in the case of a mistake (omitting a to). The implementation sounds easy offhand either way. We could require at least one of to or from. @desimone what's your perspective here?

I would even say splitting proxy into its own section, irrelevant from policy is even better, for a small code change to support 2 formats, we decompose what is truly 2 completely separate configurations.

This might be a road we embark on eventually but I'd like to set that aside that as a separate discussion since there's a lot more to consider than policy semantics. This would probably come out of other decisions.

I would even love to run this without the proxy component, as there are other well known services for doing this already, and just use nging with auth_request

The current proxy is meant to be generic enough to protect traffic to many other endpoints that might be nginx, caddy, envoy, gloo, etc. There's definitely discussion to be had about the implementation, but supporting the various third party proxies and their auth mechanisms is potentially a large undertaking. Pomerium doesn't currently implement an IDP, so http basic subrequests are out. We'd be limited most likely to JWT driven schemes or rely on plugins to implement our RPC protocol. As an example, you could probably write a caddy module do authz checks to Pomerium.

A notable exception might be envoy, which has a full RPC protocol for request checking. In theory this could be implemented in Pomerium but it hasn't been thoroughly researched yet.

My general instinct is that it would be difficult to maintain feature parity if we supported _many_ proxies, rather than having one that is well integrated, understood and appropriately generic. Pomerium's proxy is not meant to replace any service (reverse proxy or otherwise), but to provide an auth layer in front of the ones you are already using.

This isn't to say that third party efforts for a proposal or plugin would be discouraged - it just isn't core roadmap. I think we would try to support such initiatives if someone wanted to take them up.

@desimone might have more here as well.

what's your deployment model that you'd rather have two distinct subsets of policy, rather than using the same file across your proxy and authorize services? Are you deploying proxies coupled with your individual services?

Correct I am, as authentication and authorization can be central things, they are deployed at a cluster level, but I believe its much better to have a proxy per service and not have a central proxy that has to know all. I believe this is more inline with microservices and deployment workflows in common orchestrators like Kube, Docker Swarm, etc

That way, the proxy can have a simpler config from: my external name | to: localhost(or some other internal name) and have less of a "central head".
If you check the blog article about Buzzfeed SSO (which this is a fork of) they make some similar references, or if you check https://www.ory.sh/ or https://www.keycloak.org/ or https://www.gluu.org/ they allow for this pattern where a central server takes care of auth, another server has the RBAC/ACL/whatever access rules and then you have "proxies" that use them to authn/authz.

This in my opinion is way more extensible, as otherwise internal routing changes have to be coupled with the authorization and proxy servers.

The current proxy is meant to be generic enough to protect traffic to many other endpoints that might be nginx, caddy, envoy, gloo, etc. There's definitely discussion to be had about the implementation, but supporting the various third party proxies and their auth mechanisms is potentially a large undertaking.

I was not suggesting to directly support any, but rather support a /authorize or similar endpoint, that is the way they integrate.
Check:

As you can see, NGINX just makes a call to the endpoint, and then you validate/reject based on the parameters provided.

The reason for this is that many proxies behave differently, and adding more layers pom-proxy->nginx->webservice makes configs more complex, as you have to ensure external IP is retained, cookies, http2 is supported or not, etc.

First off — thank you @pecigonzalo for making this issue and @travisgroth for your contribution to the conversation.

So just to recap. Though all agree that authorization and authentication services should be centralized, from what I’ve been able to parse there is some question as to whether:

  • the proxy component should be decoupled from access control decisions
  • access policy should be agnostic to route location
  • multiple, distributed pomerium proxies should be able to be placed lower in the stack, in front of individual services (akin to envoy’s side-car model?

Do I have that right? @pecigonzalo do you mind describing the ideal request flow and what that environment would roughly look? Do you imagine both authentication and authorization privileges be derived from a something like the (above described) JWT.

@travisgroth I just noticed the new name, which I dont believe correctly reflect my "issue" here, could we rename back?

@desimone

the proxy component should be decoupled from access control decisions

I think that would be ideal, proxy just uses gRPC to query and validate against authn or authz, as it does right now, then just forwards traffic as specified by a ruleset.

access policy should be agnostic to route location

In my opinion, yes, but I dont think its a bad idea to allow for both (validating to: and from:) to allow for strict rules if required.

multiple, distributed pomerium proxies should be able to be placed lower in the stack, in front of individual services (akin to envoy’s side-car model?

Similar yes, but a little bit higher as envoy would be service to service, the idea of course is not to replace or replicate envoy functionality.


I think all the points are correct. Ill try and draw a request flow soonish, but in general is as you described.

  • Authentication deployed as a "service" behind some client reachable url
  • Authorization deployed as a "service" behind some client reachable url
  • Proxies deployment in one of 2 fashions

    • Front proxies fleet that has all rules

    • Per-Service proxy "service" that contains only routing rules for that service or subset of services

@pecigonzalo

I think there are few distinct issues described here, some of which I think have been addressed by the solution Implemented in #32 but I want to circle back around with you.

  • Authentication deployed as a "service" behind some client reachable url
  • Authorization deployed as a "service" behind some client reachable url

This has taken the form of the new forward-auth endpoint (https://www.pomerium.io/docs/reference/reference.html#forward-auth).

  • Per-Service proxy "service" that contains only routing rules for that service or subset of services

This I am less clear about in practice, as it's not a use case I have seen or thought about prior to now (not that it means it's wrong at all, just that I'm new to it).

Thanks!

I think there are few distinct issues described here

Yes I agree and as you say, some of them have been resolved already, my intention was no to "handle" them as part of this issue, but rather the natural discussion and context of this issue lead to also touching on them.

This I am less clear about in practice, as it's not a use case I have seen or thought about prior to now (not that it means it's wrong at all, just that I'm new to it).
It should be rather simple, it's just a bunch of proxies "querying" central AuthN and AuthZ services, the services I linked before (ory, keycloack, gluu) implement them as well.

I really would like to use it as it prevents me from having to centrally modify a proxy service when creating or modifying an AuthN/Z service, and instead, just expose it with its new proxy in front of it, but still providing a central ACL/RBAC/etc list for AuthZ. I rather keep it simpler than that, as that one of the reasons I prefer pomerium, but check how the ORY does RBAC/ACL.

Let me know if there is something else I can clarify or assist.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

desimone picture desimone  Â·  4Comments

amiral-fu picture amiral-fu  Â·  6Comments

yaroot picture yaroot  Â·  3Comments

deltabweb picture deltabweb  Â·  9Comments

shreyaskarnik picture shreyaskarnik  Â·  4Comments