[SOLVED] Trouble adding CSO via API – always getting {"result":"failed"}

Started by kozistan, April 11, 2025, 07:31:30 PM

Previous topic - Next topic
Hi,
I'm trying to add a Client Specific Override (CSO) using the OPNsense API and curl, but I keep getting the response {"result":"failed"}.

I've tried various payload formats, using this OPNsense API doc as a base. Since I couldn't find a full schema for OPNsense CSO, I borrowed the format from the pf API here: https://pfrest.org/api-docs/#/VPN/postVPNOpenVPNCSOEndpoint

Here's one example I tested:

curl -v -k --location https://my.opnsense.host/api/openvpn/client_overwrites/add \
-u "key:secret" \
--header 'Content-Type: application/json' \
--data '{
  "common_name": "test.user",
  "disable": false,
  "block": false,
  "description": "IP-Reservation",
  "server_list": ["OVPN-Proton-IN"],
  "tunnel_network": "172.17.2.107/24"
}'

I also tried with escaping quotes and with other field combinations, but always got the same result.

The API call completes successfully with HTTP 200, but the body returns: {"result":"failed"}
full output:

* Host my.opnsense.host:443 was resolved.
* IPv6: (none)
* IPv4: 192.0.2.123
*   Trying 192.0.2.123:443...
* Connected to your.opnsense.host (192.0.2.123) port 443
* ALPN: curl offers h2,http/1.1
* (TLS handshake and certificate ok)
* using HTTP/2
* Server auth using Basic with user '[REDACTED]'
> POST /api/openvpn/client_overwrites/add HTTP/2
> Host: your.opnsense.host
> Authorization: Basic [REDACTED]
> User-Agent: curl/8.7.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 213
>
* upload completely sent off: 213 bytes
< HTTP/2 200
< content-type: application/json; charset=UTF-8
< server: OPNsense
< date: Fri, 11 Apr 2025 17:06:49 GMT
< set-cookie: PHPSESSID=[REDACTED]; path=/; secure; HttpOnly; SameSite=Lax
<
{"result":"failed"}

Any idea what I might be doing wrong? Is the tunnel_network key invalid in OPNsense? What's the correct schema for the CSO add endpoint?

Thanks!


Hi again, and thanks for quick reply!
After several tests and with help from search and instances/search API calls, I believe I've finally figured out why my CSO API calls keep returning {"result":"failed"}.
Root cause: missing name field in OpenVPN instances?

The API endpoint client_overwrites/add expects server_list to include internal OpenVPN instance names, not just their descriptions. These names are normally set internally when creating an instance, but in my case, all instances returned:

"name": null,
"description": "OVPN-Proton-IN"

This explains why the CSO can't be added – the instance has no valid internal name, and the API doesn't know where to attach the override.

curl -X POST 'https://my.opnsense/api/openvpn/instances/search' \
-u 'APIKEY:APISECRET' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-raw '{}' \
--insecure | jq '.rows[] | {name, description}'


Am I wrong?

Follow-up: Still getting {"result":"failed"} despite updated syntax and values

I tried adjusting the request syntax after reviewing the updated API docs and checking existing CSO entries via the search endpoint.

Here's the command I used:

curl -X POST 'https://my.opnsense.host/api/openvpn/client_overwrites/add' \
-u 'APIKEY:APISECRET' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-raw '{
  "common_name": "test.user",
  "description": "IP-Reservation",
  "servers": "OVPN-Proton-IN (52002 / UDP)",
  "tunnel_network": "172.17.2.107/32"
}' \
--insecure

Before sending the request, I verified the servers value by listing current CSOs with:

curl -X POST 'https://my.opnsense.host/api/openvpn/client_overwrites/search' \
-u 'APIKEY:APISECRET' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-raw '{}' \
--insecure | jq '.rows[] | select(.servers | startswith("OVPN"))'

Which returned:

{
  "uuid": "298345e2-da80-45a4-ad65-295670b148d1",
{
  "uuid": "298345e2-da80-45a4-ad65-295670b148d1",
  "enabled": "1",
  "servers": "OVPN-Proton-IN (52002 / UDP)",
  "common_name": "howno",
  "block": "0",
  "push_reset": "0",
  "tunnel_network": "",
  "tunnel_networkv6": "",
  "local_networks": "",
  "remote_networks": "",
  "route_gateway": "",
  "redirect_gateway": "",
  "register_dns": "0",
  "dns_domain": "",
  "dns_domain_search": "",
  "dns_servers": "",
  "ntp_servers": "",
  "wins_servers": "",
  "description": ""
}
}

I updated the request payload accordingly, but unfortunately, the API still responds with
{"result":"failed"}


Update: Got it working – here's where I went wrong and what the fix looks like, thanks again for the guide!

I was doing Incorrect JSON structure – The API expects a top-level key called "cso", like this:

{
  "cso": {
    "common_name": "...",
    ...
  }
}

And wrong servers field – You must provide the UUID of the OpenVPN instance (not its name or description).
You can retrieve these UUIDs by calling:

curl -X POST 'https://your.opnsense/api/openvpn/instances/search' ...
Have another Q, what about removing CSO. By inspecting API while removing CSO i can not see the correct endpoint. Im trying it with:

curl -X POST 'https://my.opnsense.host/api/openvpn/client_overwrites/del' \
-u 'APIKEY:APISECRET' \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-raw '{"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}' \
--insecure

and getting
{"errorMessage":"Endpoint not found"}

ok, at Network/Headers was the answer for correct endpoint.

{OPNSENSE_URL}/api/openvpn/client_overwrites/del/{uuid}
thanks again!