Skip to content

feat: OPA Authorizer #777

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

Merged
merged 36 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
88944a6
add support for authorization with OPA
labrenbe Mar 28, 2025
142f867
add docs on authorization with OPA
labrenbe Mar 28, 2025
e603d7d
update helm chart
labrenbe Mar 31, 2025
39caff2
Merge remote-tracking branch 'origin/main' into feat/opa-authorizer
labrenbe Apr 10, 2025
032ab37
rename input.user to input.identity
labrenbe Apr 15, 2025
4470e83
Merge remote-tracking branch 'origin/main' into feat/opa-authorizer
labrenbe Apr 15, 2025
6bd4831
add opa cm watch
labrenbe Apr 15, 2025
b87430b
remove unused test
labrenbe Apr 16, 2025
3c9c7c1
update rego with new opa response schema
labrenbe May 6, 2025
7dd6e38
update docs
labrenbe May 8, 2025
47f1ab5
improve docs
labrenbe May 9, 2025
150dfdd
update docs
labrenbe May 9, 2025
9b3e0d3
Merge remote-tracking branch 'origin/main' into feat/opa-authorizer
labrenbe May 9, 2025
031820b
fix docs
labrenbe May 9, 2025
13e60bc
refactor references_config_map function
labrenbe May 9, 2025
cfa6783
fix docs
labrenbe May 9, 2025
f9bcd44
wip: add integration test
labrenbe May 14, 2025
46fc511
add retries to nifi calls during test
labrenbe May 15, 2025
8f0a02f
simplify rego rules
labrenbe May 15, 2025
ca61f92
move nifi flow copying into init container
labrenbe May 15, 2025
6e66627
remove oidc test and fix oidc-opa test with tls disabled
labrenbe May 16, 2025
96d66db
Apply suggestions from code review
labrenbe May 19, 2025
3107704
improve docs
labrenbe May 19, 2025
2a9a992
require opa operator during integration tests
labrenbe May 19, 2025
cd7e5bb
Merge remote-tracking branch 'origin/main' into feat/opa-authorizer
labrenbe May 20, 2025
ca80daa
reduce flow election time
maltesander May 20, 2025
71519af
reduce verbosity, improve response handling
maltesander May 20, 2025
c26759f
linter attempt 1
maltesander May 20, 2025
860e346
remove securityContext from keycloak
labrenbe May 20, 2025
6d77992
remove securityContext from test container
labrenbe May 20, 2025
993d4d9
Apply suggestions from code review
labrenbe May 20, 2025
fff5c79
remove 'json' annotation from code snippet
labrenbe May 20, 2025
74a8243
fix docs formatting
labrenbe May 20, 2025
0e7f10b
Update docs/modules/nifi/pages/usage_guide/security.adoc
labrenbe May 21, 2025
df3d756
Merge remote-tracking branch 'origin/main' into feat/opa-authorizer
labrenbe May 21, 2025
19225a9
add caching default values to docs
labrenbe May 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion deploy/helm/nifi-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ spec:
description: Settings that affect all roles and role groups. The settings in the `clusterConfig` are cluster wide settings that do not need to be configurable at role or role group level.
properties:
authentication:
description: Authentication options for NiFi (required). Read more about authentication in the [security documentation](https://docs.stackable.tech/home/nightly/nifi/usage_guide/security).
description: Authentication options for NiFi (required). Read more about authentication in the [security documentation](https://docs.stackable.tech/home/nightly/nifi/usage_guide/security#authentication).
items:
properties:
authenticationClass:
Expand All @@ -55,6 +55,42 @@ spec:
- authenticationClass
type: object
type: array
authorization:
description: Authorization options. Learn more in the [NiFi authorization usage guide](https://docs.stackable.tech/home/nightly/nifi/usage-guide/security#authorization).
nullable: true
properties:
opa:
description: Configure the OPA stacklet [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery) and the name of the Rego package containing your authorization rules. Consult the [OPA authorization documentation](https://docs.stackable.tech/home/nightly/concepts/opa) to learn how to deploy Rego authorization rules with OPA.
nullable: true
properties:
cache:
default:
entryTimeToLive: 30s
maxEntries: 10000
description: Least Recently Used (LRU) cache with per-entry time-to-live (TTL) value.
properties:
entryTimeToLive:
default: 30s
description: Time to live per entry
type: string
maxEntries:
default: 10000
description: Maximum number of entries in the cache; If this threshold is reached then the least recently used item is removed.
format: uint32
minimum: 0.0
type: integer
type: object
configMapName:
description: The [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery) for the OPA stacklet that should be used for authorization requests.
type: string
package:
description: The name of the Rego package containing the Rego rules for the product.
nullable: true
type: string
required:
- configMapName
type: object
type: object
createReportingTaskJob:
default:
enabled: true
Expand Down
174 changes: 171 additions & 3 deletions docs/modules/nifi/pages/usage_guide/security.adoc
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
= Security
:description: Secure Apache NiFi on Kubernetes with TLS, authentication, and authorization using the Stackable operator. Configure LDAP, OIDC, and sensitive data encryption.
:nifi-docs-authorization: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#multi-tenant-authorization
:nifi-docs-access-policies: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#access-policies
:nifi-docs-component-level-access-policies: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#component-level-access-policies
:nifi-docs-access-policy-inheritance: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#access-policy-inheritance
:nifi-docs-fileusergroupprovider: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#fileusergroupprovider
:nifi-docs-fileaccesspolicyprovider: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#fileaccesspolicyprovider
:nifi-docs-sensitive-properties-key: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#nifi_sensitive_props_key
:nifi-opa-plugin: https://github.com/DavidGitter/nifi-opa-plugin/
:opa-rego-docs: https://www.openpolicyagent.org/docs/latest/#rego

== TLS

Expand Down Expand Up @@ -166,9 +171,7 @@ stringData:
[#authorization]
== Authorization

NiFi supports {nifi-docs-authorization}[multiple authorization methods], the available authorization methods depend on the chosen authentication method.

Authorization is not fully implemented by the Stackable Operator for Apache NiFi.
The Stackable Operator for Apache NiFi supports {nifi-docs-authorization}[multiple authorization methods], the available authorization methods depend on the chosen authentication method. Using Open Policy Agent for authorization is independent of the authentication method.

[#authorization-single-user]
=== Single user
Expand All @@ -190,6 +193,171 @@ With this authorization method, all authenticated users have administrator capab
An admin user with an auto-generated password is created that can access the NiFi API.
The password for this user is stored in a Kubernetes Secret called `<nifi-name>-oidc-admin-password`.

[#authorization-opa]
=== Open Policy Agent (OPA)

NiFi can be configured to delegate authorization decisions to an Open Policy Agent (OPA) instance. More information on the setup and configuration of OPA can be found in the xref:opa:index.adoc[OPA Operator documentation].

A NiFi cluster can be configured with OPA authorization by adding this section to the configuration:

[source,yaml]
----
spec:
clusterConfig:
authorization:
opa:
configMapName: simple-opa # <1>
package: my-nifi-rules # <2>
cache:
entryTimeToLive: 5s # <3>
maxEntries: 10 # <4>
----
<1> The name of your OPA Stacklet (`simple-opa` in this case)
<2> The rego rule package to use for policy decisions.
The package needs to contain an `allow` rule.
This is optional and defaults to the name of the NiFi Stacklet.
<3> TTL for items in the cache in NiFi. Optional, defaults to 30 seconds.
<4> Maximum number of concurrent entries in the cache in NiFi. Optional, defaults to 10000 entries.

[#defining-rego-rules]
=== Defining rego rules

For a general explanation of how rules are written, please refer to the {opa-rego-docs}[OPA documentation]. Authorization with OPA is done using a {nifi-opa-plugin}[custom authorizer provided by a plugin for NiFi].

[#opa-inputs]
==== OPA Inputs
The payload sent by NiFi with each request to OPA, that is accessible within the rego rules, has the following structure:

[cols="1,2,1"]
|===
| Payload Field| Description| Possible Values
| action.name
| The action taken against the resource.
|`read`, `write`
| resource.id
| The unique identifier of the resource that is being authorized. This might be a parent component in the case of `resourceNotFound` is set to `true`.
|
| resource.name
| The name of the resource that is being authorized. This might be a parent component in the case of `resourceNotFound` is set to `true`.
|
| resource.safeDescription
| The description of the resource that is being authorized.
|
| requestedResource.id
| The unique identifier of the original resource that was requested (see <<component-level-access-policies>>).
|
| requestedResource.name
| The name of the original resource that is being authorized on (see <<component-level-access-policies>>).
|
| requestedResource.safeDescription
| The description of the original resource that is being authorized on (see <<component-level-access-policies>>).
|
| identity.name
| The name of the identity/user accessing the resource.
|
| identity.groups
| Comma-separated list of groups that the identity/user accessing the resource belongs to.
|
| properties.isAccessAttempt
| Whether this is a direct access attempt of the resource or if it's being checked as part of another response.
| `true`, `false` (String)
| isAnonymous
| Whether the entity accessing the resource is anonymous.
| `true`, `false` (String)
| resourceContext
| Object containing the event attributes to make additional access decisions for provenance events.
| ```{"": ""}``` if empty
| userContext
| Additional context for the user to make additional access decisions.
| ```{"": ""}``` if empty
|===

[#opa-result]
==== OPA Result

The OPA authorizer plugin expects rego rules to be named `allow` and to return a result following this schema:
[source]
----
{
"allowed": <Boolean>, # <1>
"resourceNotFound": <Boolean>, # <2>
"dumpCache": <Boolean>, # <3>
"message": <String>, # <4>
}
----
<1> Whether the action against the resource is allowed. Optional, defaults to false.
<2> Whether no rule was found for the authorization request. This should only be set to true in the default rule to e.g. forward policy decisions to parent components. If set to true the value of the "allowed" field will be ignored. Optional, defaults to false.
<3> Whether the whole local cache in the OPA authorizer plugin in NiFi should be invalidated. Optional, defaults to false.
<4> An optional error message that is shown to the user when access is denied.

[#access-policies]
==== Access Policies
NiFi uses {nifi-docs-access-policies}[access policies] to manage access to system-wide resources like the user interface.

[#component-level-access-policies]
==== Component Level Access Policies and Access Policy Inheritance

{nifi-docs-component-level-access-policies}[Component Level Access Policies] allow managing granular access to components like process-groups and processors. Components can {nifi-docs-access-policy-inheritance}[inherite access policies] defined for parent components, e.g. a process group is the parent component for a contained processor component.

The payload field `requestedResource` contains the id, name and description of the original resource that was requested. In cases with inherited policies, this will be an ancestor resource of the current resource. For the initial request, and cases without inheritance, the requested resource will be the same as the current resource.

When an authorizer returns "resourceNotFound" as result instead of an authorization decision, NiFi will send an authorization request for the parent component. Access policy inheritance can be recursive up to the root component. If "resourceNotFound" is returned for an authorization request and the component doesn't have a parent component, NiFi will deny access to the component.

To manage access for all process groups in the NiFi instance a rule has to be defined for the root process group which is identified by the resource name "NiFi Flow" and a resource id generated at random ("/process-groups/<uuid>").

[source,rego]
----
default allow := {
"resourceNotFound": true
} # <1>

allow := {
"allowed": true
} if {
input.resource.name == "NiFi Flow"
startswith(input.resource.id, "/process-groups")
} # <2>

allow := {
"allowed": false
} if {
input.resource.id == "/process-groups/a10c311e-0196-1000-2856-dc0606d3c5d7"
input.identity.name == "alice"
} # <3>
----
<1> The default rule should return `"resourceNotFound": true`. If this is not set, NiFi's access policy inheritance will not work. Any values for the `allowed` field in the response will be ignored.
<2> A rule that grants all users access to the root process group and thus to all components in the NiFi instance.
<3> A rule that denies access to a specific process group for the user "alice". For this process group the default rego rule will not be applied and NiFi's component inhertiance will not be used. All child components of this process group will also be authorized based on this rule unless a more granular rule overrides it.

[#communication-between-nifi-nodes]
==== Communication between NiFi nodes
To allow communication between NiFi nodes an additional rego rule is required:
[source,rego]
----
allow := {
"allowed": true
} if {
input.identity.name == "CN=generated certificate for pod" # <1>
input.resource.id == "/proxy" # <2>
}
----
<1> The identity of NiFi nodes authenticated with TLS certificates provided by the secrets operator.
<2> Only access to the `/proxy` API is required.

[#caching]
==== Caching

The OPA authorizer has a mechanism to cache results from OPA which can be configured in the NifiCluster spec (see above). To delete the whole cache add `"dumpCache": true` to the result.
[source,rego]
----
allow := {
"allowed": false
"dumpCache": true
} if {
...
}
----

[#encrypting-sensitive-properties]
== Encrypting sensitive properties on disk

Expand Down
Loading