Trouble with fetch-tool and cookie-handling..

We are trying to make API-calls to FreeIPAs / (Red Hats IdM) via the fetch tool. The procedure is done in two parts, one call to authenticate and get a cookie, and the other call is to run a api-call to update i.e. dns-records in our case. Below is a working example using curl, using headers to pass the cookie information (curl has a built-in cookie feature, that the fetch tool don’t have):

First we authenticate:

curl -s -k -i \
        -H "Accept: text/plain" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -H "Referer: https://IPA-SERVER.DNS.NAME/ipa" \
        -H "X-IPA-API-Version 2.247" \
        --data-urlencode "user=IPA_API_USER" \
        --data-urlencode "password=IPA_API_PASSWORD" \
        "https://IPA-SERVER.DNS.NAME/ipa/session/login_password"

HTTP/1.1 200 Success
Date: Wed, 12 Feb 2025 11:11:54 GMT
Server: Apache/2.4.62 (Rocky Linux) OpenSSL/3.2.2 mod_auth_gssapi/1.6.3 mod_wsgi/4.7.1 Python/3.9
Set-Cookie: ipa_session=MagBearerToken=f0DSZeog3u%2f0XNiT2eDtCRO2gWMwj4cDLTAM00AIeBzDubBbkguD4lcU7ngxnxP0g%2fQDOvCryNLWjpPKp2gmNnGbTV0qKnY%2ftB0G%2bC9XqIqYiol7wkO72%2fn0fCKhniCEwdLaGBREvrzBIXAHsVzdbHdA%2bdnKGRfoHOS%2b6NpB%2bJlM8%2bZbpsleF4Pm6hr7BoXt9DA%2bW1XIMe4bVhUn5m3%2fOh9mLOUN33tXhur3GHSgYscyhDvGGv0cyThAbJOHRs%2bxUZ5pc5iuTrUDtKiOD4uFGSlOIrv%2bEAIR4%2bLA9PvDbUk%3d;path=/ipa;httponly;secure;
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Cache-Control: no-cache, private
Vary: Accept-Encoding
Transfer-Encoding: chunked
Content-Type: text/plain; charset=UTF-8

Then we send json-data to update the record:

curl -s -k -i \
        -H "Accept: application/json" \
        -H "Content-Type: application/json" \
        -H "Referer: https://IPA-SERVER.DNS.NAME/ipa" \
        -H "X-IPA-API-Version: 2.247" \
        -H "Cookie: ipa_session=MagBearerToken=f0DSZeog3u%2f0XNiT2eDtCRO2gWMwj4cDLTAM00AIeBzDubBbkguD4lcU7ngxnxP0g%2fQDOvCryNLWjpPKp2gmNnGbTV0qKnY%2ftB0G%2bC9XqIqYiol7wkO72%2fn0fCKhniCEwdLaGBREvrzBIXAHsVzdbHdA%2bdnKGRfoHOS%2b6NpB%2bJlM8%2bZbpsleF4Pm6hr7BoXt9DA%2bW1XIMe4bVhUn5m3%2fOh9mLOUN33tXhur3GHSgYscyhDvGGv0cyThAbJOHRs%2bxUZ5pc5iuTrUDtKiOD4uFGSlOIrv%2bEAIR4%2bLA9PvDbUk%3d;path=/ipa;httponly;secure;" \
        --data "{\"method\":\"dnsrecord_mod\",\"params\":[[\"ZONE_NAME\",\"RECORD_NAME\"],{\"arecord\":\"1.1.1.1\", \"dnsttl\": 300}]}" \
        "https://IPA-SERVER.DNS.NAME/ipa/session/json" 


HTTP/1.1 200 Success
Date: Wed, 12 Feb 2025 11:12:43 GMT
Server: Apache/2.4.62 (Rocky Linux) OpenSSL/3.2.2 mod_auth_gssapi/1.6.3 mod_wsgi/4.7.1 Python/3.9
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Cache-Control: no-cache, private
Vary: Accept-Encoding
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8

{"result": {"result": {"arecord": ["1.1.1.1"], "dnsttl": ["300"], "idnsname": ["test"]}, "value": "test", "messages": [{"type": "warning", "name": "VersionMissing", "message": "API Version number was not sent, forward compatibility not guaranteed. Assuming server's API version, 2.254", "code": 13001, "data": {"server_version": "2.254"}}], "summary": null}, "error": null, "id": null, "principal": "IPA_API_USER@IPA_DOMAIN", "version": "4.12.2"}

The problem with the fetch tool arises when we want to extract the cookie-information from the headers, what I want is output like this.

/tool fetch url="https://IPA-SERVER.DNS.NAME/ipa/ui/" output=user-with-headers

        status: finished
    downloaded: 2KiB
      duration: 1s
          data: <!DOCTYPE html>, i.e
  http-headers: Accept-Ranges: bytes
		Cache-Control: no-cache, private, max-age=0
                Content-Encoding: gzip
		Content-Length: 741
                Content-Security-Policy: frame-ancestors 'none'
                Content-Type: text/html; charset=UTF-8
                Date: Wed, 12 Feb 2025 10:43:11 GMT
                Expires: Wed, 12 Feb 2025 10:43:11 GMT
                Last-Modified: Wed, 21 Aug 2024 15:06:37 GMT
                Server: Apache/2.4.62 (Rocky Linux) OpenSSL/3.2.2 mod_auth_gssapi/1.6.3 mod_wsgi/4.7.1 Python/3.9
                Vary: Accept-Encoding
                X-Frame-Options: DENY

Here is another example with a random Norwegian newspaper, but we can see the cookie-information from the header-output:

/tool fetch url="https://www.vg.no" output=user-with-headers
     
        status: finished
    downloaded: 20KiB
      duration: 1s
          data: <!DOCTYPE html>, i.e
  http-headers: accept-ch: sec-ch-ua-model,sec-ch-ua-platform-version
                Accept-Ranges: bytes
                Age: 0
                Cache-Control: max-age=0, must-revalidate, public
                Connection: keep-alive
                Content-Encoding: gzip
                Content-Security-Policy: frame-ancestors https://admarket.no https://admarket.schibsted.se https://schibsted.dredition.aptoma.no/; upgrade-insecure-requests
                Content-Type: text/html; charset=UTF-8
                Date: Wed, 12 Feb 2025 11:44:46 GMT
                ETag: W/"d456341b09be46d77967e93f95ba953d-br"
                Link: <https://cmp.vg.no/unified/wrapperMessagingWithoutDetection.js>; rel=preload; as=script,... i.e
                Permissions-Policy: unload=(), fullscreen=(self),ch-ua-model=*,ch-ua-platform-version=*
                Set-Cookie: clientBucket=77; Expires=Wed, 26 Feb 2025 11:44:46 GMT; Domain=vg.no; Path=/; Secure; samesite=strict
                strict-transport-security: max-age=15552000
                Transfer-Encoding: chunked
                Vary: Accept-Encoding,User-Agent
                x-age: 103
                x-cache: HIT:2888
                x-clue: aHR0cHM6Ly9kaXNjb3JkLmdnL3Znbm8gb2cgc2kgJ2hlYWRlcm5lc2UnCg==
                X-Content-Type-Options: nosniff
                x-varnish-director: vg_frimand_udo
                X-VG-TLSProxy: u89-tlsproxy-01.int.vgnett.no
                X-VG-WebCache: oa68-varnish-02
                X-XSS-Protection: 1

But when I try to do fetch tool-authentication, I receive no headers back:

/tool fetch url="https://IPA-SERVER.DNS.NAME/ipa/session/login_password" \
        mode=https \
        http-method=post \
        http-header-field="Accept: text/plain,\
        Content-Type: application/x-www-form-urlencoded,\
        Referer: https://IPA-SERVER.DNS.NAME/ipa,\
        X-IPA-API-Version: 2.247" \
        http-data="user=IPA_API_USER&password=IPA_API_PASSWORD" \
        output=user-with-headers

        status: finished
    downloaded: 0KiB    
          data:         
  http-headers:

The best clue I have is that with curl we cannot use “–head” to view the headers, we have to use “–include”, some internet comments explain that http-method post and head, cannot be combined.

I find one other forum-post that seems to do the same thing we are attempting:

http://forum.mikrotik.com/t/cookie-based-auth-using-fetch/172277/1

But he explicitly explains that with output=user-with-headers, he sees the header-information.

Any ideas/tips, anyone?

I can also confirm that passing the right cookie-information (for example information obtained via curl on another computer), can be passed via the fetch-tool:

/tool fetch url="https://IPA-SERVER.DNS.NAME/ipa/session/json" \
    http-method=post \
    http-header-field="Accept: application/json,Content-Type: application/json,\
    Referer: https://IPA-SERVER.DNS.NAME/ipa,\
    X-IPA-API-Version: 2.247,\
    Cookie: ipa_session=MagBearerToken=f0DSZeog3u%2f0XNiT2eDtCRO2gWMwj4cDLTAM00AIeBzDubBbkguD4lcU7ngxnxP0g%2fQDOvCryNLWjpPKp2gmNnGbTV0qKnY%2ftB0G%2bC9XqIqYiol7wkO72%2fn0fCKhniCEwdLaGBREvrzBIXAHsVzdbHdA%2bdnKGRfoHOS%2b6NpB%2bJlM8%2bZbpsleF4Pm6hr7BoXt9DA%2bW1XIMe4bVhUn5m3%2fOh9mLOUN33tXhur3GHSgYscyhDvGGv0cyThAbJOHRs%2bxUZ5pc5iuTrUDtKiOD4uFGSlOIrv%2bEAIR4%2bLA9PvDbUk%3d;path=/ipa;httponly;secure;" \
    http-data="{\"method\":\"dnsrecord_mod\",\"params\":[[\"ZONE_NAME\",\"RECORD_NAME\"],{\"arecord\":\"192.168.1.100\",\"dnsttl\":3600}]}" \
    keep-result=no 

      status: finished
  downloaded: 0KiB    
    duration: 1s

This indeed updates the DNS-record in freeIPA, but we need to be able to obtain the cookie-information in the first place via the fetch-tool.

If anyone feels like testing, there are public demo servers available:

server: ipa.demo1.freeipa.org
user: admin
password: Secret123

curl-result:

$ curl -s -k -i -H "Accept: text/plain" -H "Content-Type: application/x-www-form-urlencoded" -H "Referer: https://ipa.demo1.freeipa.org/ipa" -H "X-IPA-API-Version 2.247" --data-urlencode "user=admin"  --data-urlencode "password=Secret123" "https://ipa.demo1.freeipa.org/ipa/session/login_password"

HTTP/1.1 200 Success
Date: Wed, 12 Feb 2025 16:34:47 GMT
Server: Apache/2.4.59 (Fedora Linux) OpenSSL/3.1.1 mod_wsgi/4.9.4 Python/3.12 mod_auth_gssapi/1.6.5
Set-Cookie: ipa_session=MagBearerToken=FoB%2bb8yOi0lOFD%2bthlabCFhWZgDOt3juLRqHB%2byp0rn%2flOmNxBqQH761Z59nHIxZSThN5oDzdEyI%2f5gCF89sW8zDYjjOL67TEFJyvFWefsISxKDdF%2f239CvA%2fR0SRnBrbKUx1U3ReYDLsV%2fxqAHxdffSXQnR2oKJSkJMAV4VzSmT5NumbggpQMFN8WKRIV1YlxcyjQpfDhmK2T%2f%2f474WVuq9f39uc0%2fT2cPvtrV6qhw%3d;path=/ipa;httponly;secure;
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Cache-Control: no-cache, private
Vary: Accept-Encoding
Transfer-Encoding: chunked
Content-Type: text/plain; charset=UTF-8

fetch-result:

/tool fetch url="https://ipa.demo1.freeipa.org/ipa/session/login_password" \
        mode=https \
        http-method=post \
        http-header-field="Accept: text/plain,\
        Content-Type: application/x-www-form-urlencoded,\
        Referer: https://ipa.demo1.freeipa.org/ipa,\
        X-IPA-API-Version: 2.247" \
        http-data="user=admin&password=Secret123" \
        output=user-with-headers

        status: finished
    downloaded: 0KiB    
          data:         
  http-headers:

My main hypothesis is that the fetch tool won’t provide any headers if no data is downloaded from the request.

I will see if I can test that hypothesis the next couple of days.

A simple test seems to contradict my initial hypothesis, at least when I use a simple GET-method:

I set up a basic HTTP-server with caddy, like this that listens to port 80, and only responds with 200 and a “Set-Cookie”-header:

:80 {
        handle {
                header Set-Cookie "cookie-test"
                respond 200
        }
}

Curl output:

curl http://172.17.10.66 -i
HTTP/1.1 200 OK
Server: Caddy
Set-Cookie: cookie-test
Date: Sun, 16 Feb 2025 07:26:11 GMT
Content-Length: 0

Fetch output:

/tool fetch http://172.17.10.66 output=user-with-headers 
        status: finished                           
    downloaded: 0KiB                               
      duration: 0s                                 
          data:                                    
  http-headers: Content-Length: 0                  
                Date: Sun, 16 Feb 2025 07:20:43 GMT
                Server: Caddy                      
                Set-Cookie: cookie-test

But also when I change method to post:

/tool fetch http-method=post http://172.17.10.66 output=user-with-headers 
        status: finished                           
    downloaded: 0KiB                               
      duration: 0s                                 
          data:                                    
  http-headers: Content-Length: 0                  
                Date: Sun, 16 Feb 2025 07:30:00 GMT
                Server: Caddy                      
                Set-Cookie: cookie-test

Actually if I run the exact same command as I do against the public-demo ipa-server (see my example above), but against my caddy server instead, I do get a output from fetch:

/tool fetch url="http://172.17.10.66" \
        http-method=post \
        http-header-field="Accept: text/plain,\
        Content-Type: application/x-www-form-urlencoded,\
        Referer: https://ipa.demo1.freeipa.org/ipa,\
        X-IPA-API-Version: 2.247" \
        http-data="user=admin&password=Secret123" \
        output=user-with-headers
           status: finished                           
    downloaded: 0KiB                               
      duration: 0s                                 
          data:                                    
  http-headers: Content-Length: 0                  
                Date: Sun, 16 Feb 2025 07:42:51 GMT
                Server: Caddy                      
                Set-Cookie: cookie-test

Any more ideas or inputs are greatly appreciated.

A basic fetch against the basic ipa-url (of the public demo-servers), without trying to authenticate, gives the following output:


/tool fetch url="https://ipa.demo1.freeipa.org/ipa/ui/" output=user-with-headers 
        status: finished 
    downloaded: 2KiB
         total: 0KiB
      duration: 1s 
          data: <!DOCTYPE html>n<html>n<head>n    <meta charset="utf-8">n    <title>Identity Management</title>nn    <!--[if IE]>n    <meta id="ie-detector">n    <![endif]-->nn    <meta name="viewport" content="width=device-width, initial-scale=1.0">n    <scri>
  http-headers: Accept-Ranges: bytes
                Cache-Control: no-cache, private, max-age=0
                Content-Encoding: gzip
                Content-Length: 741
                Content-Security-Policy: frame-ancestors 'none'
                Content-Type: text/html; charset=UTF-8
                Date: Sun, 16 Feb 2025 07:50:56 GMT
                Expires: Sun, 16 Feb 2025 07:50:56 GMT
                Last-Modified: Wed, 10 Jan 2024 09:47:22 GMT
                Server: Apache/2.4.59 (Fedora Linux) OpenSSL/3.1.1 mod_wsgi/4.9.4 Python/3.12 mod_auth_gssapi/1.6.5
                Vary: Accept-Encoding
                X-Frame-Options: DENY