Windows 11 + FreeIPA (no AD)

Shared foundation page — how a Windows 11 host that is not joined to Active Directory talks directly to FreeIPA. Referenced from the Keycloak-on-Windows and FreeIPA-CLI-on-Windows pages so they do not each repeat the prereqs.

What this setup actually gives you
  • Windows 11 trusts the FreeIPA CA, so TLS (LDAPS, HTTPS to the IPA UI, IPA-issued service certs) validates.
  • Kerberos tickets can be obtained from the FreeIPA KDC using MIT Kerberos for Windows, native Windows SSPI (with ksetup), or WSL2.
  • DNS is pointed at the IPA servers so SRV-record discovery works.
  • Clocks are synced to the IPA NTP servers so Kerberos does not fail on skew.

What it does not give you. There is no SSSD on Windows. HBAC is not enforced on the Windows host. Group membership is only visible to applications that look it up over LDAP/Kerberos. Your Windows login is still a local or Microsoft account — this is not "joining" Windows to FreeIPA.

What you need before starting

Step 1: trust the FreeIPA CA on Windows

Three trust stores matter, and the first two are almost always required:

Windows Local Machine store (for browsers, curl, PowerShell)

Open an elevated PowerShell and run:

Import-Certificate `
  -FilePath C:\certs\ipa-ca.crt `
  -CertStoreLocation Cert:\LocalMachine\Root

Verify with certutil -store Root — you should see the IPA CA subject in the list. Browsers (Edge, Chrome, and the modern Firefox policy that honours the Windows store) now trust IPA-issued certs. So does certutil -verify, curl.exe, and Invoke-WebRequest.

Tip. Firefox by default uses its own cert store. Either enable Settings → Privacy & Security → Certificates → "Allow Firefox to automatically trust third-party root certificates…", or import the CA into Firefox explicitly.

Java truststore (for anything that runs on Java on this host)

Critical if you are going to run Keycloak with kc.bat, but also useful for any other JVM-based tool. Example against the default JDK truststore (adjust path for your JDK):

& "$env:JAVA_HOME\bin\keytool.exe" -importcert `
  -trustcacerts -noprompt `
  -alias freeipa-ca `
  -file C:\certs\ipa-ca.crt `
  -keystore "$env:JAVA_HOME\lib\security\cacerts" `
  -storepass changeit

Or, for app-local truststores (preferred for Keycloak so a JDK upgrade does not wipe your trust), build a dedicated file and point the JVM at it:

& "$env:JAVA_HOME\bin\keytool.exe" -importcert `
  -trustcacerts -noprompt `
  -alias freeipa-ca `
  -file C:\certs\ipa-ca.crt `
  -keystore C:\keycloak\conf\truststore.p12 `
  -storetype PKCS12 `
  -storepass changeit

Then when launching Keycloak or any JVM tool:

set JAVA_OPTS_APPEND=-Djavax.net.ssl.trustStore=C:\keycloak\conf\truststore.p12 -Djavax.net.ssl.trustStorePassword=changeit -Djavax.net.ssl.trustStoreType=PKCS12

WSL2 trust store (if you use WSL2 for the IPA CLI)

Inside WSL:

# Debian/Ubuntu
sudo cp /mnt/c/certs/ipa-ca.crt /usr/local/share/ca-certificates/ipa-ca.crt
sudo update-ca-certificates

# Fedora/Rocky/Alma
sudo cp /mnt/c/certs/ipa-ca.crt /etc/pki/ca-trust/source/anchors/ipa-ca.crt
sudo update-ca-trust

Step 2: configure Kerberos

Windows has a built-in Kerberos stack (SSPI). We point it at the IPA realm with a krb5.ini file. MIT Kerberos for Windows uses the same file location.

Create C:\ProgramData\MIT\Kerberos5\krb5.ini (MIT KfW default; also the location most tools look at):

[libdefaults]
    default_realm = EXAMPLE.INTERNAL
    dns_lookup_realm = true
    dns_lookup_kdc = true
    rdns = false
    ticket_lifetime = 24h
    renew_lifetime = 7d
    forwardable = true
    udp_preference_limit = 0

[realms]
    EXAMPLE.INTERNAL = {
        kdc = ipa.example.internal:88
        master_kdc = ipa.example.internal:88
        admin_server = ipa.example.internal:749
        default_domain = example.internal
        pkinit_anchors = FILE:C:\certs\ipa-ca.crt
    }

[domain_realm]
    .example.internal = EXAMPLE.INTERNAL
    example.internal = EXAMPLE.INTERNAL

For Windows built-in SSPI Kerberos (useful for SSO to an IPA-backed web app from Edge), also register the realm with ksetup in an elevated command prompt:

ksetup /AddKdc EXAMPLE.INTERNAL ipa.example.internal
ksetup /SetRealmFlags EXAMPLE.INTERNAL TcpSupported NoPacRequested
ksetup /AddHostToRealmMap example.internal EXAMPLE.INTERNAL
ksetup /AddHostToRealmMap .example.internal EXAMPLE.INTERNAL
Do not use ksetup /SetRealm EXAMPLE.INTERNAL. That tries to make the Windows logon use IPA and requires a reboot into a broken state if you do not also set up a local account mapping. For application-level Kerberos (browser SSO, IMAP, Keycloak SSO) the /AddKdc + /AddHostToRealmMap pair is what you want.

Step 3: DNS and SRV records

Kerberos and SSSD clients locate the KDC via DNS SRV records. Your Windows host must be able to resolve them.

Easiest: point Windows at the IPA-integrated DNS. On the NIC:

Get-NetAdapter | Where-Object Status -eq 'Up'
Set-DnsClientServerAddress -InterfaceIndex <idx> -ServerAddresses 10.0.0.10,10.0.0.11

Verify from PowerShell:

Resolve-DnsName -Type SRV _kerberos._tcp.example.internal
Resolve-DnsName -Type SRV _kpasswd._tcp.example.internal
Resolve-DnsName -Type SRV _ldap._tcp.example.internal

If you cannot change the Windows DNS (corporate policy), you can still make this work by:

Step 4: time sync

Kerberos rejects tickets with clock skew greater than 5 minutes. Point Windows Time at the IPA servers:

w32tm /config /manualpeerlist:"ipa.example.internal,0x1" /syncfromflags:manual /reliable:yes /update
net stop w32time && net start w32time
w32tm /resync
w32tm /query /status

The /query /status output should show Source: ipa.example.internal and a small non-zero Last Successful Sync Time.

Step 5: get a Kerberos ticket

MIT Kerberos for Windows

kinit yourname@EXAMPLE.INTERNAL
klist

Windows SSPI (built-in, via PowerShell)

SSPI gets tickets implicitly when a Windows client process (Edge, RDP, IMAP) tries to authenticate to a service with @EXAMPLE.INTERNAL. To force-check, use klist.exe (shipped with Windows):

klist          # list tickets from the Windows LSA cache
klist purge    # drop everything

WSL2

kinit yourname@EXAMPLE.INTERNAL
klist

Step 6: verify everything works

Four quick checks. Each one catches a different broken prereq.

TLS / CA trust

# Windows side
certutil -verify -urlfetch C:\certs\some-ipa-issued-service.crt
Invoke-WebRequest -Uri https://ipa.example.internal/ipa/ui/ -UseBasicParsing | Select-Object StatusCode

DNS

Resolve-DnsName _kerberos._tcp.example.internal -Type SRV

You should see one row per IPA server.

Kerberos

kinit yourname@EXAMPLE.INTERNAL
klist

klist must show a TGT for krbtgt/EXAMPLE.INTERNAL@EXAMPLE.INTERNAL.

LDAPS bind (from WSL — easiest)

ldapsearch -H ldaps://ipa.example.internal \
  -D 'uid=admin,cn=users,cn=accounts,dc=example,dc=internal' \
  -W -x -b 'cn=users,cn=accounts,dc=example,dc=internal' \
  '(uid=yourname)' uid mail
If any step fails, do not move on. The page that builds on this one (Keycloak on Windows + LDAPS and FreeIPA CLI on Windows) will fail in cascading, confusing ways if even one of CA trust, DNS, time, or Kerberos is broken.

Known limitations