Repository navigation
OpenVPN Client Export Bug #756
Description
Describe the bug
When using the Export endpoint it returns "The OpenVPN client export failed for the following reason: Could not find client certificate.".
However the certificate does exist but the code seems to be using the wrong ID.
From my other post:
I did some further debugging and it seems the code from openvpn-client-export.inc (from pfSense itself) is returning the wrong thing.
In this line "$cert = $user['cert'][$crtid];" the $crtid is 30. However the $user array/object returning has the cert with id 30. The certificate itself is ID 30 but inside the $user object/array it has a key value of 0.
When I hardcore the $crtid to 0 it does return the configs.
To Reproduce
Below my example function I do for the call
function testFunction()
{
global $apibaseurl, $apikey;
// CREATE USER CERTIFICATE (pfSense API v1)
$endpoint = $apibaseurl. 'vpn/openvpn/client_export';
$payload = array(
"id" => "0", // OpenVPN server ID
"type" => "confzip",
"certref" => "XXXfa75f76XXX", // VERIFY THIS EXACT ID FROM listCerts()
"username" => "XXX_WarXXetxx",
);
$json = json_encode($payload);
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
'Accept: application/octet-stream', // Critical for binary ZIP response
'x-api-key: ' . $apikey
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_error($ch)) {
echo "cURL Error: " . curl_error($ch);
}
curl_close($ch);
if ($httpCode == 200 && $response !== false) {
// Binary ZIP data - save to file instead of json_decode
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="openvpn_XXX_WarXXetxx.zip"');
header('Content-Length: ' . strlen($response));
echo $response;
//echo "Export successful! ZIP saved as 'openvpn_export.zip' (size: " . strlen($response) . " bytes)\n";
} else {
$data = json_decode($response, true);
$data['http_code'] = $httpCode;
echo "<pre>Error Response:\n";
print_r($data);
echo "</pre>";
}
}
Expected behavior
I expect to get the client export as a ZIP. However I get the error. But when I change the line in openvpn-client-export.inc it does give me back the correct certificate. So the Payload I use in my code does seem to be correct I think.
pfSense Version & Package Version:
- pfSense Version: 2.8.1-RELEASE
- Package Version v2.6.0
Affected Endpoints:
- URL: /api/v2/vpn/openvpn/client_export
If there is more information needed please let me know!
Activity
jaredhendrickson13 commentedon Sep 27, 2025
Having trouble reproducing this. Here's what I'm trying on a fresh install of CE 2.8.1:
{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 0, "name": "pfSense-pkg-openvpn-client-export", "shortname": "openvpn-client-export", "descr": "Exports pre-configured OpenVPN Client configurations directly from pfSense\nsoftware.", "installed_version": "1.9.5", "latest_version": "1.9.5", "update_available": false } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 0, "descr": "vpn_ca", "refid": "68d851be0a7ff", "trust": true, "randomserial": true, "serial": 1, "is_intermediate": false, "caref": null, "keytype": "RSA", "keylen": 2048, "ecname": null, "digest_alg": "sha256", "lifetime": 3650, "dn_commonname": "internal-ca", "dn_country": "US", "dn_state": "Utah", "dn_city": "Salt Lake City", "dn_organization": "ACME Org", "dn_organizationalunit": "IT", "crt": "-----BEGIN CERTIFICATE-----\nMIIEODCCAyCgAwIBAgII...UUujXjIlDW10QvnB0fXY6xVg5Tr9m6Q==\n-----END CERTIFICATE-----\n" } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 1, "descr": "vpn_server_cert", "refid": "68d853e619706", "caref": "68d851be0a7ff", "keytype": "RSA", "keylen": 2048, "ecname": null, "digest_alg": "sha256", "lifetime": 365, "dn_commonname": "vpn_server", "dn_country": null, "dn_state": null, "dn_city": null, "dn_organization": null, "dn_organizationalunit": null, "type": "server", "dn_dns_sans": [], "dn_email_sans": [], "dn_ip_sans": [], "dn_uri_sans": [], "crt": "-----BEGIN CERTIFICATE-----\nMIIEWjCCA0KgAwIBAgII...MNcvFaeoGAI=\n-----END CERTIFICATE-----\n" } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 0, "vpnid": 1, "vpnif": "ovpns1", "description": "", "disable": false, "mode": "server_tls", "authmode": null, "dev_mode": "tun", "protocol": "UDP4", "interface": "wan", "local_port": "1194", "use_tls": true, "tls_type": "auth", "tlsauth_keydir": "default", "caref": "68d851be0a7ff", "certref": "68d853e619706", "cert_depth": 1, "dh_length": "2048", "ecdh_curve": "none", "data_ciphers": [ "AES-256-GCM" ], "data_ciphers_fallback": "AES-256-GCM", "digest": "SHA256", "strictusercn": null, "remote_cert_tls": true, "tunnel_network": "", "tunnel_networkv6": "", "serverbridge_dhcp": null, "serverbridge_interface": null, "serverbridge_routegateway": null, "serverbridge_dhcp_start": null, "serverbridge_dhcp_end": null, "gwredir": false, "gwredir6": false, "local_network": [], "local_networkv6": [], "remote_network": [], "remote_networkv6": [], "maxclients": null, "allow_compression": "no", "passtos": false, "client2client": false, "duplicate_cn": false, "connlimit": null, "dynamic_ip": false, "topology": "subnet", "inactive_seconds": 300, "ping_method": "keepalive", "keepalive_interval": 10, "keepalive_timeout": 60, "ping_seconds": null, "ping_push": null, "ping_action": null, "ping_action_seconds": null, "ping_action_push": null, "dns_domain": null, "dns_server1": null, "dns_server2": null, "dns_server3": null, "dns_server4": null, "push_blockoutsidedns": null, "push_register_dns": null, "ntp_server1": null, "ntp_server2": null, "netbios_enable": null, "netbios_ntype": null, "netbios_scope": null, "wins_server1": null, "wins_server2": null, "custom_options": [], "username_as_common_name": null, "sndrcvbuf": null, "create_gw": "both", "verbosity_level": 1 } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 0, "server": 1, "useaddr": "serveraddr", "useaddr_hostname": null, "verifyservercn": "auto", "blockoutsidedns": false, "legacy": false, "silent": false, "bindmode": "nobind", "usepkcs11": false, "pkcs11providers": null, "pkcs11id": null, "usetoken": false, "usepass": false, "p12encryption": "high", "useproxy": false, "useproxytype": null, "proxyaddr": null, "proxyport": null, "useproxypass": null, "proxyuser": null, "advancedoptions": "" } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 1, "name": "vpn_user", "uid": 2000, "scope": "user", "priv": [], "disabled": false, "descr": "", "expires": "", "cert": [], "authorizedkeys": "", "ipsecpsk": "" } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 2, "descr": "vpn_user_cert", "refid": "68d859f447964", "caref": "68d851be0a7ff", "keytype": "RSA", "keylen": 2048, "ecname": null, "digest_alg": "sha256", "lifetime": 365, "dn_commonname": "vpn_user", "dn_country": null, "dn_state": null, "dn_city": null, "dn_organization": null, "dn_organizationalunit": null, "type": "server", "dn_dns_sans": [], "dn_email_sans": [], "dn_ip_sans": [], "dn_uri_sans": [], "crt": "-----BEGIN CERTIFICATE-----\nMIIEWDCCA0CgAwIBAgIIa...nq6mJZiPGOM8RzNee\n-----END CERTIFICATE-----\n" } }{ "code": 200, "status": "ok", "response_id": "SUCCESS", "message": "", "data": { "id": 1, "name": "vpn_user", "uid": 2000, "scope": "user", "priv": [], "disabled": false, "descr": "", "expires": "", "cert": [ "68d859f447964" ], "authorizedkeys": "", "ipsecpsk": "" } }Then I'm able to unzip the file and the expected contents are present:
Seems to work regardless of the number of certificates in the pfSense config or number of user certificates assigned to a user. I've attached the resulting config file as well.
config.xml
Anything I'm missing?
cvanbrummelen commentedon Sep 30, 2025
@jaredhendrickson13 Thanks for all the steps! Don't think you are missing anything. Ive checked all the steps myself and that is pretty much what I have.
I also tried to redo the steps from step 6 to check if I need to create a user after setting the Client Export Config but no succes.
Below the response I get from client_export/configs (Did it twice).
And the body I send to client_export
Response:
Also a screenshot to show that the certificate is connected to the user:
jaredhendrickson13 commentedon Sep 30, 2025
Hmm that is very odd. What do the
system/user,certandcasections of your XML config look like?cvanbrummelen commentedon Oct 1, 2025
Below the parts of the XML, I did remove some stuff, if you need more let me know.
System/User:
Cert part for TESTUSER
CA:
jaredhendrickson13 commentedon Oct 4, 2025
Thanks for your patience and providing all this info. I do believe I see the issue now and can reproduce. The pfSense export functions unnecessarily change the context of the certificate's ID for several different factors which the API is not fully accounting for. The $crtid sometimes refers to the index of
system/user/certfield, but usually refers to the index ofcert(which is what the API is expecting). It's a little silly since the actual certificate has to be looked up atcertanyway, so looking up the certificate atsystem/user/certfirst is just an unnecessarily complicated step.Regardless, I'll get working on a fix and will have it in the next patch. Hopefully this weekend or early next week.
Thanks again for reporting this!
fix(OpenVPNClientExport): handle unexpected crtid ambiguity #756
cvanbrummelen commentedon Oct 6, 2025
Thanks! I will wait for the fix and get on it when its out.
test(OpenVPNClientExport): ensure model handles crtid ambiguity #756
cvanbrummelen commentedon Oct 8, 2025
@jaredhendrickson13 Fix works perfectly! Thanks for the work and the fast response!
lapf10 commentedon Nov 13, 2025
Hi,
Thanks for great work on this projet.
I try to export my OpenVPN Client but without success.
I do not have users created at Pfsense, my users are from LDAP.
I follow the steps metioned before and i get the error below
1 -> POST /api/v2/vpn/openvpn/client_export/config
2 -> Response GET /api/v2/vpn/openvpn/client_export/configs
3 -> Try Export POST /api/v2/vpn/openvpn/client_export
Can you please help to solve this problem.
Thanks.