rond-authz / rond Goto Github PK
View Code? Open in Web Editor NEWA lightweight container for distributed security policy evaluation
Home Page: https://rond-authz.io
License: Apache License 2.0
A lightweight container for distributed security policy evaluation
Home Page: https://rond-authz.io
License: Apache License 2.0
Is your feature request related to a problem? Please describe.
At the moment the RBAC is able to respond to a policy evaluation failure only with a 403 status code. In some situation this information may not be correct or the user should not be informed about the failed authorization.
Describe the solution you'd like
One example may be when requesting for a specific item to which access has been restricted. An unauthorized user in this case should receive a 404 response instead of 403. In the same way we can imagine that the same policy when the user tries to access a not existing item will fail. Also in this case we may want to respond with a 404 status code instead of 403.
A possible solution is to add inside the xpermission object an optional argument in which is possible to set the 4XX status code to return to the user in case of policy evaluation failure.
Describe the bug
I wrote a policy where i add:
resource := data.resources[_]
print(resource)
Expected behavior
I expect the resource object to be printed, and in any case te policy to log something if it will somehow 'fail'
Replication info
Additional context
Here is the log of the rbac sidecar
[1678727860000] DEBUG: policy evaluation completed
allowed: true
evaluationTimeMicroseconds: 11368
matchedPath: "/users/"
method: "GET"
partialEval: true
policyName: "filter_by_clinic"
reqId: "849eee886adb5e9a8aacc64e59d3303d"
requestedPath: "/users/"
[1678727860001] TRACE: policy results and query
allowed: true
query: null
reqId: "849eee886adb5e9a8aacc64e59d3303d"s
It is correct to have types in a specific package, or should them be in the package which uses them?
Is your feature request related to a problem? Please describe.
Currently, from the docs, in order to configure rönd sidecar with OAS exposed by an application service you have to specify something link this:
{
"x-permission": {
"allow": "greetings_read",
"resourceFilter": {
"rowFilter": {
"enabled": true,
"headerKey": "x-acl-rows"
}
},
"responseFilter": {
"policy": "filter_response_example"
},
"options": {
"enableResourcePermissionsMapOptimization": true
}
}
}
I find this configuration a bit redundant and suggest the following (heavily breaking) change:
{
"x-rond-config": {
"requestFlow": {
"policyName": "greetings_read",
"generateQuery": true,
"queryOptions": {
"headerName": "...",
"language": "..." // for future support to different query languages
}
},
"responseFlow": {
"policyName": "filter_response_example"
},
"options": {
"enableResourcePermissionsMapOptimization": true
}
}
}
These two headers are used to create the query used to gather bindings.
Right now they are considered trusted headers as they are generally provided by another service and not directly by the user; however if:
the user could be able to find more bindings than necessary and thus potentially breach the policies.
I propose to find a way to sanitize those inputs, what do you think?
Tracking issue for:
The possibility to run policy over the request/response body is a nice feature, however it's not always useful but still rönd loads the whole body is the content type is supported.
When dealing with large request bodies this may lead to:
I'd like to have some flag in the OAS extension that can let me disable the body reading feature
{
"x-rond": {
"requestFlow": {
"policyName": "policy_to_be_executed_BEFORE_API_invocation",
"preventRequestBodyLoad": true|false. // disable only for request
},
"responseFlow": {
"policyName": "policy_to_be_executed_AFTER_API_invocation",
"preventRequestBodyLoad": true|false // disable only for response
},
"options": {
"enableResourcePermissionsMapOptimization": false,
"ignoreTrailingSlash": false,
"preventRequestBodyLoad": true|false // disable for both request and response
}
}
}
Increasing memory: no thanks...
Rönd version used: 1.10.1
OAS support could be implemented in v2 only (x-rond
)
To make this, it's possible to remove oas and metrics from the NewSDK function and use instead a WithMetrics
and WithOas
method which configure it.
It is interesting to expose from the SDK the possibility to pass directly the configuration (without the OAS config) to get the evaluator
After I activate rond I expect to receive the following message error:
{"error":"","statusCode":403,"message":"User is not allowed to request the API"}
But I get:
{"error":"error while parsing rowFilter.enabled: strconv.ParseBool: parsing \"\": invalid syntax","message":"The request doesn't match any known API","statusCode":403}
{"error":"","statusCode":403,"message":"User is not allowed to request the API"}
Describe the bug
With a standalone rönd installation, if you try to perform a policy evaluation with the POST method, specifying content-type: application/json
but no body the request remains pending.
Expected behavior
The request should not remain pending, it should either reply 400 or ignore the invalid header/body tuple and go on with the validation
Replication info
Is your feature request related to a problem? Please describe.
Expose OpenAPI documentation when in Standalone Mode
Describe the solution you'd like
The should be an API that exposes the OpenAPI Specification of standalone mode APIs
Is your feature request related to a problem? Please describe.
Golang 1.20 introduced a new ReverseProxy Director which could substitute our custom one
Describe the solution you'd like
We should verify this and use it if applicable to our use case
RIght now when generating a query the only supported query language is MongoDB; query generation should be extended to different languages:
The default language should be changed from MongoDB to something more general purpose (maybe lucene?). This means the change may be breaking
Is your feature request related to a problem? Please describe.
Library : https://github.com/ucarion/urlpath
This library can evaluate URL Pattern Matching using the URL string. Using this alternative, it's not needed to open an internal router as BunRouter.
Is your feature request related to a problem? Please describe.
Using the SDK, it is possible that it is not used in the http flow with request/response management. In this way, we should give some customizable metadata to the Input.
Describe the solution you'd like
At the moment, the Input
contains Request
and Response
, which are coupled with the http interface.
type InputRequest struct {
Body interface{} `json:"body,omitempty"`
Headers http.Header `json:"headers,omitempty"`
Query url.Values `json:"query,omitempty"`
PathParams map[string]string `json:"pathParams,omitempty"`
Method string `json:"method"`
Path string `json:"path"`
}
type InputResponse struct {
Body interface{} `json:"body,omitempty"`
}
type Input struct {
Request InputRequest `json:"request"`
Response InputResponse `json:"response"`
ClientType string `json:"clientType,omitempty"`
User InputUser `json:"user"`
}
We could add a new CustomData field in the input.
type Input struct {
Request InputRequest `json:"request"`
Response InputResponse `json:"response"`
ClientType string `json:"clientType,omitempty"`
User InputUser `json:"user"`
CustomMetadataData map[string]any `json:"metadata"`
}
Describe alternatives you've considered
The sdk evaluator exposes at the moment EvaluateRequestPolicy
and EvaluateResponsePolicy
, so maybe we could divide the custom data:
type InputRequest struct {
Body interface{} `json:"body,omitempty"`
Headers http.Header `json:"headers,omitempty"`
Query url.Values `json:"query,omitempty"`
PathParams map[string]string `json:"pathParams,omitempty"`
Method string `json:"method"`
Path string `json:"path"`
CustomData map[string]any `json:"data,omitempty"`
}
type InputResponse struct {
Body interface{} `json:"body,omitempty"`
CustomData map[string]any `json:"data,omitempty"`
}
type Input struct {
Request InputRequest `json:"request"`
Response InputResponse `json:"response"`
ClientType string `json:"clientType,omitempty"`
User InputUser `json:"user"`
}
Otherwise, we could also set InputRequest as a map[string]interface{}
, totally configurable by the user. So that it is not coupled with http.
What do you think about?
The PolicyEvaluation function, when the policy fails, returns an error. The SDK should returns the PolicyResult with Allowed set to false instead, since it is more usable.
Lines 97 to 108 in d8c2c8b
SDK prototype interface is ready in the core package; we should remove it from the core, standardize the API and use it in the service package for evaluators setup while keeping unchanged current feature-set.
Is your feature request related to a problem? Please describe.
gorilla/mux toolkit is now archived https://github.com/gorilla#gorilla-toolkit and no longer maintained.
Describe the solution you'd like
We should move to another toolkit. We should talk about some possibilities.
To make some minor changes to the code, we should move to a toolkit based on the http package, like echo or gin-gonic.
Otherwise, we can also choose to switch to fasthttp with something like fiber.
Additional context
The gorilla toolkit remains available, so there is not hurry to do the change.
What do you prefer about the alternatives?
This application is amazing but the released package contains a lot of stuff that probably I really don't need. For example Mongo connection, crud invocation, (mia-)platform specific header management.
This impact a lot:
Different solutions are available for fixing this problem. With a really short research the main 2 alternatives I found are:
Rönd
a library (or a framework)In both cases I, as developer, can define which plugin i want to have, developing my own one without breaking the other ones.
Unfortunately go doesn't support FFI for many reason (GC etc...). But fortunately starting from a "recent" (1.9) version, go has a go native ways to load a Unix shared object.
So, I, as developer, can create my plugin define my special function (for example find_one
) by my own and instruct Rönd
to load it
I'm making a bigger example (NB: I'm not a compiler)
my library: rond-mongo
package main
var MongoFindOneDecl = &ast.Builtin{
Name: "find_one",
Decl: types.NewFunction(
types.Args(
types.S, // collectionName
types.A, // query
),
types.A, // found document
),
}
// function loaded by Rond
var Function = rego.Function2(
®o.Function{
Name: MongoFindOneDecl.Name,
Decl: MongoFindOneDecl.Decl,
},
func(ctx rego.BuiltinContext, collectionNameTerm, queryTerm *ast.Term) (*ast.Term, error) {
.... // do your stuff here
return ast.NewTerm(t), nil
},
)
// function loaded by Rond
func Startup(ctx context.Context) error {
// add mongoclient to context
return nil
}
Build my library:
go build -buildmode=plugin
Load in Rönd
the plugins
plugin_paths := strings.Split(env.PluginPaths, ",")
for _, plugin_path := range plugin_paths {
p, err := plugin.Open(plugin_path)
if err != nil {
panic(err)
}
v, err := p.Lookup("Function")
if err != nil {
panic(err)
}
f, err := p.Lookup("Startup")
if err != nil {
panic(err)
}
// store that and use in `rego.New` method
*v.(*int)
f.(func(context.Context))
}
Run Rönd
PLUGIN_PATHS="/path1/rond-mongo.so,/path2/rond-crud.so" ./rond
The runtime bindings are used for many decades and it works very well. But probably there's a reason if some products move away from a similar solution (openssl, go compiler etc...). Not so complicated to implement.
Doesn't work on Windows system
Rönd
a library (or a framework)Rönd
can move to a library that, if configured, starts a webserver, applies rego rules and proxies the request to the target server.
So my application could seem like
var MongoFindOneDecl = &ast.Builtin{
Name: "find_one",
Decl: types.NewFunction(
types.Args(
types.S, // collectionName
types.A, // query
),
types.A, // found document
),
}
var MongoFindOneFunction = rego.Function2(
®o.Function{
Name: MongoFindOneDecl.Name,
Decl: MongoFindOneDecl.Decl,
},
func(ctx rego.BuiltinContext, collectionNameTerm, queryTerm *ast.Term) (*ast.Term, error) {
.... // do your stuff here
return ast.NewTerm(t), nil
},
)
func main() {
// add mongoclient to context
r := rond.New(ctx, /* parameters */, MongoFindOneFunction /* other plugin */)
r.Start()
}
This approach reduces a lot the code but doesn't build a plugin system, so that issue still persists.
But probably that problem can be overcame move all "customer" functionality in the application. So no one should complain...
I'm not a very expert on go, so probably there're other solutions I did't consider. Please feel free to share 😄
In the rond input it is set the ClientType, which is passed as params to create a new input:
rond/sdk/rondinput/http/input.go
Lines 47 to 71 in d8c2c8b
It should be removed since it is the support to a Mia-Platform specific header, and instead use it inside some custom metadata #245.
As we figured out in #268 the check-up probe is practically useless, let's remove it!
This change would be breaking
Describe the bug
When it's used a policy to filter a response body, in log allowed field is set to false
, also if all works correctly.
An example log for the policy foo
is:
{"allowed":false,"evaluationTimeMicroseconds":123,"level":20,"matchedPath":"/api/","method":"GET","msg":"policy evaluation completed","partialEval":false,"policyName":"foo","reqId":"req-id","requestedPath":"/api/"}
Expected behavior
We expect a more correct information. Allowed false is the output from opa, but from rond this is evaluated as allowed and change the response body.
Replication info
There are the SDK tests which highlight this behaviour:
Lines 600 to 620 in 7d71122
In RBAC we defined different manual routes as expressed below:
/path/*
--> policy1/path/:id
--> policy2We defined a route with a specific verb (PATCH) in order to match the presence of a path parameter. Despite the path parameter is expressed only for a specific verb, the calls with other verbs fail because the request object that arrives to the policy evaluator contains, unexpectedly, a path parameter.
Example
With the above configuration, if we call GET /path/count
, count
is considered as :id
and we can find it in the pathParams
object, as expressed in the following log that prints the object passed to the policy evaluator.
"clientType": "backoffice",
"request": {
...
},
"method": "GET",
"path": "/path/count",
"pathParams": {
"id": "count"
},
"query": {
...
}
},
"response": {},
"user": {
"bindings": [
...
}
}
Expected behavior
We expect that the requests which are not of type PATCH /path/:id
will follow the policy expressed for the ALL /path/*
and do not consider count
as a path parameter.
Replication info
1.2.0
Right now, when rönd operates in standalone mode it is not capable of executing response-flow policies therefore I have to write any security-related filter on API responses in the API itself.
Describe the solution you'd like
I'd like to be able to invoke rönd standalone also to be able to know the payload I should return to the client.
So with a configuration such as
{
"/someapi": {
"x-rond": {
"requestFlow": { "policyName": "allow_all" },
"responseFlow": { "policyName": "my_policy" },
}
}
}
I'd like to do something like
curl -X POST http://rond-standalone/eval/someapi -d '{ my original payload }'
And receive back the payload modified by my_policy
.
Describe alternatives you've considered
To let rönd discern whether the http://rond-standalone/eval/someapi
should use the request
or response
flow I'd like to use something like:
?rondFlow=response
rond-flow=response
therefore the api would actually be different based on the flow, in this case:
http://rond-standalone/eval/response/someapi` but this would be a breaking change!Any other suggestion on a solution?
Describe the bug
Following the documentation, I've configured a custom header name (x-acl-rows
) for request filters, however the service has received it on acl_rows
Expected behavior
I'd expect to receive the generated query on the custom-defined header.
To Reproduce
"/generate-request-filter": {
"get": {
"x-permission": {
"allow": "data_filter",
"resourceFilter": {
"rowFilter": {
"enabled": true,
"headerName": "x-acl-rows"
}
}
}
}
}
data_filter {
resource := data.resources[_]
resource.name == "Jane"
resource.organization == "rönd-authz"
}
Request info from the application service
{"request":{"headers":{"Accept":"*/*","Accept-Encoding":"gzip","Acl_rows":"{\"$or\":[{\"$and\":[{\"name\":{\"$eq\":\"Jane\"}},{\"organization\":{\"$eq\":\"rönd-authz\"}}]}]}","Content-Length":"0","Host":"localhost:30000","User-Agent":"curl/7.79.1","X-Forwarded-For":"172.21.0.3"},"query":{},"path":"/generate-request-filter","method":"GET"}}%
Is your feature request related to a problem? Please describe.
At the moment it is not possible to completely revoke all the bindings associated to a resource without knowing which users and groups have access to the said resource.
Describe the solution you'd like
I'd like to call the POST /revoke/bindings/resource/{resourceType}
API endpoint and remove all the permissions associated to a resource just by sending the resourceId
inside the request body, without the need to specify all existing groups and users.
Query header is always set if policy pass, because QueryToProxy is never empty
Lines 146 to 149 in d8c2c8b
Query is passed only if some query is set to proxy.
At the moment, body requests that are not of type application/json
will be discarded by the policy.
It would be nice if the parameters of a multipart/form-data
requests will be parsed into the input
object
Is your feature request related to a problem? Please describe.
I need to take decision into my web application, as of now I'm relying on API invocations that are protected by Rönd, however it would be great to have immediately available in the browser and prevent any unnecessary roundtrip to the backend.
Inside the web application I just need it to decide whether or not showing specific pieces of the UI so it would be mainly for a better UX rather than other.
Additional context
MongoDB (and any third party product the may be introduced in the future) integration might be revised since it would be hard to have it in the frontend (on one hand it'd be a cool feature to have but it's most likely impossible to have the database reachable from the browser so I guess it'd be practically useless)
Describe the bug
When using print function, only variables are logged during policy evaluation. print("example")
doesn't work.
Describe the bug
While running integration tests if we perform two different tests on the same localhost port on which it is mocked a called we always fall in the first mock. This is probably due to the fact that the connection of the first test is never closed
Additional context
This is technical debt for further development of integration tests
Provide a custom builtin to read the request/response body from a policy; a built-in could allow a user to avoid configuring the OAS with special flags to prevent reading the body (e.g. #273).
Note: the Built-in should have access to the original http request/reponse to be able to read the contents and refresh the buffer to make it available to be proxied
It would be nice to be able to write a policy like:
my_policy {
body := read_request_body()
}
my_policy {
body := read_response_body()
}
Describe the bug
When an endpoint covered by rond
gets called with a JSON object having a field set to undefined
, an error is returned as following:
{"error":"RBAC input creation failed","message":"Internal server error, please try again later","statusCode":500}
And logs the following:
{"foundBindingsLength":3,"foundRolesLength":2,"level":10,"msg":"found bindings and roles"}
{"error":{"message":"failed request body deserialization: invalid character 'u' looking for beginning of value"},"level":50,"msg":"failed rego query input creation"}
Replication info
main
Is your feature request related to a problem? Please describe.
Revoke API does not use pagination but only revokes bindings from the first page
Describe the solution you'd like
Pagination should be implemented to make sure all bindings are revoked
Is your feature request related to a problem? Please describe.
When using rond without authorization-service no one correctly configure the user properties header and the marshaling fails
this is the error:
[1663171414738] ERROR: failed rego query input creation
reqId: "1962aee47a138892b03e57a7b29a87ef"
error: {
"message": "user properties header is not valid: json: cannot unmarshal number into Go value of type map[string]interface {}"
}
Describe the solution you'd like
rond should ignore invalid header and evaluate the expression
To get the userId, at the moment I must have it in headers and get it from the headers.
But rond already take it from the header, so we could add it in the InputUser
struct to simplify the get of the user id.
Describe the solution you'd like
Add the userId in this struct
Lines 52 to 58 in 67adb74
Describe alternatives you've considered
It's possible at the moment to take it from headers, but it's a duplication from what rond already do.
Is your feature request related to a problem? Please describe.
Fallback route should be removed
I'm experiencing difficulties with the clarity of logs when a policy validation fails in Rönd. Currently, the logs don't provide detailed information about why a policy failed validation, such as missing headers that are evaluated in the policy. This lack of detailed information makes it challenging to troubleshoot and create new policies effectively.
I would like to see improvements in the logging mechanism when a policy validation fails in Rönd. Specifically, I propose adding more descriptive information to the logs, such as which headers are missing or causing the validation failure. This additional context will greatly assist in understanding why a policy failed and aid in the creation of more effective policies. These logs could be at debug or trace level.
One alternative could be manually adding custom logging statements within policies to capture specific validation failures. However, this approach is cumbersome and adds complexity to policy management. Having built-in, informative logs directly from Rönd would streamline the troubleshooting process.
Is your feature request related to a problem? Please describe.
At the moment, to use rond it is required to have a sidecar near each service. This sidecar is an additional operation inside the pod, and rond consumes resources.
Describe the solution you'd like
I'd like to integrate rond inside our services.
As first step, we could integrate in services written in golang with some middleware authorization step.
As the next step, we could see also to create a Wasm lib to use it also in services written in other languages.
Often times on Kubernetes, when there are several policies to be evaluated at startup, the service may take a while to startup and, attaching liveness probes on the /-/rbac-healthz
route without a very high threshold may lead the service to be killed.
Decouple probes from web server startup
/-/rbac-ready
, commonly attached to the readiness probe, immediately replies but only returns 200 after precomputation is completed/-/rbac-healthz
, commonly attached to the liveness probe, immediately replies and returns 200 since the service is up and running/-/rbac-startup
, commonly attached to the startup probe, that behaves as the readiness route.Current solution are:
Done with #219
There are different storages that could be integrated as alternatives to MongoDB:
Describe the bug
If a default value is set for the policy, the query generated will be a valid empty query. This means that the policy is evaluated always truthly but the filter generated is in the form $or: []
. This breaks the query mongo but is a security problem in any case since the policy could have had a falsy response
default filter_projects = false
filter_projects {
resource := data.resources[_]
bindings := input.user.bindings[_]
roles := input.user.roles[_]
roles.roleId == bindings.roles[_]
roles.permissions[_] == "console.project.view"
bindings.resource.resourceType == "custom"
resource._id == bindings.resource.resourceId
}
filter_projects {
resource := data.resources[_]
bindings := input.user.bindings[_]
bindings.resource.resourceType == "custom"
bindings.permissions[_] == "console.project.view"
resource._id == bindings.resource.resourceId
}
Benchmark of EvaluateRequest without query generation already exists.
We should add:
Describe the bug
I can use any method to invoke standalone APIs, only POST should be supported!
Describe the bug
The query generated by the rbac service with row filtering enabled it's not capped to any length and thus it cause header overflow error.
Possible mitigation: insert the operator $in when generating the query. (Do not solve forever)
Describe the bug
This logs appears sometimes while running requests policy evaluation
2022/07/21 13:45:05 http: proxy error: context canceled
Expected behavior
Find out whether the log is hiding a bug, otherwise keep it but trasnform it to json
Replication info
SDK and core packages should expose metrics via a rond-specific interface; it's up to the user configuring the proper monitoring tool; the rond sidecar and standalone service will still use prometheus
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.