How to backup and restore of FreeIPA with Wink: Don’t try this at home?

Davinder Pal
3 min readNov 25, 2023
AI Generated

What is FreeIPA?

FreeIPA is an integrated security information management solution combining Linux (Fedora), 389 Directory Server, MIT Kerberos, NTP, DNS, Dogtag (Certificate System). It consists of a web interface and command-line administration tools.

Assumptions:
1. I am not using kerberos/DNS/dogtag sub-systems, why not, good question?
2. My restore script assumes that there is a default group with same GID if that is not the case, then please do write a restore group script first then use modify my script bit to include GID in restore process and use it.

Major drawback in their backup and restore process, which is that it does not support migrating hosts from one hostname to another. The backup and restore process assumes that the hostname remains the same. In my case, I had no other better options for this simple task and It also improved my backup/restore process because earlier, I was taking full backup like documentation, which was overkill in my case and not effective at all.

Since, I am not using most of FreeIPA features like kerberos/DNS/NTP/dogtag, it is become super easy once, I understood the Directory Server and its usage. In my case, we had a service which was creating users in Directory Service and later use their uid/guid when authenticating with other components like Apache Impala with kerberos.
let’s go to the meat of the solution.

import ipahttp # https://github.com/nordnet/python-freeipa-json

ipa = ipahttp.ipa(ipa_host)
ipa.login('admin', os.getenv("IPA_PASS"))
result = ipa.user_find()
dump_file = open(data_file, "w")
dump_file.write(json.dumps(result["result"]["result"]))

so what I am doing with above code, I am reading all the users from Directory Server over HTTP with python-freeipa-json and storing them into a simple json file like shown below

[
{
"dn": "uid=admin,cn=users,cn=accounts,dc=example,dc=local",
"krbextradata": [
{
"__base64__": "AAIGmZFkcm9vdC9xxxxxxxVERVJBLkxPQ0FMAA=="
}
],
"cn": ["Administrator"],
"krbpasswordexpiration": ["20230918121814Z"],
"objectclass": [
"top",
"person",
"posixaccount",
"krbprincipalaux",
"krbticketpolicyaux",
"inetuser",
"ipaobject",
"ipasshuser",
"ipaSshGroupOfPubKeys"
],
"loginshell": ["/bin/bash"],
"sn": ["Administrator"],
"uidnumber": ["194200000"],
"gidnumber": ["194200000"],
"memberof_group": ["admins", "trust admins"],
"gecos": ["Administrator"],
"ipauniqueid": ["f20cb142-0f63-11ee-8778-00505681606b"],
"homedirectory": ["/home/admin"],
"krbprincipalname": ["admin@EXAMPLE.LOCAL"],
"krblastpwdchange": ["20230620121814Z"],
"preserved": false,
"nsaccountlock": false,
"uid": ["admin"]
},
{
"memberof_group": ["ipausers", "service_users"],
"cn": ["Database Service Admin"],
"krbcanonicalname": ["dbservice@EXAMPLE.LOCAL"],
"krbextradata": [
{
"__base64__": "AAKEmZFkcm9vdC9hZGxxxxxxxxxERVJBLkxPQ0FMAA=="
}
],
"homedirectory": ["/home/dbservice"],
"memberof_role": ["User Administrator"],
"nsaccountlock": false,
"uid": ["dbservice"],
"loginshell": ["/bin/sh"],
"uidnumber": ["194200004"],
"preserved": false,
"mail": ["dbservice@example.local"],
"dn": "uid=dbservice,cn=users,cn=accounts,dc=example,dc=local",
"displayname": ["Database Service Admin"],
"mepmanagedentry": [
"cn=dbservice,cn=groups,cn=accounts,dc=example,dc=local"
],
"ipauniqueid": ["d337508c-0f64-11ee-b212-00505681606b"],
"krbprincipalname": ["dbservice@EXAMPLE.LOCAL"],
"givenname": ["Database"],
"objectclass": [
"top",
"...."
],
"gidnumber": ["194200004"],
"gecos": ["Database Admin"],
"sn": ["Service Admin"],
"krblastpwdchange": ["20230620122020Z"],
"initials": ["DS"]
}
]

In my, my applications are dependent on couple of fields from above data
1. uidnumber
2. gidnumber
3. memberof_group
4. uid
Rest of field can change including kerberos data + password + anythings else.

So how the restore will look like. I think, before that, I should explain, in which use-cases, it should actually used so we can on same page and later you don’t blame me later, if you ducked your shit FreeIPA solution.

  1. If you have lost all of your freeIPA machines.
  2. If you want to migrate all your users.
  3. If you just want to restore deleted individual users.
  4. If you can think of something different.
import ipahttp

ipa = ipahttp.ipa(os.getenv("IPA_HOST"))
ipa.login('admin', os.getenv("IPA_PASS"))


with open(json_file) as json_data:
data = json.load(json_data)

dontOverWriteUsers = ["admin"]
count = 0
print("| {0:<60} | {1:<10} | {2}".format("User"[:60], "UID", "Message"))
print("-"*85)
for i in data:
if i["uid"][0] not in dontOverWriteUsers:
__user_data = {
"givenname": i["givenname"][0],
"sn": i["sn"][0],
"uidnumber": i["uidnumber"][0]
}

results = ipa.user_add(i['uid'][0], opts=__user_data)

if results["result"] == None:
if results["error"]["name"] == "ValidationError":
message = results["error"]["message"]
elif results["error"]["name"] == "DuplicateEntry":
message = results["error"]["message"]
else:
message = "User Created*"

print("| {0:<60} | {1:<10} | {2}".format(i['uid'][0][:60], i["uidnumber"][0], message))
count += 1
else:
print("| {0:<60} | {1:<10} | {2}".format(i['uid'][0][:60], "", "Skipped"))
python import-user-json.py freeipa-dump-data.json 
**************************************************
Logged to FreeIPA: freeipa01.example.com

Using backup file: freeipa-dump-data.json
**************************************************

| User | UID | Message
----------------------------------------------------------
| admin | | Skipped
| example | 194202318 | user with name "example" already exists
| example1 | 194201652 | user with name "example1" already exists
........
| example_created_users | 194203011 | User Created*.
....

If everything goes well, you will have all the users and proper UID/GID with them.

--

--