Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ release:
github:
owner: observiq
name: bindplane-operator
# Tags with a semver prerelease suffix (e.g. 0.2.0-rc.1, 0.1.0-beta.0) are
# published as GitHub prereleases, and only stable tags become "Latest".
prerelease: auto
make_latest: "{{ not .Prerelease }}"
extra_files:
- glob: tmp/install.yaml
- glob: tmp/install-no-webhook.yaml
Expand Down
69 changes: 69 additions & 0 deletions api/v1alpha1/bindplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,75 @@ type OIDCConfig struct {
// When true, users cannot be invited via email and must log in via OIDC directly.
// +optional
DisableInvitations bool `json:"disableInvitations,omitempty"`

// CustomClaims customizes the ID token claim names Bindplane reads and the
// group / role strings it matches within those claims. Omit a field to keep
// the Bindplane default.
// +optional
CustomClaims *OIDCCustomClaimsConfig `json:"customClaims,omitempty"`
}

// OIDCCustomClaimsConfig customizes OIDC claim names and the group / role strings
// Bindplane matches, so an existing identity provider does not have to be reshaped
// to Bindplane's default claim names.
//
// The first group of fields renames the claims Bindplane reads on the ID token.
// The second group changes the strings Bindplane matches inside the groups and
// roles claims (after claim names are resolved).
type OIDCCustomClaimsConfig struct {
// Groups is the claim name for user groups. Defaults to "groups".
// +optional
Groups string `json:"groups,omitempty"`

// GroupIDs is the claim name for group IDs, used as a fallback when the groups
// claim is missing, empty, or parses to an empty list. Defaults to "group_ids".
// +optional
GroupIDs string `json:"groupIds,omitempty"`

// Roles is the claim name for user roles. Defaults to "roles".
// +optional
Roles string `json:"roles,omitempty"`

// OrganizationAdmin is the claim name indicating organization admin status.
// Defaults to "bindplane_org_admin".
// +optional
OrganizationAdmin string `json:"organizationAdmin,omitempty"`

// Projects is the claim name for Bindplane project assignments.
// Defaults to "bindplane_projects".
// +optional
Projects string `json:"projects,omitempty"`

// DefaultRole is the claim name for default role assignment.
// Defaults to "bindplane_default_role".
// +optional
DefaultRole string `json:"defaultRole,omitempty"`

// OrgAdminGroupName is the group / role string that indicates organization admin
// status. Defaults to "bindplane-org-admin".
// +optional
OrgAdminGroupName string `json:"orgAdminGroupName,omitempty"`

// ProjectsGroupPrefix is the group / role prefix that encodes project assignments,
// e.g. "bindplane-projects-<projectID>" or "bindplane-projects-<role>:<projectID>".
// Defaults to "bindplane-projects-".
// +optional
ProjectsGroupPrefix string `json:"projectsGroupPrefix,omitempty"`

// AdminGroupName is the group / role string that indicates the admin role.
// Defaults to "bindplane-admin".
// +optional
AdminGroupName string `json:"adminGroupName,omitempty"`

// UserGroupName is the group / role string that indicates the user role.
// Defaults to "bindplane-user".
// +optional
UserGroupName string `json:"userGroupName,omitempty"`

// ViewerGroupName is the group / role string that indicates the viewer role.
// Defaults to "bindplane-viewer".
// +optional
ViewerGroupName string `json:"viewerGroupName,omitempty"`
}

// NetworkConfig defines network configuration
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions config/crd/bases/k8s.bindplane.com_bindplanes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14834,6 +14834,31 @@ spec:
- key
type: object
x-kubernetes-map-type: atomic
customClaims:
properties:
adminGroupName:
type: string
defaultRole:
type: string
groupIds:
type: string
groups:
type: string
orgAdminGroupName:
type: string
organizationAdmin:
type: string
projects:
type: string
projectsGroupPrefix:
type: string
roles:
type: string
userGroupName:
type: string
viewerGroupName:
type: string
type: object
disableInvitations:
type: boolean
issuer:
Expand Down
28 changes: 28 additions & 0 deletions docs/configuration/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,34 @@ _Appears in:_
| `issuer` _string_ | Issuer is the URL of the OIDC provider | | Optional: \{\} <br /> |
| `scopes` _string array_ | Scopes is the list of OAuth2 scopes to request | | Optional: \{\} <br /> |
| `disableInvitations` _boolean_ | DisableInvitations disables the invitation flow for OIDC-authenticated users.<br />When true, users cannot be invited via email and must log in via OIDC directly. | | Optional: \{\} <br /> |
| `customClaims` _[OIDCCustomClaimsConfig](#oidccustomclaimsconfig)_ | CustomClaims customizes the ID token claim names Bindplane reads and the<br />group / role strings it matches within those claims. Omit a field to keep<br />the Bindplane default. | | Optional: \{\} <br /> |

#### OIDCCustomClaimsConfig

OIDCCustomClaimsConfig customizes OIDC claim names and the group / role strings
Bindplane matches, so an existing identity provider does not have to be reshaped
to Bindplane's default claim names.

The first group of fields renames the claims Bindplane reads on the ID token.
The second group changes the strings Bindplane matches inside the groups and
roles claims (after claim names are resolved).

_Appears in:_
- [OIDCConfig](#oidcconfig)

| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `groups` _string_ | Groups is the claim name for user groups. Defaults to "groups". | | Optional: \{\} <br /> |
| `groupIds` _string_ | GroupIDs is the claim name for group IDs, used as a fallback when the groups<br />claim is missing, empty, or parses to an empty list. Defaults to "group_ids". | | Optional: \{\} <br /> |
| `roles` _string_ | Roles is the claim name for user roles. Defaults to "roles". | | Optional: \{\} <br /> |
| `organizationAdmin` _string_ | OrganizationAdmin is the claim name indicating organization admin status.<br />Defaults to "bindplane_org_admin". | | Optional: \{\} <br /> |
| `projects` _string_ | Projects is the claim name for Bindplane project assignments.<br />Defaults to "bindplane_projects". | | Optional: \{\} <br /> |
| `defaultRole` _string_ | DefaultRole is the claim name for default role assignment.<br />Defaults to "bindplane_default_role". | | Optional: \{\} <br /> |
| `orgAdminGroupName` _string_ | OrgAdminGroupName is the group / role string that indicates organization admin<br />status. Defaults to "bindplane-org-admin". | | Optional: \{\} <br /> |
| `projectsGroupPrefix` _string_ | ProjectsGroupPrefix is the group / role prefix that encodes project assignments,<br />e.g. "bindplane-projects-<projectID>" or "bindplane-projects-<role>:<projectID>".<br />Defaults to "bindplane-projects-". | | Optional: \{\} <br /> |
| `adminGroupName` _string_ | AdminGroupName is the group / role string that indicates the admin role.<br />Defaults to "bindplane-admin". | | Optional: \{\} <br /> |
| `userGroupName` _string_ | UserGroupName is the group / role string that indicates the user role.<br />Defaults to "bindplane-user". | | Optional: \{\} <br /> |
| `viewerGroupName` _string_ | ViewerGroupName is the group / role string that indicates the viewer role.<br />Defaults to "bindplane-viewer". | | Optional: \{\} <br /> |

#### OpAMPComponentSpec

Expand Down
84 changes: 84 additions & 0 deletions docs/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Configuration is provided via the `spec.config` field of the `Bindplane` custom
- [System auth](#system-auth)
- [LDAP and Active Directory](#ldap-and-active-directory)
- [OIDC](#oidc)
- [Custom claims](#custom-claims)
- [Network](#network)
- [Store](#store)
- [PostgreSQL](#postgresql)
Expand Down Expand Up @@ -278,6 +279,89 @@ spec:
key: client-secret
```

#### Custom claims

`spec.config.auth.oidc.customClaims` adapts Bindplane to an identity provider that already emits its own claim names or group naming scheme, so the IdP does not have to be reshaped to Bindplane's defaults.

There are two groups of settings:

- **Claim name overrides** (`groups`, `groupIds`, `roles`, `organizationAdmin`, `projects`, `defaultRole`) rename the claims Bindplane reads on the ID token.
- **Group / role string overrides** (`orgAdminGroupName`, `projectsGroupPrefix`, `adminGroupName`, `userGroupName`, `viewerGroupName`) change the strings Bindplane matches *inside* the groups and roles claims, after the claim names above are resolved.

Every field is optional. Omit a field to keep the Bindplane default; the operator only sets an environment variable for fields you populate.

`groupIds` is a fallback: Bindplane uses it when the groups claim is missing, empty, or parses to an empty list.

`projectsGroupPrefix` matches group entries that encode project access, either `<prefix><projectID>` or `<prefix><role>:<projectID>` (for example `bindplane-projects-admin:01PROJECTID`).

| CRD Field | Environment Variable | Default | Required |
|---|---|---|---|
| `spec.config.auth.oidc.customClaims.groups` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_GROUPS` | `groups` | No |
| `spec.config.auth.oidc.customClaims.groupIds` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_GROUP_IDS` | `group_ids` | No |
| `spec.config.auth.oidc.customClaims.roles` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_ROLES` | `roles` | No |
| `spec.config.auth.oidc.customClaims.organizationAdmin` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_ORGANIZATION_ADMIN` | `bindplane_org_admin` | No |
| `spec.config.auth.oidc.customClaims.projects` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_PROJECTS` | `bindplane_projects` | No |
| `spec.config.auth.oidc.customClaims.defaultRole` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_DEFAULT_ROLE` | `bindplane_default_role` | No |
| `spec.config.auth.oidc.customClaims.orgAdminGroupName` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_ORG_ADMIN_GROUP_NAME` | `bindplane-org-admin` | No |
| `spec.config.auth.oidc.customClaims.projectsGroupPrefix` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_PROJECTS_GROUP_PREFIX` | `bindplane-projects-` | No |
| `spec.config.auth.oidc.customClaims.adminGroupName` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_ADMIN_GROUP_NAME` | `bindplane-admin` | No |
| `spec.config.auth.oidc.customClaims.userGroupName` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_USER_GROUP_NAME` | `bindplane-user` | No |
| `spec.config.auth.oidc.customClaims.viewerGroupName` | `BINDPLANE_OIDC_CUSTOM_CLAIMS_VIEWER_GROUP_NAME` | `bindplane-viewer` | No |

Custom claims require Bindplane server v1.100.2 or later. On earlier versions the environment variables are ignored and the defaults apply.

Example (renaming claim names only):

```yaml
spec:
config:
auth:
type: oidc
oidc:
issuer: https://accounts.example.com
scopes:
- openid
- profile
- email
clientIDSecretRef:
name: oidc-secrets
key: client-id
clientSecretSecretRef:
name: oidc-secrets
key: client-secret
customClaims:
groups: "user_groups"
roles: "user_roles"
organizationAdmin: "acme_org_admin"
```

Example (renaming the group and role strings Bindplane matches):

```yaml
spec:
config:
auth:
type: oidc
oidc:
issuer: https://accounts.example.com
scopes:
- openid
- profile
- email
clientIDSecretRef:
name: oidc-secrets
key: client-id
clientSecretSecretRef:
name: oidc-secrets
key: client-secret
customClaims:
orgAdminGroupName: "acme-org-admin"
projectsGroupPrefix: "acme-projects-"
adminGroupName: "acme-admin"
userGroupName: "acme-user"
viewerGroupName: "acme-viewer"
```

## Network

TLS is generally not configured on the Bindplane server when you use Ingress or Gateway API to terminate TLS. In that case, only `remoteURL` (and optionally `webURL`) need to reflect the external URL; the server continues to listen over HTTP inside the cluster.
Expand Down
4 changes: 3 additions & 1 deletion docs/configuration/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The following are configured via the Bindplane custom resource and documented in
| Area | What you configure | How the operator uses it |
|------|--------------------|---------------------------|
| **License** | `spec.config.license` or `licenseSecretRef` | Injects `BINDPLANE_LICENSE` from value or Secret into Node, Jobs, NATS, and the Jobs Migrate Job. |
| **Authentication** | System auth (`username`/`password` or Secret refs), session secret (`sessionSecret` or `sessionSecretSecretRef`), API key (`apiKey` or `apiKeySecretRef`), LDAP bind user/password and [LDAP TLS](configuration.md#ldap-and-active-directory), OIDC client ID/secret or Secret refs, agent auth (`spec.config.agents.auth`) | Sets auth-related env vars; mounts LDAP TLS Secret when `spec.config.auth.ldap.tls` is set. |
| **Authentication** | System auth (`username`/`password` or Secret refs), session secret (`sessionSecret` or `sessionSecretSecretRef`), API key (`apiKey` or `apiKeySecretRef`), LDAP bind user/password and [LDAP TLS](configuration.md#ldap-and-active-directory), OIDC client ID/secret or Secret refs, [OIDC custom claims](configuration.md#custom-claims), agent auth (`spec.config.agents.auth`) | Sets auth-related env vars; mounts LDAP TLS Secret when `spec.config.auth.ldap.tls` is set. |
| **Network TLS** | [Network TLS](configuration.md#network): `spec.config.network.tls` (secretName, certKey, keyKey, caKey, minVersion, skipVerify) | Mounts the Secret at a fixed path and sets `BINDPLANE_TLS_*` env vars to the mounted file paths. Used when you want server-side or mutual TLS on the Bindplane server (often omitted when using [Ingress or Gateway API](configuration.md#network) to terminate TLS). |
| **PostgreSQL** | Postgres username/password (or Secret refs) and [PostgreSQL TLS](configuration.md#postgresql) (`spec.config.store.postgres.tls`, sslmode) | Injects credentials; mounts Postgres TLS Secret when TLS is configured and sets `BINDPLANE_POSTGRES_SSL_*` env vars. |
| **Metrics (Prometheus)** | Optional basic auth for the HTTP endpoint where Bindplane **exposes** its own metrics via `spec.config.metrics.prometheus.username` and `password` or `passwordSecretRef` | Sets `BINDPLANE_METRICS_PROMETHEUS_USERNAME` / `BINDPLANE_METRICS_PROMETHEUS_PASSWORD` when configured. Distinct from Bindplane TSDB remote write auth (below). |
Expand Down Expand Up @@ -226,6 +226,8 @@ This rule is enforced at admission time by the Kubernetes API server (and additi
| `spec.config.errors.frontendDSN` | `frontendDSNSecretRef` |
| `spec.config.status.keys` | `keysSecretRef` |

[OIDC custom claims](configuration.md#custom-claims) (`spec.config.auth.oidc.customClaims`) deliberately have no SecretRef variant: they are claim and group *names* that identify where Bindplane looks in the ID token, not credentials, and are set as plain env var values.

## Summary

| Secret / TLS | User-configurable? | Env vars (where applicable) | Where configured | Documentation |
Expand Down
15 changes: 15 additions & 0 deletions internal/controller/bindplane_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,21 @@ const (
// OIDC additional configuration
bindplaneOIDCDisableInvitationsEnvVar = "BINDPLANE_OIDC_DISABLE_INVITATIONS"

// OIDC custom claims: claim name overrides
bindplaneOIDCCustomClaimsGroupsEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_GROUPS"
bindplaneOIDCCustomClaimsGroupIDsEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_GROUP_IDS"
bindplaneOIDCCustomClaimsRolesEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_ROLES"
bindplaneOIDCCustomClaimsOrganizationAdminEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_ORGANIZATION_ADMIN"
bindplaneOIDCCustomClaimsProjectsEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_PROJECTS"
bindplaneOIDCCustomClaimsDefaultRoleEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_DEFAULT_ROLE"

// OIDC custom claims: group / role pattern overrides
bindplaneOIDCCustomClaimsOrgAdminGroupNameEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_ORG_ADMIN_GROUP_NAME"
bindplaneOIDCCustomClaimsProjectsGroupPrefixEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_PROJECTS_GROUP_PREFIX"
bindplaneOIDCCustomClaimsAdminGroupNameEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_ADMIN_GROUP_NAME"
bindplaneOIDCCustomClaimsUserGroupNameEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_USER_GROUP_NAME"
bindplaneOIDCCustomClaimsViewerGroupNameEnvVar = "BINDPLANE_OIDC_CUSTOM_CLAIMS_VIEWER_GROUP_NAME"

// NATS TLS additional configuration
bindplaneNatsTLSSkipVerifyEnvVar = "BINDPLANE_NATS_TLS_SKIP_VERIFY"
)
Expand Down
33 changes: 33 additions & 0 deletions internal/controller/config_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,39 @@ func getOIDCEnvVars(oidc *bindplanev1alpha1.OIDCConfig) []corev1.EnvVar {
if oidc.DisableInvitations {
envVars = append(envVars, corev1.EnvVar{Name: bindplaneOIDCDisableInvitationsEnvVar, Value: "true"})
}
envVars = append(envVars, getOIDCCustomClaimsEnvVars(oidc.CustomClaims)...)
return envVars
}

// getOIDCCustomClaimsEnvVars returns OIDC custom claims environment variables.
// Each field is omitted when empty so the Bindplane server default applies.
// Returns nil when customClaims is nil.
func getOIDCCustomClaimsEnvVars(customClaims *bindplanev1alpha1.OIDCCustomClaimsConfig) []corev1.EnvVar {
if customClaims == nil {
return nil
}
pairs := []struct {
name string
value string
}{
{bindplaneOIDCCustomClaimsGroupsEnvVar, customClaims.Groups},
{bindplaneOIDCCustomClaimsGroupIDsEnvVar, customClaims.GroupIDs},
{bindplaneOIDCCustomClaimsRolesEnvVar, customClaims.Roles},
{bindplaneOIDCCustomClaimsOrganizationAdminEnvVar, customClaims.OrganizationAdmin},
{bindplaneOIDCCustomClaimsProjectsEnvVar, customClaims.Projects},
{bindplaneOIDCCustomClaimsDefaultRoleEnvVar, customClaims.DefaultRole},
{bindplaneOIDCCustomClaimsOrgAdminGroupNameEnvVar, customClaims.OrgAdminGroupName},
{bindplaneOIDCCustomClaimsProjectsGroupPrefixEnvVar, customClaims.ProjectsGroupPrefix},
{bindplaneOIDCCustomClaimsAdminGroupNameEnvVar, customClaims.AdminGroupName},
{bindplaneOIDCCustomClaimsUserGroupNameEnvVar, customClaims.UserGroupName},
{bindplaneOIDCCustomClaimsViewerGroupNameEnvVar, customClaims.ViewerGroupName},
}
var envVars []corev1.EnvVar
for _, p := range pairs {
if p.value != "" {
envVars = append(envVars, corev1.EnvVar{Name: p.name, Value: p.value})
}
}
return envVars
}

Expand Down
Loading
Loading