Skip to content
Open
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
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
![Baton Logo](./docs/images/baton-logo.png)

# `baton-ipa` [![Go Reference](https://pkg.go.dev/badge/github.com/conductorone/baton-ipa.svg)](https://pkg.go.dev/github.com/conductorone/baton-ipa) ![main ci](https://github.com/conductorone/baton-ipa/actions/workflows/main.yaml/badge.svg)

`baton-ipa` is a connector for IPA (Identity, Policy & Audit) Servers built using the [Baton SDK](https://github.com/conductorone/baton-sdk). It communicates using the LDAP protocol to sync data about roles, users, and groups.
`baton-ipa` is a connector for FreeIPA and Red Hat Identity Management, built using the [Baton SDK](https://github.com/conductorone/baton-sdk). It speaks LDAP to the IPA directory to sync users, groups, roles, hosts and host groups, and reads HBAC rules to derive host access.

Check out [Baton](https://github.com/conductorone/baton) to learn more about the project in general.

## LDAP

## Credentials

To access the IPA server, you must provide the username and password you use to login to the IPA server.
The connector binds to the IPA directory over LDAP. You need the distinguished name and password of an account that can read the directory — for example `uid=c1-service,cn=users,cn=accounts,dc=example,dc=com`. To provision access, that account also needs write access to the `member` and `memberManager` attributes on the groups and roles it manages.

# Getting Started

_Also see [Set up an LDAP connector](https://www.conductorone.com/docs/product/integrations/ldap/) in the ConductorOne documentation for instructions including using LDAP from ConductorOne._
_See [docs/connector.mdx](./docs/connector.mdx) for the customer-facing setup walkthrough, including how to configure the connector from ConductorOne._

## Installing

Expand All @@ -39,8 +35,12 @@ brew install conductorone/baton/baton conductorone/baton/baton-ipa
| `--url` | `BATON_URL` | **required** URL to the LDAP server. Can be either `ldap:` or `ldaps:` schemes, sets the hostname, and optionally a port number. For example: `ldaps://ldap.example.com:636` |
| `--base-dn` | `BATON_BASE_DN` | **optional** Base Distinguished name to search for LDAP objects in, for example `DC=example,DC=com` |
| `--user-search-dn` | `BATON_USER_SEARCH_DN` | **optional** Distinguished name to search for User objects in. If unset the Base DN is used. |
| `--group-search-dn` | `BATON_GROUP_SEARCH_DN` | **optional** Distinguished name to search for User objects in. If unset the Base DN is used. |
| `--provisioning` | `BATON_PROVISIONING` | **optional** Enable Provisioning of Groups and Roles by `baton-ipa`. `true` or `false`. Defaults to `false` |
| `--group-search-dn` | `BATON_GROUP_SEARCH_DN` | **optional** Distinguished name to search for Group objects in. If unset the Base DN is used. |
| `--role-search-dn` | `BATON_ROLE_SEARCH_DN` | **optional** Distinguished name to search for Role objects in. If unset the Base DN is used. For example: `cn=roles,cn=accounts,dc=example,dc=com`. Role entries must also have a `cn=roles` component in their own DN — pointing this at a container without one syncs zero roles. |
| `--filter` | `BATON_FILTER` | **optional** An additional LDAP filter applied to every search. For example `(!(objectClass=computer))` excludes every entry with that object class. |
| `--insecure-skip-verify` | `BATON_INSECURE_SKIP_VERIFY` | **optional** When connecting over TLS, skip verification of the server certificate. `true` or `false`. Defaults to `false` |
| `--disable-operational-attrs` | `BATON_DISABLE_OPERATIONAL_ATTRS` | **optional** Do not fetch operational attributes. Some LDAP servers do not support them. When set, `created_at` and last login are not synced. `true` or `false`. Defaults to `false` |
| `--provisioning` | `BATON_PROVISIONING` | **optional** Enable provisioning by `baton-ipa`: grant and revoke on group `member` and `manager` and on role `member`. `true` or `false`. Defaults to `false` |

Use `baton-ipa --help` to see all configuration flags and environment variables.

Expand Down Expand Up @@ -115,14 +115,15 @@ After successfully syncing data, use the baton CLI to list the resources and see

# Data Model

`baton-ipa` will fetch information about the following IPA resources:
`baton-ipa` syncs the following IPA resource types:

- Users (`posixAccount`)
- Groups (`ipaUserGroup`) — entitlements: `member`, `manager`
- Roles (`groupOfNames` under the role search DN, matching entries also need a `cn=roles` component in their own DN) — entitlement: `member`, grantable to users, groups, hosts and host groups
- Hosts (`ipaHost`)
- Host groups (`ipaHostGroup`)

- Users
- Roles
- Groups
- Host
- Host Groups
- HBAC Rules
HBAC rules (`ipaHBACRule`) are read but are not synced as a resource type of their own. They are the source of the entitlements that appear on hosts and host groups: each rule naming a host or host group becomes an entitlement on it. A rule whose `hostCategory` or `userCategory` is `all` — including the `allow_all` rule FreeIPA ships — is emitted against a single virtual `any-host` or `anyone` resource rather than expanded across every host.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The virtual wildcard group is anyone-group in code (pkg/connector/group.go:37), displayed as Anyone; anyone matches neither the ID nor the display name. The host one is correct (any-host, pkg/connector/host.go:22). Worth aligning so a reader searching C1 for these resources finds them.


`baton-ipa` will sync information only from under the base DN specified by the `--base-dn` flag in the configuration.

Expand Down
214 changes: 214 additions & 0 deletions docs/connector.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
---
title: "Set up a FreeIPA connector"
og:title: "Set up a FreeIPA connector"
description: "C1 provides identity governance and just-in-time provisioning for FreeIPA. Integrate your FreeIPA deployment with C1 to run user access reviews (UARs), enable just-in-time access requests, and automatically provision and deprovision access."
og:description: "C1 provides identity governance and just-in-time provisioning for FreeIPA. Integrate your FreeIPA deployment with C1 to run user access reviews (UARs), enable just-in-time access requests, and automatically provision and deprovision access."
sidebarTitle: "FreeIPA"
---

The FreeIPA connector reads your directory over LDAP. It works with FreeIPA and with Red Hat Identity Management, which ships the same server components.

{/* AUTO-GENERATED:START - capabilities
Generated from baton_capabilities.json. Do not edit manually. */}

## Capabilities

| Resource | Sync | Provision |
| :--- | :--- | :--- |
| Accounts | <Icon icon="square-check" iconType="solid" color="#c937ae"/> | |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The Accounts row leaves the Provision column empty, but baton_capabilities.json declares CAPABILITY_RESOURCE_DELETE on user, and both the note at line 27 and docs/docs-info.md say accounts are deprovisioned by deletion. The table and the prose disagree — either mark the row (with a "delete only, no create" qualifier) or say in the table why deletion is not counted as provisioning.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Table already left the Provision cell blank for Accounts, and now the prose agrees (deletion currently errors rather than works, so it is not counted as a working Provision capability). Kept the empty cell rather than adding a checkmark.

| Groups (`ipaUserGroup` in IPA) | <Icon icon="square-check" iconType="solid" color="#c937ae"/> | <Icon icon="square-check" iconType="solid" color="#c937ae"/> |
| Roles (`groupOfNames` in IPA) | <Icon icon="square-check" iconType="solid" color="#c937ae"/> | <Icon icon="square-check" iconType="solid" color="#c937ae"/> |
| Hosts (`ipaHost` in IPA) | <Icon icon="square-check" iconType="solid" color="#c937ae"/> | |
| Host groups (`ipaHostGroup` in IPA) | <Icon icon="square-check" iconType="solid" color="#c937ae"/> | |

{/* AUTO-GENERATED:END - capabilities */}

**Notes:**

- Groups offer two entitlements: **member** and **manager**. Roles offer **member**, and a role membership can be granted to a user, a group, a host, or a host group.
- Host and host-group access comes from your HBAC rules rather than from a fixed entitlement per resource. Each rule that names a host or host group becomes an entitlement on that resource, so what C1 shows follows the rules you have written.
- A rule that applies to every host or every user — one whose `hostCategory` or `userCategory` is set to `all`, such as the `allow_all` rule FreeIPA ships — appears in C1 as a grant against a single **Any** or **Anyone** resource, rather than being expanded across your whole estate.
- Group and role memberships expand. When a group is granted a role, C1 resolves the group's members through to that role, including members reached through nested groups.
- Account deprovisioning by deletion is not yet supported end to end: the resource ID the connector stores for a user (`ipaUniqueID`) doesn't match what the delete path expects (a DN), so a delete call currently errors instead of removing the entry. The connector does not create accounts.
- Last login comes from the Kerberos `krbLastSuccessfulAuth` attribute, which is an operational attribute. Setting `--disable-operational-attrs` turns off both last login and created-at for every account.

## Gather FreeIPA credentials

<Warning>
To configure the FreeIPA connector you need an account that can read your directory over LDAP. To let C1 provision access, that account also needs write access to the group and role membership attributes.
</Warning>

<Steps>
<Step>
Choose the account the connector will bind as.

<Tip>
Create a dedicated service account rather than reusing `admin`. It keeps the connector's access reviewable and lets you scope it to read-only if you do not want C1 to provision access.
</Tip>
</Step>

<Step>
Record the account's full distinguished name and password.

The distinguished name is the complete path to the entry, for example `uid=c1-service,cn=users,cn=accounts,dc=example,dc=com`.
</Step>

<Step>
Grant the account read access to the parts of the directory you want C1 to see: the user, group and role containers, your hosts and host groups, and your HBAC rules.
</Step>

<Step>
To let C1 provision access, grant the account write access to the `member` and `memberManager` attributes on the groups and roles you want it to manage.

<Warning>
Write access is used by C1 when automatically provisioning access. **If you do not want C1 to perform these tasks, grant read access only.**
</Warning>
</Step>

<Step>
Record the URL of your FreeIPA server, using either the `ldap:` or `ldaps:` scheme and optionally a port, for example `ldaps://ipa.example.com:636`.

<Warning>
Prefer `ldaps:` in production. The connector binds with a password, and a plain `ldap:` connection sends it unencrypted.
</Warning>
</Step>
</Steps>

**Done.** Next, move on to the connector configuration instructions.

## Configure the FreeIPA connector

<Tabs>
<Tab title="Cloud-hosted">
**Follow these instructions to use a built-in, no-code connector hosted by C1.**

*Cloud-hosted connector not currently available.*
</Tab>

<Tab title="Self-hosted">
Follow these instructions to use the [FreeIPA](https://github.com/conductorone/baton-ipa) connector, hosted and run in your own environment.

When running in service mode on Kubernetes, a self-hosted connector maintains an ongoing connection with C1, automatically syncing and uploading data at regular intervals. This data is immediately available in the C1 UI for access reviews and access requests.

### Step 1: Set up a new FreeIPA connector

<Steps>
<Step>
In C1, navigate to **Integrations** > **Connectors** > **Add connector**.
</Step>

<Step>
Search for **Baton** and click **Add**.
</Step>

<Step>
Choose how to set up the new FreeIPA connector:

- Add the connector to a currently unmanaged app (select from the list of apps that were discovered in your identity, SSO, or federation provider that aren't yet managed with C1)
- Add the connector to a managed app (select from the list of existing managed apps)
- Create a new managed app
</Step>

<Step>
Set the owner for this connector. You can manage the connector yourself, or choose someone else from the list of C1 users. Setting multiple owners is allowed.

If you choose someone else, C1 will notify the new connector owner by email that their help is needed to complete the setup process.
</Step>

<Step>
Click **Next**.
</Step>

<Step>
In the **Settings** area of the page, click **Edit**.
</Step>

<Step>
Click **Rotate** to generate a new Client ID and Secret.

Carefully copy and save these credentials. We'll use them in Step 2.
</Step>
</Steps>

### Step 2: Create Kubernetes configuration files

Create two Kubernetes manifest files for your FreeIPA connector deployment:

#### Secrets configuration

```yaml expandable
# baton-ipa-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: baton-ipa-secrets
type: Opaque
stringData:
# C1 credentials
BATON_CLIENT_ID: <C1 client ID>
BATON_CLIENT_SECRET: <C1 client secret>

# FreeIPA credentials
BATON_URL: <Your FreeIPA server URL, for example ldaps://ipa.example.com:636>
BATON_BIND_DN: <The distinguished name the connector binds as>
BATON_PASSWORD: <That account's password>
BATON_BASE_DN: <The distinguished name to search under>

# Optional: include if you want C1 to provision access using this connector
BATON_PROVISIONING: "true"
```

See the connector's README or run `--help` to see all available configuration flags and environment variables.

#### Deployment configuration

```yaml expandable
# baton-ipa.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: baton-ipa
labels:
app: baton-ipa
spec:
selector:
matchLabels:
app: baton-ipa
template:
metadata:
labels:
app: baton-ipa
baton: "true"
baton-app: ipa
spec:
containers:
- name: baton-ipa
image: public.ecr.aws/conductorone/baton-ipa:latest
imagePullPolicy: IfNotPresent
env:
- name: BATON_HOST_ID
value: baton-ipa
envFrom:
- secretRef:
name: baton-ipa-secrets
```

### Step 3: Deploy the connector

<Steps>
<Step>
Create a namespace in which to run C1 connectors (if desired), then apply the secret config and deployment config files.
</Step>

<Step>
Check that the connector data uploaded correctly. In C1, click **Applications**. On the **Managed apps** tab, locate and click the name of the application you added the FreeIPA connector to. FreeIPA data should be found on the **Entitlements** and **Accounts** tabs.
</Step>
</Steps>

**Done.** Your FreeIPA connector is now pulling access data into C1.
</Tab>
</Tabs>

<Tip>
Looking for a specific version of this connector? Browse the available releases at [dist.conductorone.com/ConductorOne/baton-ipa](https://dist.conductorone.com/ConductorOne/baton-ipa).
</Tip>
65 changes: 65 additions & 0 deletions docs/docs-info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
While developing the connector, please fill out this form. This information is needed to write docs and to help other users set up the connector.

## Connector capabilities

1. What resources does the connector sync?

- Users (`posixAccount`)
- Groups (`ipaUserGroup`)
- Roles (`groupOfNames` under the role search DN)
- Hosts (`ipaHost`)
- Host groups (`ipaHostGroup`)

HBAC rules (`ipaHBACRule`) are read but are not synced as their own resource type. They are the source of the entitlements that appear on hosts and host groups.

2. Can the connector provision any resources? If so, which ones?

Yes, partially.

| Resource | Grant | Revoke | Create | Delete |
| :--- | :--- | :--- | :--- | :--- |
| Groups | ✅ `member` and `manager`, by adding the principal's DN to `member` / `memberManager` | ✅ removes the DN from that attribute | - | - |
| Roles | ✅ `member`, accepting users, groups, hosts and host groups as principals | ✅ | - | - |
| Users | - | - | - | ⚠️ not working today (see below) |
| Hosts | - | - | - | - |
| Host groups | - | - | - | - |

Account creation is not implemented. Account deprovisioning is meant to be a hard delete of the entry, not a disable, but `Delete`/`Get` currently canonicalize the resource ID as a DN while user resources are keyed by `ipaUniqueID`, so a delete call errors instead of removing the entry. Not documented as a working capability until that's fixed.

## Connector credentials

1. What credentials or information are needed to set up the connector? (For example, API key, client ID and secret, domain, etc.)

- **URL**: the FreeIPA server address, `ldap:` or `ldaps:` scheme, optional port (for example `ldaps://ipa.example.com:636`)
- **Bind DN**: the full distinguished name of the account the connector binds as (for example `uid=c1-service,cn=users,cn=accounts,dc=example,dc=com`)
- **Password**: that account's password
- **Base DN**: the distinguished name the connector searches under (for example `dc=example,dc=com`)

2. For each item in the list above:

* How does a user create or look up that credential or info?

* **URL**: the hostname of the IPA server the customer already administers. Port 389 for `ldap:`, 636 for `ldaps:`.
* **Bind DN**: created in FreeIPA like any other user — `ipa user-add`, or Identity → Users in the Admin UI. The DN is the entry's path; for a user created in the default container it is `uid=<login>,cn=users,cn=accounts,<base DN>`.
* **Password**: set when the account is created, or with `ipa passwd`.
* **Base DN**: derived from the IPA realm. A realm of `EXAMPLE.COM` gives a base DN of `dc=example,dc=com`.

* Does the credential need any specific scopes or permissions? If so, list them here.

LDAP has no scope model, so access is granted through directory permissions rather than scopes. The bind account needs read access to the containers holding users, groups, roles, hosts, host groups and HBAC rules.

* If applicable: Is the list of scopes or permissions different to sync (read) versus provision (read-write)? If so, list the difference here.

* **Sync (read-only)**: read access over the base DN subtree. Reading `krbLastSuccessfulAuth` for last login also requires that operational attributes are available; the `--disable-operational-attrs` flag turns that off, along with created-at.
* **Provisioning (read-write)**: additionally, write access to the `member` and `memberManager` attributes on the groups and roles C1 should manage.

* What level of access or permissions does the user need in order to create the credentials?

An account that can create users and grant directory permissions — in practice a member of `admins`, or a role holding the *User Administrator* and *Host Administrator* privileges.

## Notes for reviewers

- **Deployment shape.** This connector speaks LDAP to a server that is usually internal-only, so most customers run it self-hosted rather than cloud-hosted. A cloud-hosted connector would need network reach from C1's infrastructure to the IPA server.
- **The `--domain` and `--user-dn` flags are deprecated** and hidden from the generated config schema. `--url` replaces `--domain`; `--bind-dn` replaces `--user-dn`. New setups should never need them.
- **Wildcard HBAC rules.** A rule whose `hostCategory` or `userCategory` is `all` — including the `allow_all` rule FreeIPA ships enabled — is modelled as a grant against a single virtual **Any host** or **Anyone** resource rather than being expanded across every host. This keeps a default IPA install from producing a grant per host, but it does mean the wildcard is visible in C1 as one row rather than as broad access.
- **No plan or tier gating.** FreeIPA is open source and Red Hat Identity Management ships with RHEL; there are no paid tiers that hide resource types from this connector.
Loading
Loading