One of the benefits of web APIs is that we can use command line tools to call them. FreeIPA is no different, but perhaps a hair trickier, as it combines the use of Kerberos with a strict JSON format. Getting it right took a little trial and error.
Update 1: The libcurl and curl packages have been changed to make credential delegation deliberate instead of automatic. The command below has been modified from when I originally wrote this article.
Update 2: The HTTP server now defends against Cross Site Request Forgery attacks. The examples now have a new header in them: -H referer:https://$HOSTNAME/ipa
Precondition: run kinit with a valid user name.
Here’s the successful request to list all users:
curl -v -H referer:https://$HOSTNAME/ipa \ -H "Content-Type:application/json" \ -H "Accept:applicaton/json" \ --negotiate -u : \ --cacert /etc/ipa/ca.crt \ -d '{"method":"user_find","params":[[""],{}],"id":0}' \ -X POST \ https://`hostname`/ipa/json |
Lets take this by the numbers.
- curl command, plus the verbose option
- Add a header telling the server we are sending a JSON request
- Add a header telling the server we can accept JSON in the response
- Negotiate authentication. We fake out curl by telling it to use no userid:password in the -u option
- UPDATE: Removed the option to Delegate credentials. No longer required due to S4U2Proxy. Delegating TGTs is a bad idea, but in the past, you had to send your TGT back to the FreeIPA server so it can potentially modify the LDAP database, and it needed to do so as ‘you.’
- I am posting from a CLI on the server machine. This just uses the same ca cert file that the as web server uses. TODO: Figure out how to get this file from a remote machine.
- here is the json request. Note that all quotes must be escaped, as we are sending this from the command line.
- Use the HTTP verb post to the URL specified.
If you want to do this from a remote machine, you can get the ca.cert with:
curl -k https://$YOURHOST/ipa/config/ca.crt > /tmp/ipa.ca.cert |
and modify the portion of the curl command line from:
–cacert /etc/ipa/ca.crt
to
–cacert /tmp/ipa.ca.cert
Update! Now using a file. Notice the @ sign in the -d option
curl -v \ -H referer:https://$HOSTNAME/ipa \ -H "Content-Type:application/json" \ -H "Accept:applicaton/json"\ --negotiate -u : \ --cacert /etc/ipa/ca.crt \ -d @/home/ayoung/sampledata.json \ -X POST https://`hostname`/ipa/json |
For more information about the curl change see this: http://curl.haxx.se/docs/adv_20110623.html
Thanks a lot! Very useful post 🙂
BTW- is there any way to avoid kinit before curl?
I want to make IPA fully automatic in my environment
Use a keytab: http://kb.iu.edu/data/aumh.html
OK, I’ve found a bit better way (keytab won’t work in IPA due to salted passwords).
First, you have to authenticate to IPA and get cookie
local sessid=$(curl -v \
-H referer:https://ipa.mybox/ipa/ui/index.html \
-H “Content-Type:application/x-www-form-urlencoded” \
-H “Accept:*/*” \
–negotiate -u : \
–cacert /etc/ipa/ca.crt \
-d “user=USERNAME” -d “password=PASSWORD” \
-X POST \
https://ipa.mybox/ipa/session/login_password \
2>&1 | grep -o “ipa_session=[a-zA-Z0-9]*”)
then use this cookie to authenticate in cookie version of JSON API:
curl -v \
-H referer:https://ipa.mybox/ipa/ui/index.html \
-H “Content-Type:application/json” \
-H “Accept:applicaton/json” \
–negotiate -u : \
–cacert /etc/ipa/ca.crt \
–cookie “$sessid” \
-d “$postdata” \
-X POST \
https://ipa.mybox/ipa/session/json
I wouldn’t suggest doing that. Much better to do:
echo $PASSWORD | kinit $USERNAME
and then use Curl. I understand why we had to enable Password based authentication in recent versions of IPA, but I wouldn’t recommend using it unless absolutely needed.
Can you expand on “keytab won’t work in IPA due to salted passwords”
Here’s how you do it in python. Note: requires MIT kerberos 1.11 or later if you want to skip doing the kinit, and just let the script do the kinit implicitly with the keytab.
import kerberos
import sys
import os
from requests.auth import AuthBase
import requests
import json
class IPAAuth(AuthBase):
def __init__(self, hostname, keytab):
self.hostname = hostname
self.keytab = keytab
self.token = None
self.refresh_auth()
def __call__(self, request):
if not self.token:
self.refresh_auth()
request.headers[‘Authorization’] = ‘negotiate ‘ + self.token
return request
def refresh_auth(self):
if self.keytab:
os.environ[‘KRB5_CLIENT_KTNAME’] = self.keytab
else:
LOG.warn(‘No IPA client kerberos keytab file given’)
service = “HTTP@” + self.hostname
flags = kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG
try:
(_, vc) = kerberos.authGSSClientInit(service, flags)
except kerberos.GSSError, e:
LOG.error(“caught kerberos exception %r” % e)
raise e
try:
kerberos.authGSSClientStep(vc, “”)
except kerberos.GSSError, e:
LOG.error(“caught kerberos exception %r” % e)
raise e
self.token = kerberos.authGSSClientResponse(vc)
hostname, url, keytab, cacert = sys.argv[1:]
request = requests.Session()
request.auth = IPAAuth(hostname, keytab)
ipaurl = ‘https://%s/ipa’ % hostname
jsonurl = url % {‘hostname’: hostname}
request.headers.update({‘Content-Type’: ‘application/json’,
‘Referer’: ipaurl})
request.verify = cacert
myargs = {‘method’: ‘dnsrecord_add’,
‘params’: [[“testdomain.com”, “test4.testdomain.com”],
{‘a_part_ip_address’: ‘172.31.11.4’}],
‘id’: 0}
resp = request.post(jsonurl, data=json.dumps(myargs))
print resp.json()
myargs = {‘method’: ‘dnsrecord_find’, ‘params’: [[“testdomain.com”], {}], ‘id’: 0}
resp = request.post(jsonurl, data=json.dumps(myargs))
print resp.json()
Run the script like this:
python script.py ipahost.domain.tld ‘https://%(hostname)s/ipa/json’ myuser.keytab /etc/ipa/ca.crt
@uosiu i used ur sollution concerning adding the cookie, but it gives me 401 when i try to send a curl request using the cookie: Unable to verify your Kerberos credentials …)
any help would be apreciated wella 😉
Thank for such a good post.I am facing issue when i try to call the rest api it says 500 internal server error.As per the log s it is not able to get the credentials for HTTP principal even though i am iniitalizing ticket using the keytab.Can anyone please help?
The 500 means the server itself is misconfigured (obviously) so I suspect that either the keytab is in the wrong place, permissions, wrong principal in it, or some other mistconfiguration. Check SELinux, too.