Hi IPA Users,
I have a custom PHP script on the same Apache HTTPD server as used by IPA and the script attempts to make a request to the IPA Server's JSON endpoint using PHP's libcurl and a custom service principal. However, the request is coming across as the IPA HTTP service principal, not my custom principal (and therefore the permissions are wrong). If I run curl from the command line it works as expected. In fact, I believe this was working before and now isn't after I upgraded to IPA 4.5.4. How do I get PHP's libcurl to use my custom service principal instead of the HTTP service principal installed by IPA?
This works:
kinit myservice/ipaserver.example.com -k -t /etc/myservice.keytab
/usr/bin/curl -v -H referer:https://ipaserver.example.com/ipa -H "Content-Type:application/json" -H "Accept:applicaton/json" --negotiate -u : --cacert /etc/ipa/ca.crt -d '{"method":"user_mod/1","params":[["testuser"],{"userpassword": "testpassword", "version": "2.228"}],"id":0}' -X POST https://ipaserver.example.com/ipa/json
And as expected the verbose response includes the attribute: "principal": "myservice/ipaserver.example.com@EXAMPLE.COM"
Now here is what the PHP script function for the same request looks like:
<?php function web_request($body) { $krbcache = tmpfile();
$KRB5CCPATH = stream_get_meta_data($krbcache)['uri']; $IPAHOSTNAME = "ipaserver.example.com"; $ref = "https://" . $IPAHOSTNAME . "/ipa"; $url = "https://" . $IPAHOSTNAME . "/ipa/json";
putenv("KRB5CCNAME=FILE:/$KRB5CCPATH"); putenv("IPAHOSTNAME=$IPAHOSTNAME"); putenv("KRB5_CLIENT_KTNAME=/etc/myservice.keytab"); putenv("KRB5_KTNAME=/etc/myservice.keytab");
$command = "kinit myservice/ipaserver.example.com -k -t /etc/myservice.keytab"; shell_exec($command);
$ch = curl_init($url);
$headers = array("Expect:", "Content-Type:application/json", "Accept:application/json", "referer: " . $ref);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE); curl_setopt($ch, CURLOPT_CAINFO, "/etc/ipa/ca.crt"); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_USERNAME, ":");
#DEBUG OPTS curl_setopt($ch, CURLINFO_HEADER_OUT, true); curl_setopt($ch, CURLOPT_VERBOSE, true);
$result = curl_exec($ch);
#START DEBUG LOGGING $info = curl_getinfo($ch); foreach($info as $key => $value) { if(!is_array($value)) { error_log($key . ': ' . $value); } } error_log('Request Body: ' . $body); error_log('Response: ' . $result); # END DEBUG LOGGING
if(curl_errno($ch)) { throw new Exception("Could not send CURL request: " . curl_error($ch)); }
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($status !== 200) { throw new Exception("Unable to authenticate to server: HTTP Return code: " . $status); }
curl_close($ch);
fclose($krbcache);
return $result; }
?>
This fails with a permissions error and I assume it has something to do with the verbose output indicating the wrong credential cache was used: "principal": "HTTP/ipaserver.example.com@EXAMPLE.COM"
Any tips?
Thanks,
Ryan
Further investigation suggests this might have something to do with gssproxy. I was expecting to find the HTTP keytab at /etc/httpd/conf/ipa.keytab, but now see it is in /var/lib/ipa/gssproxy. This problem only occurs if the PHP script is executed by the apache user in the context of the HTTPD web server. Executing the PHP script directly such as "sudo -u apache php test.php" works as expected (the myservice principal is used). Anyone know why apache user in HTTPD context goes with HTTP service principal despite the script executing kinit with a different principal and setting environment variables to try to use the alternative principal?
On Thu, 2018-07-12 at 12:02 +0000, Ryan Slominski via FreeIPA-users wrote:
Further investigation suggests this might have something to do with gssproxy. I was expecting to find the HTTP keytab at /etc/httpd/conf/ipa.keytab, but now see it is in /var/lib/ipa/gssproxy. This problem only occurs if the PHP script is executed by the apache user in the context of the HTTPD web server. Executing the PHP script directly such as "sudo -u apache php test.php" works as expected (the myservice principal is used). Anyone know why apache user in HTTPD context goes with HTTP service principal despite the script executing kinit with a different principal and setting environment variables to try to use the alternative principal?
You really shouldn't kinit as the Apache user on an IPA system, unless you fork/exec and explicitly set KRB5CCNAME so it does not conflict (and overwrites) the ccaches used by Apache.
In that case you may also want to disable the GSS_USE_PROXY env var in your subprocess so that gssproxy is not invoked at all.
But in general, do not mess with kerberos and the apache user on an IPA server is the takeaway.
Simo.
Thanks Simo,
I've got this working now using PHP's shell_exec and a bash script that invokes curl directly (as opposed to using libcurl in PHP). This allows me to clear the environment (unset GSS_USE_PROXY).
Here is the final solution for reference:
PHP script now looks like:
<?php function user_show($username) { $body = "{\"method\":\"user_show/1\",\"params\":[[" . json_encode($username) . "],{\"version\": \"2.228\"}],\"id\":0}"; $json = web_request($body); return $json; } function web_request($body) { $body = escapeshellarg($body); $command = "/opt/scripts/request.sh $body"; $result = shell_exec($command); $json = json_decode($result, true); if(is_null($json)) { throw new Exception("Request Error: " . $result); } if(!is_null($json['error'])) { $msg = $json['error']['message']; throw new Exception("Request Error: " . $msg); } return $json; } ?>
Shell script request.sh:
#!/bin/sh body=$1 tmpfile=$(mktemp) # Remove GSSPROXY export -n GSS_USE_PROXY export KRB5CCNAME=FILE:/$tmpfile kinit myservice/ipaserver.example.com -k -t /etc/myservice.keytab /usr/bin/curl -s -H referer:https://ipaserver.example.com/ipa -H "Content-Type:application/json" -H "Accept:applicaton/json" --negotiate -u : --cacert /etc/ipa/ca.crt -d "$body" -X POST https://ipaserver.example.com/ipa/json rm "$tmpfile"
freeipa-users@lists.fedorahosted.org