Examples accessing WAPI using Curl

The following sections demonstrate how to interact with WAPI through curl ( see http://curl.haxx.se/ for more information). This sample code shows you how to create an object, modify it, search for it, and delete it. The sample code uses the network object and assumes that no other networks exist on the appliance.

Use -k1 in curl to allow connections even if the appliance SSL certificate is not signed by a recognized SSL authority and to force TLS negotiation. If you want to capture the actual traffic, use the –trace or –trace-ascii options to invoke curl.

These tests assume that the appliance ip is 192.168.1.2, and that you have a valid user name of ‘admin’ and a password of ‘testpw’.

\ at the end of the line means the line was wrapped for documentation purposes but should be joined with the previous line(s) when entering the command in your shell.

Note that some shells can interact with quote characters inside the requests. In case of a failure, consider using the curl -v and –trace-ascii options to inspect what has been sent to the server to ensure that your shell did not affect the requested data.

Standard sample code

Create a network

To create networks, use a POST request:

curl -k1 -u admin:testpw -X POST https://192.168.1.2/wapi/v2.12.3/network \
    -d network=10.1.0.0/16

The server returns a reference of the created network:

"network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16"

To create another network, send another POST request:

curl -k1 -u admin:testpw -X POST https://192.168.1.2/wapi/v2.12.3/network \
    -d network=10.2.0.0/16

Read a network

To verify that both networks have been created, send a GET request:

curl -k1 -u admin:testpw -X GET https://192.168.1.2/wapi/v2.12.3/network

The server returns a list with both networks:

[
    {
        "_ref": "network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16",
        "network": "10.1.0.0/16",
        "network_view": "default"
    },
    {
        "_ref": "network/ZG5zLm5ldHdvcmskMTAuMi4wLjAvMTYvMA:10.2.0.0%2F16",
        "network": "10.2.0.0/16",
        "network_view": "default"
    }
]

Note that the returned references could be different in your installation. The sample code uses references returned in the above example. Depending on your installation, make sure that you use the references your server returns.

Modify a network

To modify a network, send a PUT request. Send the following to modify its comment:

curl -k1 -u admin:testpw -X PUT \
    https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:\
    10.1.0.0%2F16 -d comment='Sample comment'

The server still returns the network reference. Note that this could be different from before:

"network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16"

Check that the network was modified, since comment is not a field that is returned by default add _return_fields to the GET request:

curl -k1 -u admin:testpw -X GET https://192.168.1.2/wapi/v2.12.3/network \
    -d _return_fields=network,network_view,comment

Note that the 10.1.0.0/16 network has been modified:

[
    {
        "_ref": "network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16",
        "comment": "Sample comment",
        "network": "10.1.0.0/16",
        "network_view": "default"
    },
    {
        "_ref": "network/ZG5zLm5ldHdvcmskMTAuMi4wLjAvMTYvMA:10.2.0.0%2F16",
        "network": "10.2.0.0/16",
        "network_view": "default"
    }
]

Search for a network

To find networks with comments that contain the word sample in a case-insensitive way:

curl -k1 -u admin:testpw -X GET https://192.168.1.2/wapi/v2.12.3/network \
    -d comment~:=sample

The server returns the network we just modified:

[
    {
        "_ref": "network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16",
        "comment": "Sample comment",
        "network": "10.1.0.0/16",
        "network_view": "default"
    }
]

If there is no match, the server returns an empty list:

curl -k1 -u admin:testpw -X GET https://192.168.1.2/wapi/v2.12.3/network \
    -d comment~:=nomatch

The server returns the following:

[]

Delete a network

To delete a network, send a DELETE request using a reference you have retrieved by searching. For example, to delete the networks we created above, send the following:

curl -k1 -u admin:testpw -X DELETE \
    https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:\
    10.1.0.0%2F16

The server returns the reference of the object it just deleted, if the deletion was successful:

"network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16"

To delete the other network, send the following:

curl -k1 -u admin:testpw -X DELETE \
    https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvcmskMTAuMi4wLjAvMTYvMA:\
    10.2.0.0%2F16

Note that both networks have been removed:

curl -k1 -u admin:testpw -X GET https://192.168.1.2/wapi/v2.12.3/network

The server returns the following:

[]

Create a host record

To create a host record in a specified zone, first send the following request to create the zone:

curl -k1 -u admin:testpw -H "Content-Type: application/json" \
-X POST https://192.168.1.2/wapi/v2.12.3/zone_auth \
-d '{"fqdn": "zone.com"}'

Then send the following request to create the host:

curl -k1 -u admin:testpw -H "Content-Type: application/json" \
-X POST https://192.168.1.2/wapi/v2.12.3/record:host -d \
'{"ipv4addrs":[{"ipv4addr":"10.222.0.12"}],"name":"host.zone.com"}'

Note that it might be necessary to specify the content type explicitly when using the -d option in curl.

Schedule an object creation.

To schedule an object creation, use a POST request with the _schedinfo.scheduled_time parameter:

curl -k1 -u admin:testpw -X POST https://192.168.1.2/wapi/v2.12.3/network \
    -d network=10.1.0.0/16 -d _schedinfo.scheduled_time=1367752903

The server returns a reference of the created scheduled task:

"scheduledtask/b25lLnF1ZXVlZF90YXNrJDY:6/PENDING"

Execute a function call.

To execute a function call, use a POST request with the _function parameter. For example, first create a network:

curl -k1 -u admin:testpw -X POST https://192.168.1.2/wapi/v2.12.3/network \
    -d network=10.1.0.0/16

the server will then return a reference to the network that was just created:

"network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0/16/default"

now use this reference to retrieve the next three available /24 networks in this network excluding 10.1.1.0/24 and 10.1.3.0/24:

curl -k1 -u admin:testpw -X POST \
https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0\
/16/default?_function=next_available_network -H "Content-Type: application/json" \
-d '{"exclude": ["10.1.1.0/24", "10.1.3.0/24"], "cidr": 24, "num": 3}'

The server returns a list of available networks with the above constraints:

{
    "networks": [
            "10.1.0.0/24",
            "10.1.2.0/24",
            "10.1.4.0/24"
        ]
}

Uploading a file to the appliance

To upload a file to the appliance, first tell the appliance so:

curl -k1 -u admin:testpw -X POST \
'https://192.168.1.2/wapi/v2.12.3/fileop?_function=uploadinit'

The appliance will return the URL and a token value:

{
    "token": "eJydkMFOwzAMhu9+k......",
    "url": "https://192.168.1.2/...."
}

The file can then be uploaded to the specified URL:

curl -k1 -u admin:testpw -F name=somefile.txt -F filedata=@somefile.txt \
'https://192.168.1.2/...'

Finally, we need to signal to the appliance that the upload has been completed and that it needs to perform the requested action on the uploaded file. In this example, we will use setfiledest:

curl -k1 -u admin:testpw -X POST \
'https://192.168.1.2/wapi/v2.12.3/fileop?_function=setfiledest' \
-H "Content-Type: application/json" \
-d '{ "dest_path": "/somefile.txt", "type": "TFTP_FILE", '
 '"token": "eJydkMFOwzAMhu9+k..." }'

Downloading a file from the appliance

To download a file from the appliance, first select what to download. In this example, we will download a backup:

curl -k1 -u admin:testpw -X POST \
'https://192.168.1.2/wapi/v2.12.3/fileop?_function=getgriddata' \
-H "Content-Type: application/json" -d '{"type": "BACKUP"}'

The appliance will return a token and a URL from which the file should be downloaded:

{
    "token": "eJydUMtuwyAQvO....",
    "url": "https://192.168.1.2/...."
}

We can then download the file:

curl -k1 -u admin:testpw -H "Content-type:application/force-download" -O \
"https://192.168.1.2/...."

After the download has been completed, we can signal to the appliance that the operation is done by calling downloadcomplete and passing the token we have retrieved in the first step:

curl -k1 -u admin:testpw -X POST \
'https://192.168.1.2/wapi/v2.12.3/fileop?_function=downloadcomplete' \
-H "Content-Type: application/json" -d '{ "token": "eJydUMtuwyAQvO+...."}'

Executing a paging request

First insert a zone and some A records:

curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/zone_auth \
-d fqdn=test1.com
curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/network \
-d network=10.1.0.0/16
curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/record:a \
-d ipv4addr=10.1.0.1 -d name=a1.test1.com
curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/record:a \
-d ipv4addr=10.1.0.2 -d name=a2.test1.com
curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/record:a \
-d ipv4addr=10.1.0.3 -d name=a3.test1.com
curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/record:a \
-d ipv4addr=10.1.0.4 -d name=a4.test1.com
curl -k1 -u admin:testpw -X POST https://127.0.0.1/wapi/v2.12.3/record:a \
-d ipv4addr=10.1.0.5 -d name=a5.test1.com

Then check that all records have been inserted correctly:

curl -k1 -u admin:testpw -X GET \
'https://127.0.0.1/wapi/v2.12.3/record:a?name~=test1.com&_return_fields=name'
[
    {
        "_ref": "record:a/ZG5zLmJpbmRfYSQuXY29tLn3DEwLjEuMC4x:a1.test1.com/default",
        "name": "a1.test1.com"
    },
    {
        "_ref": "record:a/ZG5zLmJpbmRLnRlc3QxLGE1LDEwLjEuMC41:a5.test1.com/default",
        "name": "a5.test1.com"
    },
    {
        "_ref": "record:a/ZG5zLmJpbmRfYSQc3QxLGE0LDEwLjEuMC40:a4.test1.com/default",
        "name": "a4.test1.com"
    },
    {
        "_ref": "record:a/ZG5zLmuY29tLnRlc3QxLGEzLDEwLjEuMC4z:a3.test1.com/default",
        "name": "a3.test1.com"
    },
    {
        "_ref": "record:a/ZG5zLmJpbmRfYSQuX2RLGEyLDEwLjEuMC4y:a2.test1.com/default",
        "name": "a2.test1.com"
    }
]

Now request two records at a time:

curl -k1 -u admin:testpw -X GET \
'https://127.0.0.1/wapi/v2.12.3/record:a?\
name~=test1.com&_return_fields=name&_paging=1&_max_results=2&_return_as_object=1'
{
    "next_page_id": "789c5590...4efc1732",
    "result": [
        {
            "_ref": "record:a/ZG5zLmJpbmRfYSQuXY29tLn3DEwLjEuMC4x:a1.test1.com/default",
            "name": "a1.test1.com"
        },
        {
            "_ref": "record:a/ZG5zLmJpbmRLnRlc3QxLGE1LDEwLjEuMC41:a5.test1.com/default",
            "name": "a5.test1.com"
        }
    ]
}

The server has returned the first page of results and a next_page_id to be used for the next page request. Note that the actual next_page_id will not contain periods (.). The periods are used here to shorten the actual ID:

curl -k1 -u admin:testpw -X GET \
'https://127.0.0.1/wapi/v2.12.3/record:a?_page_id=789c5590...4efc1732'
{
    "next_page_id": "789c5590...3e113c3d4d",
    "result": [
        {
            "_ref": "record:a/ZG5zLmJpbmRfYSQc3QxLGE0LDEwLjEuMC40:a4.test1.com/default",
            "name": "a4.test1.com"
        },
        {
            "_ref": "record:a/ZG5zLmuY29tLnRlc3QxLGEzLDEwLjEuMC4z:a3.test1.com/default",
            "name": "a3.test1.com"
        }
    ]
}

Let’s now fetch the last page of results using the page_id that was just returned:

curl -k1 -u admin:testpw -X GET \
'https://127.0.0.1/wapi/v2.12.3/record:a?_page_id=789c5590...3e113c3d4d'
{
    "result": [
        {
            "_ref": "record:a/ZG5zLmJpbmRfYSQuX2RLGEyLDEwLjEuMC4y:a2.test1.com/default",
            "name": "a2.test1.com"
        }
    ]
}

Note that the server has not returned a next_page_id because this was the last page of results.

XML Sample code

Create a network

To create networks, use a POST request:

curl -k1 -u admin:testpw -X POST -HContent-Type:text/xml --data-binary \
    '<value type="object"><network>10.1.0.0/16</network></value>' \
    https://192.168.1.2/wapi/v2.12.3/network?_return_type=xml-pretty

The server returns a reference of the created network:

<?xml version="1.0"?>
<value>network/ZG5zLm5ldHdvMS4wLjAvMTYvMA:10.1.0.0%2F16</value>

To create another network, send another POST request:

curl -k1 -u admin:testpw -X POST -HAccept:text/xml -HContent-Type:text/xml \
    --data-binary '<value type="object"><network>10.2.0.0/16</network></value>' \
    https://192.168.1.2/wapi/v2.12.3/network?_return_type=xml-pretty

Read a network

To verify that both networks have been created, send a GET request:

curl -k1 -u admin:testpw -X GET \
https://192.168.1.2/wapi/v2.12.3/network?_return_type=xml-pretty

The server returns a list with both networks:

<?xml version="1.0"?>
<list>
  <value type="object">
    <network_view>default</network_view>
    <_ref>network/ZG5zLm5ldHdvMS4wLjAvMTYvMA:10.1.0.0%2F16</_ref>
    <network>10.1.0.0/16</network>
  </value>
  <value type="object">
    <network_view>default</network_view>
    <_ref>network/ZG5zLm5ldHduMi4wLjAvMTYvMA:10.2.0.0%2F16</_ref>
    <network>10.2.0.0/16</network>
  </value>
</list>

Note that the returned references could be different in your installation. The sample code uses references returned in the above example. Depending on your installation, make sure that you use the references your server returns.

Modify a network

To modify a network, send a PUT request. Send the following to modify its comment:

curl -k1 -u admin:testpw -X PUT -HAccept:text/xml -HContent-Type:text/xml \
    --data-binary '<value type="object"><comment>Sample comment</comment></value>' \
    https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvMS4wLjAvMTYvMA:10.1.0.0%2F16?\
    _return_type=xml-pretty

The server still returns the network reference. Note that this could be different from before:

<?xml version="1.0"?>
<value>network/ZG5zLm5ldHdvMS4wLjAvMTYvMA:10.1.0.0%2F16</value>

Check that the network was modified, since comment is not a field that is returned by default add _return_fields to the GET request:

curl -k1 -u admin:testpw -X GET -HAccept:text/xml \
    https://192.168.1.2/wapi/v2.12.3/network \
    -d _return_fields=network,network_view,comment -d _return_type=xml-pretty

Note that the 10.1.0.0/16 network has been modified:

<?xml version="1.0"?>
<list>
  <value type="object">
    <comment>Sample comment</comment>
    <network_view>default</network_view>
    <_ref>network/ZG5zLm5ldHdvMS4wLjAvMTYvMA:10.1.0.0%2F16</_ref>
    <network>10.1.0.0/16</network>
  </value>
  <value type="object">
    <network_view>default</network_view>
    <_ref>network/ZG5zLm5ldHduMi4wLjAvMTYvMA:10.2.0.0%2F16</_ref>
    <network>10.2.0.0/16</network>
  </value>
</list>

Search for a network

To find networks with comments that contain the word sample in a case-insensitive way:

curl -k1 -u admin:testpw -X GET -HAccept:text/xml \
    https://192.168.1.2/wapi/v2.12.3/network -d comment~:=sample \
    -d _return_type=xml-pretty

The server returns the network we just modified:

<?xml version="1.0"?>
<list>
  <value type="object">
    <comment>Sample comment</comment>
    <network_view>default</network_view>
    <_ref>network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0%2F16</_ref>
    <network>10.1.0.0/16</network>
  </value>
</list>

If there is no match, the server returns an empty list:

curl -k1 -u admin:testpw -X GET -HAccept:text/xml \
    https://192.168.1.2/wapi/v2.12.3/network -d comment~:=nomatch \
    -d _return_type=xml-pretty

The server returns the following:

<?xml version="1.0"?>
<list>
</list>

Delete a network

To delete a network, send a DELETE request using a reference you have retrieved by searching. For example, to delete the networks we created above, send the following:

curl -k1 -u admin:testpw -X DELETE -HAccept:text/xml \
    https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvcmskMLjAvMTYvMA:10.1.0.0%2F16 \
    -d _return_type=xml-pretty

The server returns the reference of the object it just deleted, if the deletion was successful:

<?xml version="1.0"?>
<value>network/ZG5zLm5ldHdvcmskMLjAvMTYvMA:10.1.0.0%2F16</value>

To delete the other network, send the following:

curl -k1 -u admin:testpw -X DELETE -HAccept:text/xml \
    https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdAuMi4wLjAvMTYvMA:10.2.0.0%2F16 \
    -d _return_type=xml-pretty

Note that both networks have been removed:

curl -k1 -u admin:testpw -X GET -HAccept:text/xml \
    https://192.168.1.2/wapi/v2.12.3/network -d _return_type=xml-pretty

The server returns the following:

<?xml version="1.0"?>
<list>
</list>

Create a host record

To create a host record in a specified zone, first send the following request to create the zone:

curl -k1 -u admin:testpw -H "Content-Type: application/xml" -X POST \
https://192.168.1.2/wapi/v2.12.3/zone_auth -d \
'<?xml version="1.0"?><value type="object"><fqdn>zone.com</fqdn></value>'

Then send the following request to create the host:

curl -k1 -u admin:testpw -H "Content-Type: application/xml" -X POST \
https://192.168.1.2/wapi/v2.12.3/record:host -d \
'<?xml version="1.0"?><value type="object"><name>host.zone.com</name>'\
'<ipv4addrs><list><value type="object"><ipv4addr>10.222.0.12</ipv4addr>'\
'</value></list></ipv4addrs></value>'

Note that it might be necessary to specify the content type explicitly when using the -d option in curl.

Schedule an object creation.

To schedule an object creation, use a POST request with the _schedinfo.scheduled_time parameter:

curl -k1 -u admin:testpw -X POST -HContent-Type:text/xml --data-binary \
    '<value type="object"><network>10.1.0.0/16</network></value>' \
    'https://192.168.1.2/wapi/v2.12.3/network'\
    '?_return_type=xml-pretty&_schedinfo.scheduled_time=1367752903'

The server returns a reference of the created scheduled task:

<?xml version="1.0"?>
<value>scheduledtask/b25lLnF1ZXVlZF90YXNrJDA:0/PENDING</value>

Execute a function call.

To execute a function call, use a POST request with the _function parameter. For example, first create a network:

curl -k1 -u admin:testpw -X POST -HContent-Type:text/xml --data-binary \
    '<value type="object"><network>10.2.0.0/16</network></value>' \
    https://192.168.1.2/wapi/v2.12.3/network?_return_type=xml-pretty

the server will then return a reference to the network that was just created:

<?xml version="1.0"?>
<value>network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:10.1.0.0/16/default</value>

now use this reference to retrieve the next three available /24 networks in this network excluding 10.1.1.0/24 and 10.1.3.0/24:

curl -k1 -u admin:testpw -X POST -HContent-Type:text/xml --data-binary \
'<value type="object"><exclude><list><value>10.1.1.0/24</value>'\
'<value>10.1.3.0/24</value></list></exclude><cidr type="int">24</cidr>'\
'<num type="int">3</num></value>' \
'https://192.168.1.2/wapi/v2.12.3/network/ZG5zLm5ldHdvcmskMTAuMS4wLjAvMTYvMA:'\
'10.1.0.0/16/default?_function=next_available_network&_return_type=xml-pretty'

The server returns a list of available networks with the above constraints:

<?xml version="1.0"?>
<value type="object">
<networks>  <list>
    <value>10.1.0.0/24</value>
    <value>10.1.2.0/24</value>
    <value>10.1.4.0/24</value>
  </list>
</networks></value>

Certificate Based Authentication

This section includes examples for configuring certificate based authentication. To simplify the examples, self-signed certificate is generated for client certificate signing.

Generate self-signed CA certificate

Generate a self-signed certificate and use it as a Certificate Authority (CA) certificate that is treated as a trusted source for signing client certificates *. To do so, run the openssl req command with the -x509 argument.

To generate a private key alongside with a certificate, run the -newkey command with the argument that tells openssl that you need a RSA private key of length 4096. The -nodes (literally “No-DES”) parameter is used to skip passphrase private key protection, as follows:

openssl req -x509 -newkey rsa:4096 -nodes -keyout ca.key.pem \
-out ca.cert.pem -days 365 -subj '/CN=ib-root-ca'

* - however in a real world using real Certificate Authorities is preferred.

Output:

Generating a 4096 bit RSA private key
..........................................................++
..........................................................++
writing new private key to 'ca.key.pem'
-----

Generate Certificate Signing Request (CSR) for a client certificate

The first step in acquiring client certificate is to create a Certificate Signing Request (CSR) that is signed by the Certificate Authority. To generate a CSR, run the openssl req command with the -new argument. Same as for the CA certificate, a client private key is generated using the -newkey option without passphrase protection (-nodes). The CSR validity period is determined by the -days argument and is equal to 365 days. Note that the Canonical Name (CN) in the subject should contain the desired user name, as follows:

openssl req -new -sha256 -newkey rsa:4096 -nodes \
-keyout client.key.pem -days 365 -out client.req.pem \
-subj '/CN=ib-employee'

Output:

Generating a 4096 bit RSA private key
..........................................................++
.................++
writing new private key to 'client.key.pem'
-----

Sign the client certificate with CA certificate

The last step in generating the client certificate is the CSR signing by CA. (In this example, we are using a previously generated CA certificate). To sign the CSR by a CA, run the openssl x509 command with the -req argument and pass the client CSR (client.req.pem), CA Certificate (ca.cert.pem), CA private key (ca.key.pem) and an arbitrary serial number (1209199). To include SAN (Subject Alternative Name) e-mail address use -extfile argument (or explicit configuration file) with subjectAltName set to a desired e-mail address, as follows:

openssl x509 -req -days 365 \
-extfile <(printf "subjectAltName=email:employee@infoblox.com") \
-in client.req.pem -CA ca.cert.pem -CAkey ca.key.pem -set_serial 1209199 \
-out client.cert.pem

Output:

Signature ok
subject=/CN=ib-employee
Getting CA Private Key

Upload CA Certificate

To upload the CA certificate, you first initialize the data upload procedure. To initialize the data upload procedure, call the fileop datauploadinit function that returns the URL of the destination file and the token that will be used in the certificate upload operations, as follows:

curl -H "Content-Type:application/json" -k -u admin:infoblox -X POST \
https://127.0.0.1/wapi/v2.12.3/fileop?_function=uploadinit -d '{}'

The server will return URL for direct upload and file token to use in fileop function calls:

{
    "token": "eJydUMtOwzAQvO+...",
    "url": "https://127.0.0.1/http_direct_file_io/..."
}

Using curl we can upload contents of the CA certificate (ca.cert.pem) to a URL returned from datauploadinit operation:

curl -k1 -u admin:infoblox -F file=@ca.cert.pem \
"https://127.0.0.1/http_direct_file_io/..."

To upload the CA certificate (cacertificate), call the fileop uploadcertificate function with the certificate_usage parameter set to EAP_CA, member set to a desired member hostname, and token set to a token value returned by a fileop datauploadinit function call, as follows:

curl -k1 -u admin:infoblox -X POST -H "Content-Type: application/json" \
https://127.0.0.1/wapi/v2.12.3/fileop?_function=uploadcertificate -d \
'{
    "certificate_usage": "EAP_CA",
    "member": "infoblox.localdomain",
    "token": "eJydUMtOwzAQvO+..."
}'

The server will return empty dictionary if operation succeeds:

{}

Run the GET operation to verify that the cacertificate is now present in the database, as follows:

curl -k1 -u admin:infoblox -X GET https://127.0.0.1/wapi/v2.12.3/cacertificate

The server will return cacertificate object:

[
    {
        "_ref": "cacertificate/b25lLmVhcF9j...",
        "distinguished_name": "CN=\"ib-root-ca\"",
        "issuer": "CN=\"ib-root-ca\"",
        "serial": "9f770b9a53359c6b",
        "valid_not_after": 1528955885,
        "valid_not_before": 1497419885
    }
]

Create Admin User

Create adminuser object with name matching the client.cert.pem SAN e-mail, as follows:

curl -k1 -u admin:infoblox -H "Content-Type: application/json" -X POST \
https://127.0.0.1/wapi/v2.12.3/adminuser -d \
'{
    "admin_groups": ["admin-group"],
    "name": "employee@infoblox.com",
    "password": "infoblox"
}'

The server will return a reference to the adminuser that was just created:

"adminuser/b25lLmFkbWluJGVtcGxveWVlQGluZm9ibG94LmNvbQ:employee%40infoblox.com"

Create Certificate Authentication Service (CAS)

Create certificate:authservice object with OCSP disabled (for simplicity), and the CA certificate set to a previously installed CA certificate (ca.cert.pem). To drop password authentication, enable_password_request is set to “false”. The AUTO_MATCH match type forces NIOS to extract the username from the certificate and searches for it in effective authorization policies based on the configured match policies. The auto_populate_login setting specifies the match policy, that is, match by e-mail address in the SAN, as follows:

curl -k1 -u admin:infoblox -H "Content-Type: application/json" -X POST \
https://127.0.0.1/wapi/v2.12.3/certificate:authservice -d \
'{
    "name": "cert-login",
    "ocsp_check": "DISABLED",
    "ca_certificates": [
        "cacertificate/b25lLmVhcF9j..."
    ],
    "enable_password_request": false,
    "client_cert_subject": "",
    "trust_model": "DIRECT",
    "user_match_type": "AUTO_MATCH",
    "auto_populate_login": "SAN_EMAIL"
}'

The server will return a reference to the certificate:authservice object that was just created:

"certificate:authservice/b25lLm9jc3BfYXV0aF9zZXJ2aWNlJGNlcnQtbG9naW4:cert-login"

Include CAS to Authentication Policy

You need to include the Certificate Authentication Policy in the list of Grid authentication policies. To do so, first perform the GET operation on the authpolicy object object, as follows:

curl -k1 -u admin:infoblox -X GET \
https://127.0.0.1/wapi/v2.12.3/authpolicy?_return_fields=auth_services

The server will return an authpolicy object:

[
    {
        "_ref": "authpolicy/b25lLnJlbW90ZV9hZG1pbl9wb2xpY3kkMA:authpolicy",
        "auth_services": [
            "localuser:authservice/Li5sb2NhbF91c2VyX2F1dGhfc2VydmljZSQw:Local%20Admin"
        ]
    }
]

Then, update the authpolicy object. Note that the CAS reference should precede the Local User Authentication Service to avoid server performing password authentication, as follows:

curl -k1 -u admin:infoblox -H "Content-Type: application/json" -X PUT \
https://127.0.0.1/wapi/v2.12.3/authpolicy/b25lLnJlbW90ZV9hZG1pbl9wb2xpY3kkMA:authpolicy -d \
'{
    "auth_services": [
        "certificate:authservice/b25lLm9jc3BfYXV0aF9zZXJ2aWNlJGNlcnQtbG9naW4:cert-login",
        "localuser:authservice/Li5sb2NhbF91c2VyX2F1dGhfc2VydmljZSQw:Local%20Admin"
    ]
}'

The server will return reference to the authpolicy object if the operation succeeds:

"authpolicy/b25lLnJlbW90ZV9hZG1pbl9wb2xpY3kkMA:authpolicy"

Restart product and run sample GET

Perform the GET operation on any object (admingroup in our example) using the client key and client certificate, as follows:

curl -k -v -s --key client.key.pem --cert client.cert.pem \
-X GET https://127.0.0.1/wapi/v2.12.3/admingroup

Verbose (-v) output of the curl command is included to verify the TLS connection, as follows:

*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / DHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*        subject: C=US; ST=California; L=Sunnyvale; O=Infoblox; OU=Engineering; CN=www.infoblox.com
*        start date: 2017-07-13 08:09:23 GMT
*        expire date: 2018-07-13 08:09:23 GMT
*        issuer: C=US; ST=California; L=Sunnyvale; O=Infoblox; OU=Engineering; CN=www.infoblox.com
*        SSL certificate verify result: self signed certificate (18), continuing anyway.
> GET /wapi/v2.12.3/admingroup HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.43.0
> Accept: */*
>
* TLSv1.2 (IN), TLS handshake, Hello request (0):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
< HTTP/1.1 200 OK
< Date: Thu, 13 Jul 2017 08:25:10 GMT
< WWW-Authenticate: Basic realm="InfoBlox ONE Platform"
< Cache-Control: no-cache, no-store
< Pragma: no-cache
< Content-Type: application/json
< set-cookie: ibapauth="ip=127.0.0.1,client=API,group=admin-group,ctime=1499934313,timeout=600,mtime=1499934313,su=1,auth=LOCAL,user=employee@infoblox.com,X8E/BVuFP95+srDuipWO5n5xqThqK5XA2F4"; httponly; Path=/; secure
< Transfer-Encoding: chunked
<

[
    {
        "_ref": "admingroup/b25lLmFkbWluX2dyb3VwJC5zcGx1bmstcmVwb3J0aW5nLWdyb3Vw:splunk-reporting-group",
        "name": "splunk-reporting-group"
    },
    {
        "_ref": "admingroup/b25lLmFkbWluX2dyb3VwJC5hZG1pbi1ncm91cA:admin-group",
        "name": "admin-group"
    },
    {
        "_ref": "admingroup/b25lLmFkbWluX2dyb3VwJC5jbG91ZC1hcGktb25seQ:cloud-api-only",
        "comment": "Admins allowed to perform API request on Cloud API",
        "name": "cloud-api-only"
    }
]

* Connection #0 to host 127.0.0.1 left intact

Note that you can incorporate the client key in the client certificate (simply concatenate the certificate and key files), and then use only the –cert option.