diff options
| author | Hannes Duerr <h.duerr@proxmox.com> | 2025-02-10 15:19:25 +0100 |
|---|---|---|
| committer | Thomas Lamprecht <t.lamprecht@proxmox.com> | 2025-03-06 10:00:21 +0100 |
| commit | 894d2d33e3b104bcdc8b83e3f205427db93800c3 (patch) | |
| tree | 94a5af2ba5a2a9b84a04ea28800affa7cc1ec5d2 /src | |
| parent | 77671ba327fa0fc6461d70f9e9e64891b58c36ab (diff) | |
api request helper: enforce TLS cert-check and add cert-fingerprint option
Currently, we do not verify the TLS certificate for API requests
external IPAM and DNS integration. This could allow man-in-the-middle
attacks, albeit most IPAM infrastructure is on controlled and isolated
LANs, so it's not something that should frequently happen; and
technically our IPAM integration is still marked as tech-preview,
which had its reasons.
Enforce verification, and allow users to pass a cert SHA256
fingerprint to ensure a certificates validity if it's not trusted by
the system trust store, as it's, e.g., the case for self-signed certs.
The code was adapted from the one in pve-apiclient, which we cannot
reuse directly as it is only implemented for requests against PVE
nodes, not as a generic HTTP client request helper.
Add the new dependency `libio-socket-ssl-perl` required to get the
verify callback for the TLS certificate used for cert-fingerprint
checking.
Signed-off-by: Hannes Duerr <h.duerr@proxmox.com>
Tested-by: Stefan Hanreich <s.hanreich@proxmox.com>
[TL: return valid for non-leaf certs and rewrite commit message]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/PVE/Network/SDN.pm | 23 |
1 files changed, 21 insertions, 2 deletions
diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm index 1186d9e..8cd9ad8 100644 --- a/src/PVE/Network/SDN.pm +++ b/src/PVE/Network/SDN.pm @@ -3,7 +3,9 @@ package PVE::Network::SDN; use strict; use warnings; +use IO::Socket::SSL; # important for SSL_verify_callback use JSON; +use Net::SSLeay; use PVE::INotify; @@ -256,7 +258,7 @@ sub encode_value { #helpers sub api_request { - my ($method, $url, $headers, $data) = @_; + my ($method, $url, $headers, $data, $expected_fingerprint) = @_; my $encoded_data = to_json($data) if $data; @@ -270,7 +272,24 @@ sub api_request { $ua->env_proxy; } - $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00); + if (defined($expected_fingerprint)) { + my $ssl_verify_callback = sub { + my (undef, undef, undef, undef, $cert, $depth) = @_; + + # we don't care about intermediate or root certificates, always return as valid as the + # callback will be executed for all levels and all must be valid. + return 1 if $depth != 0; + + my $fingerprint = Net::SSLeay::X509_get_fingerprint($cert, 'sha256'); + + return $fingerprint eq $expected_fingerprint ? 1 : 0; + }; + $ua->ssl_opts( + verify_hostname => 0, + SSL_verify_mode => SSL_VERIFY_PEER, + SSL_verify_callback => $ssl_verify_callback, + ); + } my $response = $ua->request($req); |
