-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Load multiple policy bundles #721
Comments
hi @stan-podolski. OPA supports multiple services so that different backends can be used for bundle downloading, status updates, decision logs, etc. Can you provide a bit more detail about why you need to load bundles from multiple sources into OPA? FWIW, the bundle that you serve to OPA could contain many different policies (as well as the data they depend on). If you do have multiple sources of policy and data, one option would be to combine them in your bundle service implementation. |
Hi @tsandall, my use case is such that I have a number of independent teams, which manage their individual policies. They also have independent release cycles. |
That makes sense. By restricting the agent to a single bundle, we ensure that the bundle management is largely orthogonal to other capabilities in OPA. For example, OPA does not have to map policy decisions => bundles in the event log. Another example, is optimization; administrators could reasonably expect OPA to be optimize across bundles (e.g., by deduplicating the data.) Hope this helps. I'm going to close this issue for now. If you have other questions feel free to post issues or ask us on Slack. |
I'm re-opening this issue because a few users have requested it. Now that we let bundles declare the root(s) they own, we could have multiple (non-overlapping) bundles loaded at once. The status and decision log plugins will need to take multiple bundles into account:
|
I've been poking around at this and have a proposal for some changes, would be great to get some feedback from folks who would want to use this. I want to make sure we can handle the desired use-cases nicely. Proposed ChangesConfigCurrently we configure bundles with a map like: services:
acmecorp:
url: https://example.com/service/v1
bundle:
name: authz/bundle.tar.gz
prefix: somedir
service: acmecorp We already support multiple services like: services:
acmecorp:
url: https://example.com/service/v1
hooli:
url: https://hooli.com/service/
bundle:
name: authz/bundle.tar.gz
prefix: somedir
service: acmecorp The new config supported for services:
acmecorp:
url: https://example.com/service/v1
hooli:
url: https://hooli.com/service/
bundles:
authz/bundle.tar.gz:
resource: somedir/authz/bundle.tar.gz
service: acmecorp
hooli-bundle:
resource: somedir/authz/bundle.tar.gz
service: hooli In this format the yaml key is the new name which would probably only surface in logs and status updates. Internally this becomes the new On that note, the other big change is that instead of a prefix+name with default prefix magic we just let them configure the resource path. The URL then is simply Update: We will allow for an empty services:
example:
url: https://example.com/policy/
bundles:
authz/bundle.tar.gz:
service: example Will utilize the default behavior and result in queries going to Note that the Bundle Root configWe already allow for setting a root for the bundle to manage in For the initial implementation of this we will document the dangers that /health ChangesWe have a parameter to allow for including bundle status in the health We could potentially add another parameter to specify which bundles to Status API ChangesThe status API defines a payload that looks very similar to the original The current proposal is to include all bundles configured in the status payload Old Request: POST /status[/<partition_name>] HTTP/1.1
Content-Type: application/json {
"labels": {
"app": "my-example-app",
"id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a",
"version": "v0.12.0"
},
"bundle": {
"name": "http/example/authz",
"active_revision": "TODO",
"last_successful_download": "2018-01-01T00:00:00.000Z",
"last_successful_activation": "2018-01-01T00:00:00.000Z"
}
} New Request: POST /status[/<partition_name>] HTTP/1.1
Content-Type: application/json {
"labels": {
"app": "my-example-app",
"id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a",
"version": "v0.12.0"
},
"bundles": {
"authz/bundle.tar.gz": {
"active_revision": "123",
"last_successful_download": "2018-01-01T00:00:00.000Z",
"last_successful_activation": "2018-01-01T00:00:00.000Z"
},
"hooli-bundle": {
"active_revision": "876",
"last_successful_download": "2018-01-01T00:00:00.000Z",
"last_successful_activation": "2018-01-01T00:00:00.000Z"
}
}
} The older version of the payload with Decision logsAs-is we report a revision with the decision. We will now report a map of bundles with Old Payload: [
{
"labels": {
"app": "my-example-app",
"id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a",
"version": "v0.12.0"
},
"decision_id": "4ca636c1-55e4-417a-b1d8-4aceb67960d1",
"revision": "W3sibCI6InN5cy9jYXRhbG9nIiwicyI6NDA3MX1d",
"path": "http/example/authz/allow",
"input": {
"method": "GET",
"path": "/salary/bob"
},
"result": "true",
"requested_by": "[::1]:59943",
"timestamp": "2018-01-01T00:00:00.000000Z"
}
] New Payload: [
{
"labels": {
"app": "my-example-app",
"id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a",
"version": "v0.12.0"
},
"decision_id": "4ca636c1-55e4-417a-b1d8-4aceb67960d1",
"bundles": {
"authz/bundle.tar.gz": {
"revision": "W3sibCI6InN5cy9jYXRhbG9nIiwicyI6NDA3MX1d"
},
"hooli-bundle": {
"revision": "7747ea16-ce75-45d9-be0f-7105926c303e"
}
},
"path": "http/example/authz/allow",
"input": {
"method": "GET",
"path": "/salary/bob"
},
"result": "true",
"requested_by": "[::1]:59943",
"timestamp": "2018-01-01T00:00:00.000000Z"
}
] The bundles are an object with a REST API provenance parameterUpdate the response given when the Request POST /v1/data/example?provenance=true HTTP/1.1 Old Response: {
"provenance": {
"build_commit": "1955fc4d",
"build_host": "foo.com",
"build_timestamp": "2019-04-29T23:42:04Z",
"revision": "ID-b1298a6c-6ad8-11e9-a26f-d38b5ceadad5",
"version": "0.10.8-dev"
},
"result": true
} New Response: {
"provenance": {
"build_commit": "1955fc4d",
"build_host": "foo.com",
"build_timestamp": "2019-04-29T23:42:04Z",
"bundles": {
"authz/bundle.tar.gz": {
"revision": "W3sibCI6InN5cy9jYXRhbG9nIiwicyI6NDA3MX1d"
},
"hooli-bundle": {
"revision": "7747ea16-ce75-45d9-be0f-7105926c303e"
}
},
"version": "0.10.8-dev"
},
"result": true
} The new format will only be send if the Internal Changes of note
Will update the proposal as investigation/coding continues. ====
|
[1] https://github.com/open-policy-agent/opa-istio-plugin/blob/master/internal/internal.go#L35 |
Thanks @tsandall ! I had totally missed the decision logger changes and that we read the manifest out of storage like that. Will update to account for them and the manifest storage stuff. I'll remove the version field from the status API, IIRC the first write up of this I re-used the For the bundle root config I'm not sure if we want to wait on this. The solution is partially for security but also for ordering. For example, if we have two bundles with overlapping roots we can't guarantee the order we download/process/activate the bundles. So one bundle might get downloaded and take some root over, then the second one tries and fails to activate.. OPA might still be handling requests but not giving the same responses as if the second bundle had been activated first. I spent some time thinking through other ways to handle this.. we could batch together activations and check roots on all configured bundles before activating any of them. If we find any conflicts we could de-activate/remove the older (currently being used) version of all conflicting bundles. The downside is that we lose some flexibility with different configured poll rates, or if say one server is temporarily offline or slow it could block all the other bundles from updating. It also means OPA keeps running but is giving bad (probably error) responses for requests it could have otherwise been handling. Another crazy idea is having some sort of hierarchy or policy to decide which bundle in conflicting bundles wins... The complexity seemed to heavily outweigh the benefits though. The crux of the problem is that the bundle gets to decide what it owns. IMO any approach we take with this should push the final decision for that to the OPA service (whether through config or internal policy). We just need to make sure bundles don't come in and declare they own some root that overlaps with someone elses. So, the solution I like is that the admin instead draws lines in the sand and says bundle A owns namespace foo and bundle B owns namespace bar. They can do whatever they want in those namespaces, but if they break out of them they are in trouble (and only that bundle). We also don't have to worry about timing. If each of those configured roots updates at different times we can be assured that they won't just remove/rewrite everything in someone elses. It might still affect the evaluation results if they are importing from some other namespace.. but IMO that is on the policy author and deployer to coordinate. Edit: Follow up after thinking on it for a little while.. I'm more ok with leaving the bundle roots stuff for the next iteration unless we get some input from users on it. Me making assumptions and guessing at problems is probably going to give a less good solution. For now we can just document the risks run by using multiple bundles and how to avoid them with best practices. |
Sounds good. Thanks for thinking through the edge cases/attack vectors nonetheless! |
Read through the updates. I have a couple thoughts.
Proposal (list format): bundles:
- name: authz/bundle.tar.gz
prefix: somedir
service: acmecorp OR (object format): bundles:
authz/bundle.tar.gz:
prefix: somedir
service: acmecorp
{
"revisions": {
"authz/bundle.tar.gz": "kjsfkjahfdsasdhfaljfskdf",
"authz/context": "kjsafhsfaksksksjsdjsksa"
}
} EDIT: Just to clarify, we ought to use the object format for the bundle config, I just wanted to cover both options. |
Thanks @tsandall !
I'm not 100% sure I follow why they would need to parse the resource. The name is given to them in the proposed status api payload as the key for the bundle details and could be whatever they need (my examples show only simple strings but it can still be From my point of view I don't like using that older style path+filename as the name or key for a couple of reasons. First, as keys, and probably just being a If the deployer needed that name to be
Yep, thats easy enough. I would opt for removing it from the status API if we stick with it. I had figured it might be useful for the status API to get more of the config (since it is monitoring bundles) vs the other auditing logs that just needed the revision for the bundle to correlate later on. My reasoning being that someone/something watching the results of the status API would see an error and, if in an error state, would want to know where it is without looking up the opa config. The auditing use-case may not need that shortcut and is also much more frequently sent so I left out the extra info. All that being said, we didn't report anything other than the name and revision previously so we can stick with that. Re: (4) and the format, I opted to break the "name" -> "revision" style info in favor of a map of "name" -> bundle object with "revision" field so that we can more easily add things later on. If we group them under the {
"revisions": {
"authz/bundle.tar.gz": "kjsfkjahfdsasdhfaljfskdf",
"authz/context": "kjsafhsfaksksksjsdjsksa"
},
"other_bundle_thing": {
"authz/bundle.tar.gz": "the thing",
"authz/context": "another thing"
},
"yet_another_bundle_thing": {
"authz/bundle.tar.gz": "more thing",
"authz/context": "another thing"
}
} versus something like: {
"authz/bundle.tar.gz": {
"revision": "kjsfkjahfdsasdhfaljfskdf",
"other_bundle_thing": "the thing",
"yet_another_bundle_thing": "more thing"
},
"authz/context": {
"revision": "kjsfkjahfdsasdhfaljfskdf",
"other_bundle_thing": "another thing",
"yet_another_bundle_thing": "another thing"
}
} IMO the second one is much easier to parse (programmatically and eyeballing json) |
Closing the loop here from the offline conversation. There was confusion on my part about the new "resource" field (I thought it was replacing the existing "name" field as the bundle identifier.) Decoupling the bundle identifier from the location where the bundle is stored could be useful and explicitly specifying the path is preferrable to the current implicit style using the "prefix". The proposal looks good to me on re-reading.
|
I also updated to include the default behavior for
Fixed in latest update.
Yea.. I wasn't sure how best to tackle that one. If you have any ideas on handling it better I'm all ears. |
This change brings in support for multiple bundles to be downloaded and activated OPA. This is enabled by using the new config option `bundles` to define the bundles, and deprecates the older `bundle` option. The new `bundles` keyword and structure is propagated through to the decision logs, status API, provenance, stored manifests, etc. Check out the doc changes for all the updated structures. That being said any existing configuration using `bundle` will *not* see the new structure, everything is intended to be backwards compatible (almost to a fault). Fixes: open-policy-agent#721 Signed-off-by: Patrick East <east.patrick@gmail.com>
This change brings in support for multiple bundles to be downloaded and activated OPA. This is enabled by using the new config option `bundles` to define the bundles, and deprecates the older `bundle` option. The new `bundles` keyword and structure is propagated through to the decision logs, status API, provenance, stored manifests, etc. Check out the doc changes for all the updated structures. That being said any existing configuration using `bundle` will *not* see the new structure, everything is intended to be backwards compatible (almost to a fault). Fixes: #721 Signed-off-by: Patrick East <east.patrick@gmail.com>
I'm trying to find a way to load policies from different bundles into the same OPA instance. So far I was unable to do so as you can only specify 1 bundle in the config file (https://www.openpolicyagent.org/docs/bundles.html). However, it is possible to specify multiple number of services from where bundle can be downloaded.
If this is not supported - what is the recommended way to load policies from different sources into the same OPA instance?
Thanks!
The text was updated successfully, but these errors were encountered: