Windows 11 + FreeIPA (no AD)
- 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
- Windows 11 with local admin rights on the host.
- A reachable FreeIPA server (call it
ipa.example.internal, realmEXAMPLE.INTERNAL). - The IPA CA certificate. On the IPA server:
/etc/ipa/ca.crt. Copy it to the Windows host (SCP via PuTTY, USB, whatever your environment allows) asC:\certs\ipa-ca.crt. - A FreeIPA user you can
kinitas — ordinary user is fine for verification, admin for anyipawrites. - Optional: MIT Kerberos for Windows if you want native Windows
kinit.exeoutside of WSL.
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.
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
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:
- Setting
dns_lookup_kdc = falseinkrb5.iniand keeping the explicitkdc =line (already above). - Adding a
hostsentry for the IPA server(s) atC:\Windows\System32\drivers\etc\hostsso the FQDN always resolves to the right IP.
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
Known limitations
- No SSSD on Windows. You cannot log into Windows with IPA credentials via this path. Use the experimental ipa-client-windows or a real AD trust if you need that.
- HBAC is not enforced on Windows. It is enforced by services (e.g. SSSD-on-Linux) that look it up. A Windows box can hold a valid Kerberos ticket for a user who is HBAC-blocked from actual target hosts; that is by design.
- Group membership is service-scoped. Your local Windows groups are not IPA groups. Applications that read
memberOfover LDAP (Keycloak, web apps) see the real IPA group; Windowswhoami /groupsdoes not. - Password change.
kpasswdworks against IPA but Windows will not know. Change via the IPA Web UI or WSL. - IPA CA renewal. When the IPA CA rotates you must re-import the new CA into every trust store above. Put a reminder in your IPA calendar (
ipa ca-show ipa | grep -i "Valid Until").
Related pages
- FreeIPA foundations — users, hosts, services, Kerberos on the Linux side.
- FreeIPA CLI on Windows — run
ipacommands from this host. - Keycloak on Windows + LDAPS — wire a Keycloak instance on this host to FreeIPA.
- Kerberos — tickets, TGTs, common errors.
- Certificates — the bigger picture of private keys, CSRs, and CAs.