Repository navigation
Closed as not planned
Labels
staleThis issue or PR has become staleThis issue or PR has become stale
Description
📋 Prerequisites
- I have searched the existing issues to avoid creating a duplicate
- By submitting this issue, you agree to follow our Code of Conduct
📝 Feature Summary
Add role-based access control (RBAC) authorization for Kagent API, UI, and CLI with OIDC group integration
❓ Problem Statement / Motivation
Current Limitation
Kagent currently lacks authorization for its UI and API. All users have full access to all resources across all namespaces. The `NoopAuthorizer` allows everything - there are no permission checks.
## Security Risks
This creates security risks in multi-tenant environments where:
- Users should only see resources in their assigned namespaces
- Different roles need different permission levels (admins vs readonly users)
- Sensitive data (API keys, secrets) should be masked from non-admins
- Unauthorized users can create, modify, or delete critical resources
## Who Is Affected
- **Platform teams** running Kagent in shared/multi-tenant environments
- **Security teams** requiring audit trails and access control
- **End users** who need read-only access without modification permissions
- **Administrators** who need to enforce least-privilege access
## Why This Is Needed
- Compliance requirements for production deployments
- Prevent accidental or malicious resource modifications
- Enable safe delegation of access to junior team members or contractors
- Support enterprise OIDC/SSO integration for unified access control
💡 Proposed Solution
Overview
Implement Casbin-based RBAC authorization enforced at the API layer (applies to UI, CLI, and direct API calls).
## Core Components
### 1. Role System
- **2 built-in roles**: `admin` (full access) and `readonly` (read-only)
- Support for **multiple roles per user**
- **Default policy**: authenticated users without explicit roles get `admin` access
- Extensible architecture for custom roles via Casbin policy configuration
### 2. Permission Model
```
┌─────────────────┬───────┬──────────┐
│ Resource/Action │ admin │ readonly │
├─────────────────┼───────┼──────────┤
│ Agent │ │ │
│ create │ ✓ │ ✗ │
│ get/list │ ✓ │ ✓ │
│ update │ ✓ │ ✗ │
│ delete │ ✓ │ ✗ │
│ invoke (chat) │ ✓ │ ✗ │
├─────────────────┼───────┼──────────┤
│ ModelConfig │ │ │
│ create │ ✓ │ ✗ │
│ get/list │ ✓ │ ✓ (masked)│
│ update │ ✓ │ ✗ │
│ delete │ ✓ │ ✗ │
├─────────────────┼───────┼──────────┤
│ MCPServer │ │ │
│ create │ ✓ │ ✗ │
│ get/list │ ✓ │ ✓ │
│ update │ ✓ │ ✗ │
│ delete │ ✓ │ ✗ │
└─────────────────┴───────┴──────────┘
```
### 3. Authorization Architecture
- **Casbin RBAC model** with group policy support
- **API-level enforcement** (backend validates all requests)
- **Secret masking** for readonly users (API keys, credentials → `****`)
- **OIDC group integration** via group policy (`g, group:platform-team, admin`)
- **Multi-role support** via Casbin's native grouping mechanism
### 4. UI Integration
- New endpoint: `GET /api/auth/permissions` returns user's roles and capabilities
- Permission-based UI rendering (hide/show buttons based on role)
- Backend still enforces all permissions (UI is UX-only)
### 5. Policy Configuration
**Model (model.conf):**
```ini
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
```
**Policy (policy.csv):**
```csv
# Admin permissions
p, admin, Agent, get
p, admin, Agent, list
p, admin, Agent, create
p, admin, Agent, update
p, admin, Agent, delete
p, admin, Agent, invoke
# ... (full permissions for ModelConfig, MCPServer)
# Readonly permissions
p, readonly, Agent, get
p, readonly, Agent, list
p, readonly, ModelConfig, get
p, readonly, ModelConfig, list
p, readonly, MCPServer, get
p, readonly, MCPServer, list
# Group assignments (examples)
# g, group:kagent-admins, admin
# g, group:kagent-users, readonly
```
### 6. Role Resolution Flow
1. User authenticates → Principal contains `User.ID`, `User.Email`, `User.Groups` (from OIDC)
2. Casbin looks up all roles assigned to user's groups via `g` policies
3. User receives **all matching roles** (not just first match)
4. Permission check passes if **ANY** role grants the action
5. If no roles match, default to `admin` role
## Technical Details
- Replace `NoopAuthorizer` with `CasbinAuthorizer`
- Policy storage: ConfigMap (static), database adapter (dynamic), or runtime (OIDC mapping)
- Casbin caching for performance
- Unit tests covering all role combinations
## Extensibility
- Custom roles: Add policy lines (no code changes)
- New resource types: Add to policy configuration
- Future: Agent-to-agent authorization (Principal.Agent already available)
🔄 Alternatives Considered
### Alternative 1: OPA (Open Policy Agent)
- **Pros**: Rego policy language, broader policy use cases, CNCF project
- **Cons**: Steeper learning curve, more complex for simple RBAC, overkill for current needs
- **Decision**: Casbin is simpler, battle-tested for RBAC, sufficient for requirements
### Alternative 2: Kubernetes RBAC Only
- **Pros**: Native Kubernetes integration, no external library
- **Cons**: Cannot control API-level actions (only CRD access), no secret masking, limited to Kubernetes resources
- **Decision**: Need application-level authorization, not just K8s resource access
🎯 Affected Service(s)
UI Service
📚 Additional Context
Assumptions
- Authentication (OIDC, JWT, login flow) is handled separately and provides a `Principal` with:
- `User.ID` (from OIDC `sub` claim)
- `User.Email` (from OIDC `email` claim)
- `User.Groups` (from OIDC `groups` claim)
- Roles are NOT in the Principal - they are resolved at authorization time via Casbin
## Out of Scope
- Authentication implementation (OIDC provider configuration)
- Agent-to-agent authorization (future work)
- Audit logging (separate enhancement)
- Namespace-based resource filtering (all resources visible, but actions restricted)
## Risks & Mitigations
| Risk | Impact | Mitigation |
|------|--------|------------|
| Casbin policy misconfiguration | High | Unit tests for all role combinations |
| Performance impact of authz checks | Low | Casbin has built-in caching |
| Policy ConfigMap size limits | Low | ConfigMap supports up to 1MB |
| Breaking existing deployments | Medium | Keep NoopAuthorizer as fallback option |
## Open Questions
1. Should we support policy hot-reload without restart?
2. Should authorization decisions be logged for compliance/audit?
3. Should there be a UI for creating custom roles beyond `admin` and `readonly`?
🙋 Are you willing to contribute?
- I am willing to submit a PR for this feature
Reacted by Jairus Christensen, Guillaume Benchouka, Frédéric Marchand, Woo SeongJin, David J. M. Karlsen, acn-masataka-mizugaki, Karim Oueslati, sajadsameti, chaaben-v, Simon Baier and 7 more
Reacted by ITES.be
Metadata
Metadata
Assignees
Labels
staleThis issue or PR has become staleThis issue or PR has become stale
Type
Projects
- StatusShow more project fieldsBacklog
Activity
nujragan93 commentedon Feb 11, 2026
@EItanya I feel we should start by defining backend authorization interfaces in the application now, with implementations that call internal methods directly. This keeps the architecture simple, avoids unnecessary infrastructure, and minimizes latency since everything stays in-process. In the future, if requirements evolve, the same interfaces can be switched to make a network call to an external authorizer without requiring widespread refactoring across the codebase. I also feel that an external authorizer will also introduce a new single point of failure. what do you think?
EItanya commentedon Feb 17, 2026
On one hand I agree with you, but I also worry that picking a specific RBAC engine
implup front is a potentially losing battle, and by adding an extension point we allow people to put in whatever system they want, and there are many. We can of course take an opinionated approach in the Open Source like theoauth2-proxyimplemented by #1293, but the flexibility means we don't need to support one specific engine going forward. What do you think?EItanya commentedon Feb 17, 2026
Hey @nujragan93, any chance you could jump in Discord and we can continue the conversation there, I don't see you in the server currently.
nujragan93 commentedon Feb 17, 2026
@EItanya Just joined the server, lets continue the conversation there
OIDC proxy auth (#1293)
christensenjairus commentedon Apr 16, 2026
What's the status on this?
thehackercat commentedon Apr 19, 2026
Any update?
danialkhatib commentedon May 22, 2026
Any update? : )
davidkarlsen commentedon Jun 23, 2026
Picking this up again, because I think the current state is a real security problem and not just a missing feature.
Today the controller ships with
NoopAuthorizer, which means every authenticated user can do everything — list, invoke, edit and delete every Agent, ModelConfig and ToolServer, across every namespace. The moment someone enables OIDC (oauth2-proxy, #1293) they quite reasonably assume access control comes with it. It doesn't. For any shared or multi-tenant install, that gap is a genuine risk, not a nice-to-have.The good news: the hard part is already done. The
Authorizerinterface is wired into every handler, andProxyAuthenticatoralready hands it the full set of JWT claims. So this isn't a big refactor — it's "write one real authorizer and stop returningnil."On the design question raised earlier in this thread — an opinionated in-process RBAC engine vs. a pluggable extension point — I'd like to propose CEL as the default, because I think it makes both camps happy:
claims,verb,resource. Groups become one option among many rather than the mechanism:"team-a" in claims.groups,claims.role == "admin",resource.namespace == claims.dept, or whatever your IdP actually emits. That matters — pinning authz to agroupsclaim is too narrow; operators should be able to key off any claim.For the specific "give these people access to this agent" case: the policy can live right on the Agent CR (an annotation, or a
spec.accessPolicyfield), authored by whoever owns the agent — but still enforced centrally in the controller. We compile the expression once on reconcile, cache the compiled program, and surface a bad expression onstatus.conditions, so a misconfigured policy shows up onkubectl get agentinstead of silently failing at request time.Worth calling out: most of this has already been prototyped. #1766 did the per-agent annotation + list filtering + A2A gating, and #1370 did the external-authorizer interface. Both stalled on inactivity rather than getting rejected. I'd rather build on them than start over — keep #1766's enforcement points, generalize
allowed-groupsinto a CEL expression over claims, and keep #1370's interface as the external-authz escape hatch.If this direction sounds reasonable, I'm happy to write it up as an EP (a follow-on to EP-476, which explicitly deferred fine-grained authz) and take a run at the implementation.
@EItanya @peterj — does "CEL as the default, behind the existing
Authorizerinterface" line up with where you wanted this to land?marcellodesales commentedon Jul 7, 2026
@davidkarlsen I can be a tester... we have a private OIDC and we need this badly...
elihai-risk commentedon Jul 16, 2026
Adopter perspective — we're piloting kagent as a shared, multi-team agent platform, so this is directly on our critical path. A few observations that might help shape the direction, before anyone commits to an implementation:
Our primary need is visibility scoping, not just action gating. The most common ask from our end users is "I should only see my team's agents/tool servers, not everyone else's." The current proposal explicitly puts "namespace-based resource filtering (all resources visible, but actions restricted)" out of scope, which means admin/readonly roles alone wouldn't satisfy that use case — a
readonlyuser would still list every resource in every watched namespace. It looks like the list-filtering angle is closer to what's sketched in #1766. Is the intent to keep visibility-filtering as a separate track, or to fold it into this EP?Engine direction (Casbin vs. CEL). There seem to be two live directions — Casbin here, and the CEL-based proposal in #2075 (which leans on the fact that
cel-gois already in the module graph and keepsAuthorizeras a pluggable seam). For adopters it'd be very helpful to have a committed direction on the issue before code lands, since the two imply fairly different policy authoring/config stories. Do the maintainers have a preference, or is the pluggable-Authorizerseam (#1370) the intended contract, with Casbin/CEL as interchangeable implementations behind it?Default posture. The "authenticated users with no matching role default to
admin" behavior would be a surprising default for us; a default-deny (or explicit opt-in fallback) would fit least-privilege expectations better in shared environments. Worth pinning down as part of the design.Happy to share our multi-team requirements in more detail if useful.
marcellodesales commentedon Jul 22, 2026
Here's what I'm looking for:
I have found a couple of problems:
Question
How is this better managed with agentgateway? in a regular setting, I'm deploying kagent as a developer facility and where we intend to provide different Engineering groups access to their OIDC groups and, each of those groups be mapped to their namespaces... But in the end of the day, I still think agentgateway would provide the richer experience in terms of tooling and capability... Is this where this tooling is going towards?
tkm112345 commentedon Jul 25, 2026
I have the same need: restrict which agents each LDAP group (via the OIDC groups claim) can see and invoke, in a shared multi-team install. +1 on prioritizing this.
Rotfuks commentedon Aug 6, 2026
Pushing this topic a bit again, because we have a need for it as well :)
We are looking at kagent for a setup with multiple teams, where agents and sessions are shared between those teams. A few points from our side:
I'd suggest Sessions should be part of the permission model, not only agents. We work with collaborative sessions, where several people from different teams take part in the same conversation with an agent. So we need to be able to say who can join a session, who can read its history, and who should not see it at all. The content of a session can be just as sensitive as the configuration of an agent, because it contains everything that was exchanged in it. So a team might see an Agent with all shared sessions, while team members can still decide to have private sessions with the same Agent.
Much of the discussion here is about keeping teams separate from each other. In our case some agents and sessions are shared on purpose, so we also need a way to give a specific team access to a specific agent or session. So namespace might not be the best solution for us.
On defaults, we would also prefer, just like @elihai-risk, that a user without an assigned role gets no access at all, instead of getting admin access. Nobody notices when access is granted by accident, which makes this very hard to defend in a security review. We are confident that existing installations can be kept working in another way, for example by making the current permissive behaviour something an operator has to switch on explicitly.
On the choice of engine, my gut feeling is that we should not commit to one specific authorization engine here. We would rather see an interface that different engines can be plugged into, so that adopters can use the one that fits their environment. Is there a direction the maintainers are leaning towards?
Happy to join Discord to go through our requirements in more detail, and happy to help push this forward wherever it is useful, reviewing an EP, requirements input, tests, or taking implementation work. But I think we first need some commitment from the maintainers into a direction to take, right?
marcellodesales commentedon Aug 27, 2026
I've pushed a lot of work towards agentgateway and kagent to support multiple models of:
tlyyxjz commentedon Sep 4, 2026
Following this thread since we're watching the space closely — from the Casbin side (I contribute tooling to the Casbin ecosystem), here's how the requirements discussed here map to what already exists, in case it saves the team some design time:
The core need discussed here (per-namespace visibility + role gating + OIDC groups) is very close to Casbin's RBAC-with-domains model:
dommaps naturally to a Kubernetes namespace; OIDC group claims becomeg, <user>, <role>, <namespace>assignments (or map groups to roles once at login). The multiple adopters asking for "LDAP/OIDC group → which namespaces I can see" is literallyg(r.sub, p.sub, r.dom)+GetImplicitPermissionsForUser().objtypes (agent,session,apikey) in the same policy table rather than a separate system.NoopAuthorizerfor a casbin-backedAuthorizerinterface is a small adapter, and policies can live in the existing DB via gorm-adapter.On tooling/testing (relevant to the testing concerns raised earlier): we built casbin-config-doctor (diagnoses policy/model mismatches) and authztest (Go test helpers:
authztest.AssertAllow(t, e, "alice", "ns1", "agent", "invoke")) — useful if the team adopts policy-as-config and wants CI enforcement of expected allow/deny matrices.Honest caveats so you can evaluate fairly: casbin gives you decision (match request → allow/deny), not enforcement plumbing — you'd still wire the middleware, OIDC claim extraction, and data masking (masking secrets for non-admins is app-layer). And wildcard/ABAC matchers are powerful but easy to get subtly wrong, which is exactly the class of bug our doctor tool exists for.
Question for the maintainers: is the design discussion leaning towards a pluggable authorizer interface (so casbin / OpenFGA / custom could all be implementations), or a built-in model? If pluggable, I'm happy to sketch what a casbin-based implementation of that interface would look like against the requirements in this thread.