feat: add group-based role mapping for FreeIPA IAM

FreeIPA users were always assigned RoleUser regardless of their
group membership. This adds --ipa-admin-group-cn so that members
of a specified FreeIPA group are automatically granted the admin
role, and  --ipa-enable-userplus to elevate the default role to
userplus for all IPA users. The user_show RPC is updated to
request all attributes so that memberof_group is reliably
returned.
This commit is contained in:
Ben McClelland
2026-05-29 20:53:06 -07:00
parent f7cc70b157
commit 7300306886
5 changed files with 60 additions and 15 deletions
+3 -1
View File
@@ -152,6 +152,8 @@ type Opts struct {
IpaVaultName string
IpaUser string
IpaPassword string
IpaAdminGroupCN string
IpaEnableUserPlus bool
IpaInsecure bool
}
@@ -179,7 +181,7 @@ func New(o *Opts) (IAMService, error) {
o.VaultServerCert, o.VaultClientCert, o.VaultClientCertKey)
fmt.Printf("initializing Vault IAM with %q\n", o.VaultEndpointURL)
case o.IpaHost != "":
svc, err = NewIpaIAMService(o.RootAccount, o.IpaHost, o.IpaVaultName, o.IpaUser, o.IpaPassword, o.IpaInsecure)
svc, err = NewIpaIAMService(o.RootAccount, o.IpaHost, o.IpaVaultName, o.IpaUser, o.IpaPassword, o.IpaAdminGroupCN, o.IpaEnableUserPlus, o.IpaInsecure)
fmt.Printf("initializing IPA IAM with %q\n", o.IpaHost)
default:
// if no iam options selected, default to the single user mode
+26 -13
View File
@@ -49,21 +49,25 @@ type IpaIAMService struct {
vaultName string
username string
password string
adminGroupCN string
enableUserPlus bool
kraTransportKey *rsa.PublicKey
rootAcc Account
}
var _ IAMService = &IpaIAMService{}
func NewIpaIAMService(rootAcc Account, host, vaultName, username, password string, isInsecure bool) (*IpaIAMService, error) {
func NewIpaIAMService(rootAcc Account, host, vaultName, username, password, adminGroupCN string, enableUserPlus, isInsecure bool) (*IpaIAMService, error) {
ipa := IpaIAMService{
id: 0,
version: IpaVersion,
host: host,
vaultName: vaultName,
username: username,
password: password,
rootAcc: rootAcc,
id: 0,
version: IpaVersion,
host: host,
vaultName: vaultName,
username: username,
password: password,
adminGroupCN: adminGroupCN,
enableUserPlus: enableUserPlus,
rootAcc: rootAcc,
}
jar, err := cookiejar.New(nil)
if err != nil {
@@ -124,15 +128,16 @@ func (ipa *IpaIAMService) GetUserAccount(access string) (Account, error) {
return ipa.rootAcc, nil
}
req, err := ipa.newRequest("user_show/1", []string{access}, map[string]any{})
req, err := ipa.newRequest("user_show/1", []string{access}, map[string]any{"all": true})
if err != nil {
return Account{}, fmt.Errorf("ipa user_show: %w", err)
}
userResult := struct {
Gidnumber []string
Uidnumber []string
PidNumber []string
Gidnumber []string `json:"gidnumber"`
Uidnumber []string `json:"uidnumber"`
PidNumber []string `json:"pidnumber"`
MemberOfGroup []string `json:"memberof_group"`
}{}
err = ipa.rpc(req, &userResult)
@@ -153,9 +158,17 @@ func (ipa *IpaIAMService) GetUserAccount(access string) (Account, error) {
return Account{}, err
}
role := RoleUser
if ipa.enableUserPlus {
role = RoleUserPlus
}
if ipa.adminGroupCN != "" && slices.Contains(userResult.MemberOfGroup, ipa.adminGroupCN) {
role = RoleAdmin
}
account := Account{
Access: access,
Role: RoleUser,
Role: role,
UserID: uid,
GroupID: gid,
ProjectID: pId,