* [pve-devel] [PATCH-SERIES v2 common/manager] Fix SSL Certificate and Key Upload Issues @ 2023-03-03 17:57 Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match Max Carrara ` (3 more replies) 0 siblings, 4 replies; 13+ messages in thread From: Max Carrara @ 2023-03-03 17:57 UTC (permalink / raw) To: pve-devel This is v2 of https://lists.proxmox.com/pipermail/pve-devel/2023-February/055955.html Changes from v1: - properly handle OpenSSL errors[0] - provide actually correct format changes[1] - include trailing comma[2] - make JavaScript code a little more concise[3] [0] https://lists.proxmox.com/pipermail/pve-devel/2023-March/055962.html [1] https://lists.proxmox.com/pipermail/pve-devel/2023-March/055963.html [2] Not on mailing list unfortunately, boo! [3] https://lists.proxmox.com/pipermail/pve-devel/2023-March/055961.html pve-common: Max Carrara (2): certificate: add subroutine that checks if cert and key match certificate: fix formatting and whitespace src/PVE/Certificate.pm | 74 +++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 15 deletions(-) pve-manager: Max Carrara (2): fix #4552: certhelpers: check if custom cert and key match on change ui: cert upload: fix private key field sending empty string PVE/CertHelpers.pm | 65 +++++++++++++++++++++++++++---- www/manager6/node/Certificates.js | 3 ++ 2 files changed, 60 insertions(+), 8 deletions(-) -- 2.30.2 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [pve-devel] [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match 2023-03-03 17:57 [pve-devel] [PATCH-SERIES v2 common/manager] Fix SSL Certificate and Key Upload Issues Max Carrara @ 2023-03-03 17:57 ` Max Carrara 2023-03-07 10:52 ` [pve-devel] applied: " Fabian Grünbichler 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 2/2] certificate: fix formatting and whitespace Max Carrara ` (2 subsequent siblings) 3 siblings, 1 reply; 13+ messages in thread From: Max Carrara @ 2023-03-03 17:57 UTC (permalink / raw) To: pve-devel This is done here in order to allow other packages to make use of this subroutine. Signed-off-by: Max Carrara <m.carrara@proxmox.com> --- src/PVE/Certificate.pm | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/PVE/Certificate.pm b/src/PVE/Certificate.pm index 31a7722..22de762 100644 --- a/src/PVE/Certificate.pm +++ b/src/PVE/Certificate.pm @@ -228,6 +228,47 @@ sub get_certificate_fingerprint { return $fp; } +sub certificate_matches_key { + my ($cert_path, $key_path) = @_; + + die "No certificate path given!\n" if !$cert_path; + die "No certificate key path given!\n" if !$key_path; + + die "Certificate at '$cert_path' does not exist!\n" if ! -e $cert_path; + die "Certificate key '$key_path' does not exist!\n" if ! -e $key_path; + + my $ctx = Net::SSLeay::CTX_new() + or $ssl_die->( + "Failed to create SSL context in order to verify private key" + ); + + eval { + my $filetype = &Net::SSLeay::FILETYPE_PEM; + + Net::SSLeay::CTX_use_PrivateKey_file($ctx, $key_path, $filetype) + or $ssl_die->( + "Failed to load private key from '$key_path' into SSL context" + ); + + Net::SSLeay::CTX_use_certificate_file($ctx, $cert_path, $filetype) + or $ssl_die->( + "Failed to load certificate from '$cert_path' into SSL context" + ); + + Net::SSLeay::CTX_check_private_key($ctx) + or $ssl_die->( + "Failed to validate private key and certificate" + ); + }; + my $err = $@; + + Net::SSLeay::CTX_free($ctx); + + die $err if $err; + + return 1; +} + sub get_certificate_info { my ($cert_path) = @_; -- 2.30.2 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [pve-devel] applied: [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match Max Carrara @ 2023-03-07 10:52 ` Fabian Grünbichler 2023-03-07 17:47 ` Thomas Lamprecht 0 siblings, 1 reply; 13+ messages in thread From: Fabian Grünbichler @ 2023-03-07 10:52 UTC (permalink / raw) To: Proxmox VE development discussion with an added 'check_' prefix for the sub name to indicate that this will die if the checked condition is not met. On March 3, 2023 6:57 pm, Max Carrara wrote: > This is done here in order to allow other packages to make use of > this subroutine. > > Signed-off-by: Max Carrara <m.carrara@proxmox.com> > --- > src/PVE/Certificate.pm | 41 +++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 41 insertions(+) > > diff --git a/src/PVE/Certificate.pm b/src/PVE/Certificate.pm > index 31a7722..22de762 100644 > --- a/src/PVE/Certificate.pm > +++ b/src/PVE/Certificate.pm > @@ -228,6 +228,47 @@ sub get_certificate_fingerprint { > return $fp; > } > > +sub certificate_matches_key { > + my ($cert_path, $key_path) = @_; > + > + die "No certificate path given!\n" if !$cert_path; > + die "No certificate key path given!\n" if !$key_path; > + > + die "Certificate at '$cert_path' does not exist!\n" if ! -e $cert_path; > + die "Certificate key '$key_path' does not exist!\n" if ! -e $key_path; > + > + my $ctx = Net::SSLeay::CTX_new() > + or $ssl_die->( > + "Failed to create SSL context in order to verify private key" > + ); > + > + eval { > + my $filetype = &Net::SSLeay::FILETYPE_PEM; > + > + Net::SSLeay::CTX_use_PrivateKey_file($ctx, $key_path, $filetype) > + or $ssl_die->( > + "Failed to load private key from '$key_path' into SSL context" > + ); > + > + Net::SSLeay::CTX_use_certificate_file($ctx, $cert_path, $filetype) > + or $ssl_die->( > + "Failed to load certificate from '$cert_path' into SSL context" > + ); > + > + Net::SSLeay::CTX_check_private_key($ctx) > + or $ssl_die->( > + "Failed to validate private key and certificate" > + ); > + }; > + my $err = $@; > + > + Net::SSLeay::CTX_free($ctx); > + > + die $err if $err; > + > + return 1; > +} > + > sub get_certificate_info { > my ($cert_path) = @_; > > -- > 2.30.2 > > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [pve-devel] applied: [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match 2023-03-07 10:52 ` [pve-devel] applied: " Fabian Grünbichler @ 2023-03-07 17:47 ` Thomas Lamprecht 0 siblings, 0 replies; 13+ messages in thread From: Thomas Lamprecht @ 2023-03-07 17:47 UTC (permalink / raw) To: Proxmox VE development discussion, Fabian Grünbichler, Max Carrara Am 07/03/2023 um 11:52 schrieb Fabian Grünbichler: > with an added 'check_' prefix for the sub name to indicate that this will die if > the checked condition is not met. please use assert_ for that in the future, check is hardly more telling as it often indicates the booleaness of the return value not that the function dies (i.e., asserts something). E.g., SSLeay's CTX_check_private_key also has that semantic FWICT. The other two existing methods in that module with the IMO not so ideal naming scheme are check_pem and check_expiry, the former has not use outside of the module, so it could be made private, the latter is actually already implemented as "check" method in my understanding, and only dies if the cert could not be parsed, but otherwise returns boolean - so it could stay. But, as its only used once outside (no in-module use) in the ACME API - it might be nicer to replace by a method that only extracts+returns the validity value or range and then lets the call side do the smaller/greater than timestamp check directly - but well, that's rather to much work for the very littel, if any, code style improvement. > > On March 3, 2023 6:57 pm, Max Carrara wrote: >> This is done here in order to allow other packages to make use of >> this subroutine. >> >> Signed-off-by: Max Carrara <m.carrara@proxmox.com> >> --- >> src/PVE/Certificate.pm | 41 +++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 41 insertions(+) >> >> diff --git a/src/PVE/Certificate.pm b/src/PVE/Certificate.pm >> index 31a7722..22de762 100644 >> --- a/src/PVE/Certificate.pm >> +++ b/src/PVE/Certificate.pm >> @@ -228,6 +228,47 @@ sub get_certificate_fingerprint { >> return $fp; >> } >> >> +sub certificate_matches_key { >> + my ($cert_path, $key_path) = @_; >> + >> + die "No certificate path given!\n" if !$cert_path; >> + die "No certificate key path given!\n" if !$key_path; >> + >> + die "Certificate at '$cert_path' does not exist!\n" if ! -e $cert_path; >> + die "Certificate key '$key_path' does not exist!\n" if ! -e $key_path; >> + >> + my $ctx = Net::SSLeay::CTX_new() >> + or $ssl_die->( >> + "Failed to create SSL context in order to verify private key" >> + ); >> + >> + eval { >> + my $filetype = &Net::SSLeay::FILETYPE_PEM; >> + >> + Net::SSLeay::CTX_use_PrivateKey_file($ctx, $key_path, $filetype) >> + or $ssl_die->( >> + "Failed to load private key from '$key_path' into SSL context" misses trailing new-line, like all existing call sites of this had ;-) fixed in follow up >> + ); meh this is IMO the same as the post-if, OK for one line but otherwise don't will clarify the style guide - but actually, these would be all short enough for 100cc - so why span a post-thingy multiline in the first place here? Note also that in general, the closing parenthesis either goes on the line with the param (for one or two parameters, if still << 100 CC) or an trailing comma should be added. I.e., if it wasn't in the stylistic already problematic post if/or context, then either $ssl_die->( "Failed to load private key from '$key_path' into SSL context\n"); or $ssl_die->( "Failed to load private key from '$key_path' into SSL context\n", ); The former saves a line for few but long arguments, the latter is easily to extend without touching existing lines. >> + >> + Net::SSLeay::CTX_use_certificate_file($ctx, $cert_path, $filetype) >> + or $ssl_die->( >> + "Failed to load certificate from '$cert_path' into SSL context" >> + ); same here >> + >> + Net::SSLeay::CTX_check_private_key($ctx) >> + or $ssl_die->( >> + "Failed to validate private key and certificate" >> + ); same here I pushed a few follow up cleaning that whole module up, while I tried to be careful, a second eye couldn't hurt to detect any possible fat-fingered error I made ;-) >> + }; >> + my $err = $@; >> + >> + Net::SSLeay::CTX_free($ctx); >> + >> + die $err if $err; >> + >> + return 1; >> +} >> + >> sub get_certificate_info { >> my ($cert_path) = @_; >> ^ permalink raw reply [flat|nested] 13+ messages in thread
* [pve-devel] [PATCH v2 common 2/2] certificate: fix formatting and whitespace 2023-03-03 17:57 [pve-devel] [PATCH-SERIES v2 common/manager] Fix SSL Certificate and Key Upload Issues Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match Max Carrara @ 2023-03-03 17:57 ` Max Carrara 2023-03-07 10:52 ` [pve-devel] applied: " Fabian Grünbichler 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string Max Carrara 3 siblings, 1 reply; 13+ messages in thread From: Max Carrara @ 2023-03-03 17:57 UTC (permalink / raw) To: pve-devel Signed-off-by: Max Carrara <m.carrara@proxmox.com> --- src/PVE/Certificate.pm | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) NOTE: This patch can be dropped if not desired. diff --git a/src/PVE/Certificate.pm b/src/PVE/Certificate.pm index 22de762..73091ff 100644 --- a/src/PVE/Certificate.pm +++ b/src/PVE/Certificate.pm @@ -400,19 +400,22 @@ sub generate_csr { $ssl_die->("Failed to allocate X509_NAME object\n") if !$name; my $add_name_entry = sub { my ($k, $v) = @_; - if (!Net::SSLeay::X509_NAME_add_entry_by_txt($name, - $k, - &Net::SSLeay::MBSTRING_UTF8, - encode('utf-8', $v))) { - $cleanup->(1, "Failed to add '$k'='$v' to DN\n"); - } + + my $res = Net::SSLeay::X509_NAME_add_entry_by_txt( + $name, + $k, + &Net::SSLeay::MBSTRING_UTF8, + encode('utf-8', $v), + ); + + $cleanup->(1, "Failed to add '$k'='$v' to DN\n") if !$res; }; $add_name_entry->('CN', $common_name); for (qw(C ST L O OU)) { - if (defined(my $v = $attr{$_})) { + if (defined(my $v = $attr{$_})) { $add_name_entry->($_, $v); - } + } } if (defined($pem_key)) { @@ -445,13 +448,13 @@ sub generate_csr { $cleanup->(1, "Failed to set subject name\n") if (!Net::SSLeay::X509_REQ_set_subject_name($req, $name)); - $cleanup->(1, "Failed to add extensions to CSR\n") - if !Net::SSLeay::P_X509_REQ_add_extensions($req, - &Net::SSLeay::NID_key_usage => 'digitalSignature,keyEncipherment', - &Net::SSLeay::NID_basic_constraints => 'CA:FALSE', - &Net::SSLeay::NID_ext_key_usage => 'serverAuth,clientAuth', - &Net::SSLeay::NID_subject_alt_name => join(',', map { "DNS:$_" } @$san), - ); + Net::SSLeay::P_X509_REQ_add_extensions( + $req, + &Net::SSLeay::NID_key_usage => 'digitalSignature,keyEncipherment', + &Net::SSLeay::NID_basic_constraints => 'CA:FALSE', + &Net::SSLeay::NID_ext_key_usage => 'serverAuth,clientAuth', + &Net::SSLeay::NID_subject_alt_name => join(',', map { "DNS:$_" } @$san), + ) or $cleanup->(1, "Failed to add extensions to CSR\n"); $cleanup->(1, "Failed to set public key\n") if !Net::SSLeay::X509_REQ_set_pubkey($req, $pk); -- 2.30.2 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [pve-devel] applied: [PATCH v2 common 2/2] certificate: fix formatting and whitespace 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 2/2] certificate: fix formatting and whitespace Max Carrara @ 2023-03-07 10:52 ` Fabian Grünbichler 0 siblings, 0 replies; 13+ messages in thread From: Fabian Grünbichler @ 2023-03-07 10:52 UTC (permalink / raw) To: Proxmox VE development discussion On March 3, 2023 6:57 pm, Max Carrara wrote: > Signed-off-by: Max Carrara <m.carrara@proxmox.com> > --- > src/PVE/Certificate.pm | 33 ++++++++++++++++++--------------- > 1 file changed, 18 insertions(+), 15 deletions(-) > > NOTE: This patch can be dropped if not desired. > > diff --git a/src/PVE/Certificate.pm b/src/PVE/Certificate.pm > index 22de762..73091ff 100644 > --- a/src/PVE/Certificate.pm > +++ b/src/PVE/Certificate.pm > @@ -400,19 +400,22 @@ sub generate_csr { > $ssl_die->("Failed to allocate X509_NAME object\n") if !$name; > my $add_name_entry = sub { > my ($k, $v) = @_; > - if (!Net::SSLeay::X509_NAME_add_entry_by_txt($name, > - $k, > - &Net::SSLeay::MBSTRING_UTF8, > - encode('utf-8', $v))) { > - $cleanup->(1, "Failed to add '$k'='$v' to DN\n"); > - } > + > + my $res = Net::SSLeay::X509_NAME_add_entry_by_txt( > + $name, > + $k, > + &Net::SSLeay::MBSTRING_UTF8, > + encode('utf-8', $v), > + ); > + > + $cleanup->(1, "Failed to add '$k'='$v' to DN\n") if !$res; > }; > > $add_name_entry->('CN', $common_name); > for (qw(C ST L O OU)) { > - if (defined(my $v = $attr{$_})) { > + if (defined(my $v = $attr{$_})) { > $add_name_entry->($_, $v); > - } > + } > } > > if (defined($pem_key)) { > @@ -445,13 +448,13 @@ sub generate_csr { > $cleanup->(1, "Failed to set subject name\n") > if (!Net::SSLeay::X509_REQ_set_subject_name($req, $name)); > > - $cleanup->(1, "Failed to add extensions to CSR\n") > - if !Net::SSLeay::P_X509_REQ_add_extensions($req, > - &Net::SSLeay::NID_key_usage => 'digitalSignature,keyEncipherment', > - &Net::SSLeay::NID_basic_constraints => 'CA:FALSE', > - &Net::SSLeay::NID_ext_key_usage => 'serverAuth,clientAuth', > - &Net::SSLeay::NID_subject_alt_name => join(',', map { "DNS:$_" } @$san), > - ); > + Net::SSLeay::P_X509_REQ_add_extensions( > + $req, > + &Net::SSLeay::NID_key_usage => 'digitalSignature,keyEncipherment', > + &Net::SSLeay::NID_basic_constraints => 'CA:FALSE', > + &Net::SSLeay::NID_ext_key_usage => 'serverAuth,clientAuth', > + &Net::SSLeay::NID_subject_alt_name => join(',', map { "DNS:$_" } @$san), > + ) or $cleanup->(1, "Failed to add extensions to CSR\n"); > > $cleanup->(1, "Failed to set public key\n") > if !Net::SSLeay::X509_REQ_set_pubkey($req, $pk); > -- > 2.30.2 > > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > ^ permalink raw reply [flat|nested] 13+ messages in thread
* [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change 2023-03-03 17:57 [pve-devel] [PATCH-SERIES v2 common/manager] Fix SSL Certificate and Key Upload Issues Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 2/2] certificate: fix formatting and whitespace Max Carrara @ 2023-03-03 17:57 ` Max Carrara 2023-03-07 11:07 ` Fabian Grünbichler 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string Max Carrara 3 siblings, 1 reply; 13+ messages in thread From: Max Carrara @ 2023-03-03 17:57 UTC (permalink / raw) To: pve-devel It is now checked whether the new custom SSL certificate actually matches the provided or existing custom key. Also, the new custom certificate and key pair is now validated *before* it is used or replaced with the existing pair. Safety copies are still made; if a pair is currently in use, it is therefore left untouched until the new one is valid. Signed-off-by: Max Carrara <m.carrara@proxmox.com> --- PVE/CertHelpers.pm | 65 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/PVE/CertHelpers.pm b/PVE/CertHelpers.pm index 7e088cb9..826f39bc 100644 --- a/PVE/CertHelpers.pm +++ b/PVE/CertHelpers.pm @@ -53,12 +53,15 @@ sub cert_lock { sub set_cert_files { my ($cert, $key, $path_prefix, $force) = @_; - my ($old_cert, $old_key, $info); + my $info; my $cert_path = "${path_prefix}.pem"; my $cert_path_tmp = "${path_prefix}.pem.old"; + my $cert_path_new = "${path_prefix}.pem.new"; + my $key_path = "${path_prefix}.key"; my $key_path_tmp = "${path_prefix}.key.old"; + my $key_path_new = "${path_prefix}.key.new"; die "Custom certificate file exists but force flag is not set.\n" if !$force && -e $cert_path; @@ -69,26 +72,72 @@ sub set_cert_files { PVE::Tools::file_copy($key_path, $key_path_tmp) if -e $key_path; eval { - PVE::Tools::file_set_contents($cert_path, $cert); - PVE::Tools::file_set_contents($key_path, $key) if $key; + PVE::Tools::file_set_contents($cert_path_new, $cert); + + if ($key) { + PVE::Tools::file_set_contents($key_path_new, $key); + } elsif (-e $key_path) { + PVE::Tools::file_copy($key_path, $key_path_new); + } else { + die "Cannot set custom certificate without key.\n"; + } + + die "Custom certificate does not match provided key.\n" + if !PVE::Certificate::certificate_matches_key($cert_path_new, $key_path_new); + + PVE::Tools::file_copy($cert_path_new, $cert_path); + PVE::Tools::file_copy($key_path_new, $key_path); + $info = PVE::Certificate::get_certificate_info($cert_path); }; my $err = $@; if ($err) { - if (-e $cert_path_tmp && -e $key_path_tmp) { + if (-e $cert_path_new) { + eval { + warn "Removing uploaded certificate...\n"; + unlink $cert_path_new; + }; + warn "$@\n" if $@; + } + if (-e $cert_path_tmp) { eval { - warn "Attempting to restore old certificate files..\n"; + warn "Restoring previous certificate...\n"; PVE::Tools::file_copy($cert_path_tmp, $cert_path); + unlink $cert_path_tmp; + }; + warn "$@\n" if $@; + } + if (-e $key_path_new) { + eval { + if ($key) { + warn "Removing uploaded key...\n"; + } else { + warn "Removing temporary copy of key...\n"; + } + unlink $key_path_new; + }; + warn "$@\n" if $@; + } + if (-e $key_path_tmp) { + eval { + warn "Restoring previous key...\n"; PVE::Tools::file_copy($key_path_tmp, $key_path); + unlink $key_path_tmp; }; warn "$@\n" if $@; } - die "Setting certificate files failed - $err\n" + die "Setting certificate files failed - $err\n"; } - unlink $cert_path_tmp; - unlink $key_path_tmp; + for my $path ( + $cert_path_tmp, + $cert_path_new, + $key_path_tmp, + $key_path_new, + ) { + unlink $path if -e $path; + } return $info; } -- 2.30.2 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change Max Carrara @ 2023-03-07 11:07 ` Fabian Grünbichler 2023-03-07 15:45 ` Max Carrara 0 siblings, 1 reply; 13+ messages in thread From: Fabian Grünbichler @ 2023-03-07 11:07 UTC (permalink / raw) To: Proxmox VE development discussion On March 3, 2023 6:57 pm, Max Carrara wrote: > It is now checked whether the new custom SSL certificate actually > matches the provided or existing custom key. > > Also, the new custom certificate and key pair is now validated > *before* it is used or replaced with the existing pair. Safety copies > are still made; if a pair is currently in use, it is therefore left > untouched until the new one is valid. > > Signed-off-by: Max Carrara <m.carrara@proxmox.com> > --- process related: when sending v3, please indicate here that this patch requires pve-common >= XX , where XX is the actual version that has your pve-common patch, or that it requires a bump+upload of pve-common if no such version exists yet. > PVE/CertHelpers.pm | 65 ++++++++++++++++++++++++++++++++++++++++------ > 1 file changed, 57 insertions(+), 8 deletions(-) > > diff --git a/PVE/CertHelpers.pm b/PVE/CertHelpers.pm > index 7e088cb9..826f39bc 100644 > --- a/PVE/CertHelpers.pm > +++ b/PVE/CertHelpers.pm > @@ -53,12 +53,15 @@ sub cert_lock { > sub set_cert_files { > my ($cert, $key, $path_prefix, $force) = @_; > > - my ($old_cert, $old_key, $info); > + my $info; > > my $cert_path = "${path_prefix}.pem"; > my $cert_path_tmp = "${path_prefix}.pem.old"; > + my $cert_path_new = "${path_prefix}.pem.new"; > + > my $key_path = "${path_prefix}.key"; > my $key_path_tmp = "${path_prefix}.key.old"; > + my $key_path_new = "${path_prefix}.key.new"; > > die "Custom certificate file exists but force flag is not set.\n" > if !$force && -e $cert_path; > @@ -69,26 +72,72 @@ sub set_cert_files { > PVE::Tools::file_copy($key_path, $key_path_tmp) if -e $key_path; > > eval { > - PVE::Tools::file_set_contents($cert_path, $cert); > - PVE::Tools::file_set_contents($key_path, $key) if $key; > + PVE::Tools::file_set_contents($cert_path_new, $cert); > + > + if ($key) { > + PVE::Tools::file_set_contents($key_path_new, $key); > + } elsif (-e $key_path) { > + PVE::Tools::file_copy($key_path, $key_path_new); > + } else { > + die "Cannot set custom certificate without key.\n"; > + } > + > + die "Custom certificate does not match provided key.\n" > + if !PVE::Certificate::certificate_matches_key($cert_path_new, $key_path_new); > + > + PVE::Tools::file_copy($cert_path_new, $cert_path); > + PVE::Tools::file_copy($key_path_new, $key_path); > + > $info = PVE::Certificate::get_certificate_info($cert_path); > }; > my $err = $@; I think I'd actually warn about this error here, else the order is a bit weird: Setting custom certificate files Removing uploaded certificate... Removing uploaded key... Setting certificate files failed - 3385755: Failed to validate private key and certificate and it looks like it removed something and then failed. in my experience, this tends to confuse users, although it is unfortunately a pattern that is sometimes hard to avoid without making the code harder to follow. maybe the removing could even be quiet, unless something goes amiss? or the messages could clearly indicate when error handling starts, e.g. like this: Setting custom certificate files Setting certificate files failed - 3385755: Failed to validate private key and certificate Starting cleanup: Removing uploaded certificate... Removing uploaded key... Cleanup done! > > if ($err) { > - if (-e $cert_path_tmp && -e $key_path_tmp) { > + if (-e $cert_path_new) { > + eval { > + warn "Removing uploaded certificate...\n"; > + unlink $cert_path_new; > + }; unlink doesn't die on failure, it returns false and sets $!, so this error handling needs to be adapted, but see last comment below. > + warn "$@\n" if $@; > + } > + if (-e $cert_path_tmp) { > eval { > - warn "Attempting to restore old certificate files..\n"; > + warn "Restoring previous certificate...\n"; > PVE::Tools::file_copy($cert_path_tmp, $cert_path); > + unlink $cert_path_tmp; > + }; > + warn "$@\n" if $@; same here > + } > + if (-e $key_path_new) { > + eval { > + if ($key) { > + warn "Removing uploaded key...\n"; > + } else { > + warn "Removing temporary copy of key...\n"; > + } > + unlink $key_path_new; > + }; > + warn "$@\n" if $@; and here > + } > + if (-e $key_path_tmp) { > + eval { > + warn "Restoring previous key...\n"; > PVE::Tools::file_copy($key_path_tmp, $key_path); > + unlink $key_path_tmp; > }; > warn "$@\n" if $@; > } > - die "Setting certificate files failed - $err\n" > + die "Setting certificate files failed - $err\n"; > } > > - unlink $cert_path_tmp; > - unlink $key_path_tmp; > + for my $path ( > + $cert_path_tmp, > + $cert_path_new, > + $key_path_tmp, > + $key_path_new, > + ) { > + unlink $path if -e $path; > + } but actually, if you remove all those files here unconditionally, you don't need to remove them higher up in the cleanup code path? like before, you could just restore the .tmp files if they exist (and for the key, only if $key was set?) down here, you could do if (-e $path) { unlink $path or warn "Failed to clean up '$path' - $!"; } or some variant thereof, so that if removing of these temporary files fails, the user gets a warning, but otherwise it's silent. > > return $info; > } > -- > 2.30.2 > > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change 2023-03-07 11:07 ` Fabian Grünbichler @ 2023-03-07 15:45 ` Max Carrara 0 siblings, 0 replies; 13+ messages in thread From: Max Carrara @ 2023-03-07 15:45 UTC (permalink / raw) To: pve-devel On 3/7/23 12:07, Fabian Grünbichler wrote: > On March 3, 2023 6:57 pm, Max Carrara wrote: >> It is now checked whether the new custom SSL certificate actually >> matches the provided or existing custom key. >> >> Also, the new custom certificate and key pair is now validated >> *before* it is used or replaced with the existing pair. Safety copies >> are still made; if a pair is currently in use, it is therefore left >> untouched until the new one is valid. >> >> Signed-off-by: Max Carrara <m.carrara@proxmox.com> >> --- > > process related: when sending v3, please indicate here that this patch requires > pve-common >= XX , where XX is the actual version that has your pve-common > patch, or that it requires a bump+upload of pve-common if no such version exists yet. > > >> PVE/CertHelpers.pm | 65 ++++++++++++++++++++++++++++++++++++++++------ >> 1 file changed, 57 insertions(+), 8 deletions(-) >> >> diff --git a/PVE/CertHelpers.pm b/PVE/CertHelpers.pm >> index 7e088cb9..826f39bc 100644 >> --- a/PVE/CertHelpers.pm >> +++ b/PVE/CertHelpers.pm >> @@ -53,12 +53,15 @@ sub cert_lock { >> sub set_cert_files { >> my ($cert, $key, $path_prefix, $force) = @_; >> >> - my ($old_cert, $old_key, $info); >> + my $info; >> >> my $cert_path = "${path_prefix}.pem"; >> my $cert_path_tmp = "${path_prefix}.pem.old"; >> + my $cert_path_new = "${path_prefix}.pem.new"; >> + >> my $key_path = "${path_prefix}.key"; >> my $key_path_tmp = "${path_prefix}.key.old"; >> + my $key_path_new = "${path_prefix}.key.new"; >> >> die "Custom certificate file exists but force flag is not set.\n" >> if !$force && -e $cert_path; >> @@ -69,26 +72,72 @@ sub set_cert_files { >> PVE::Tools::file_copy($key_path, $key_path_tmp) if -e $key_path; >> >> eval { >> - PVE::Tools::file_set_contents($cert_path, $cert); >> - PVE::Tools::file_set_contents($key_path, $key) if $key; >> + PVE::Tools::file_set_contents($cert_path_new, $cert); >> + >> + if ($key) { >> + PVE::Tools::file_set_contents($key_path_new, $key); >> + } elsif (-e $key_path) { >> + PVE::Tools::file_copy($key_path, $key_path_new); >> + } else { >> + die "Cannot set custom certificate without key.\n"; >> + } >> + >> + die "Custom certificate does not match provided key.\n" >> + if !PVE::Certificate::certificate_matches_key($cert_path_new, $key_path_new); >> + >> + PVE::Tools::file_copy($cert_path_new, $cert_path); >> + PVE::Tools::file_copy($key_path_new, $key_path); >> + >> $info = PVE::Certificate::get_certificate_info($cert_path); >> }; >> my $err = $@; > > I think I'd actually warn about this error here, else the order is a bit weird: > > Setting custom certificate files > Removing uploaded certificate... > Removing uploaded key... > Setting certificate files failed - 3385755: Failed to validate private key and certificate > > and it looks like it removed something and then failed. in my experience, this > tends to confuse users, although it is unfortunately a pattern that is sometimes > hard to avoid without making the code harder to follow. > > maybe the removing could even be quiet, unless something goes amiss? or the > messages could clearly indicate when error handling starts, e.g. like this: > > Setting custom certificate files > Setting certificate files failed - 3385755: Failed to validate private key and certificate > > Starting cleanup: > Removing uploaded certificate... > Removing uploaded key... > Cleanup done! > Very good point, I hadn't thought about that. >> >> if ($err) { >> - if (-e $cert_path_tmp && -e $key_path_tmp) { >> + if (-e $cert_path_new) { >> + eval { >> + warn "Removing uploaded certificate...\n"; >> + unlink $cert_path_new; >> + }; > > unlink doesn't die on failure, it returns false and sets $!, so this error > handling needs to be adapted, but see last comment below. > >> + warn "$@\n" if $@; >> + } >> + if (-e $cert_path_tmp) { >> eval { >> - warn "Attempting to restore old certificate files..\n"; >> + warn "Restoring previous certificate...\n"; >> PVE::Tools::file_copy($cert_path_tmp, $cert_path); >> + unlink $cert_path_tmp; >> + }; >> + warn "$@\n" if $@; > > same here > >> + } >> + if (-e $key_path_new) { >> + eval { >> + if ($key) { >> + warn "Removing uploaded key...\n"; >> + } else { >> + warn "Removing temporary copy of key...\n"; >> + } >> + unlink $key_path_new; >> + }; >> + warn "$@\n" if $@; > > and here > >> + } >> + if (-e $key_path_tmp) { >> + eval { >> + warn "Restoring previous key...\n"; >> PVE::Tools::file_copy($key_path_tmp, $key_path); >> + unlink $key_path_tmp; >> }; >> warn "$@\n" if $@; >> } >> - die "Setting certificate files failed - $err\n" >> + die "Setting certificate files failed - $err\n"; >> } >> >> - unlink $cert_path_tmp; >> - unlink $key_path_tmp; >> + for my $path ( >> + $cert_path_tmp, >> + $cert_path_new, >> + $key_path_tmp, >> + $key_path_new, >> + ) { >> + unlink $path if -e $path; >> + } > > but actually, if you remove all those files here unconditionally, you don't need to remove them > higher up in the cleanup code path? like before, you could just restore the .tmp > files if they exist (and for the key, only if $key was set?) > > down here, you could do > > if (-e $path) { > unlink $path or warn "Failed to clean up '$path' - $!"; > } > > or some variant thereof, so that if removing of these temporary files fails, the > user gets a warning, but otherwise it's silent. > That seems much more elegant to me; thanks for pointing that out. I'll incorporate that in a v3 while also making the rest a little more user-friendly. >> >> return $info; >> } >> -- >> 2.30.2 >> >> >> >> _______________________________________________ >> pve-devel mailing list >> pve-devel@lists.proxmox.com >> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel >> >> >> > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > ^ permalink raw reply [flat|nested] 13+ messages in thread
* [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string 2023-03-03 17:57 [pve-devel] [PATCH-SERIES v2 common/manager] Fix SSL Certificate and Key Upload Issues Max Carrara ` (2 preceding siblings ...) 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change Max Carrara @ 2023-03-03 17:57 ` Max Carrara 2023-03-07 10:53 ` Fabian Grünbichler 2023-03-07 18:33 ` Thomas Lamprecht 3 siblings, 2 replies; 13+ messages in thread From: Max Carrara @ 2023-03-03 17:57 UTC (permalink / raw) To: pve-devel The private key's field is now excluded from the upload form's JSON data if it's considered empty by Ext.js. Prior to this change, the form still sent an empty string if no private key was provided by the user, even though the private key's field is marked as optional in `pve-manager/PVE/API2/Certificates.pm`, causing the JSONSchema validation to fail. Signed-off-by: Max Carrara <m.carrara@proxmox.com> --- www/manager6/node/Certificates.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www/manager6/node/Certificates.js b/www/manager6/node/Certificates.js index 34013b44..84fc12ff 100644 --- a/www/manager6/node/Certificates.js +++ b/www/manager6/node/Certificates.js @@ -173,6 +173,9 @@ Ext.define('PVE.node.CertUpload', { emptyText: gettext('No change'), name: 'key', xtype: 'textarea', + getSubmitValue: function() { + return this.getValue() || null; + }, }, { xtype: 'filebutton', -- 2.30.2 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string Max Carrara @ 2023-03-07 10:53 ` Fabian Grünbichler 2023-03-07 18:33 ` Thomas Lamprecht 1 sibling, 0 replies; 13+ messages in thread From: Fabian Grünbichler @ 2023-03-07 10:53 UTC (permalink / raw) To: Proxmox VE development discussion On March 3, 2023 6:57 pm, Max Carrara wrote: > The private key's field is now excluded from the upload form's > JSON data if it's considered empty by Ext.js. > > Prior to this change, the form still sent an empty string if no > private key was provided by the user, even though the private key's > field is marked as optional in `pve-manager/PVE/API2/Certificates.pm`, > causing the JSONSchema validation to fail. > > Signed-off-by: Max Carrara <m.carrara@proxmox.com> Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> didn't apply for now, as without the additional check it actually makes the pitfall a bit worse ;) > --- > www/manager6/node/Certificates.js | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/www/manager6/node/Certificates.js b/www/manager6/node/Certificates.js > index 34013b44..84fc12ff 100644 > --- a/www/manager6/node/Certificates.js > +++ b/www/manager6/node/Certificates.js > @@ -173,6 +173,9 @@ Ext.define('PVE.node.CertUpload', { > emptyText: gettext('No change'), > name: 'key', > xtype: 'textarea', > + getSubmitValue: function() { > + return this.getValue() || null; > + }, > }, > { > xtype: 'filebutton', > -- > 2.30.2 > > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string Max Carrara 2023-03-07 10:53 ` Fabian Grünbichler @ 2023-03-07 18:33 ` Thomas Lamprecht 2023-03-08 6:34 ` Thomas Lamprecht 1 sibling, 1 reply; 13+ messages in thread From: Thomas Lamprecht @ 2023-03-07 18:33 UTC (permalink / raw) To: Proxmox VE development discussion, Max Carrara Am 03/03/2023 um 18:57 schrieb Max Carrara: > The private key's field is now excluded from the upload form's > JSON data if it's considered empty by Ext.js. nit: Ext JS > > Prior to this change, the form still sent an empty string if no > private key was provided by the user, even though the private key's > field is marked as optional in `pve-manager/PVE/API2/Certificates.pm`, nit: I'd avoid file system paths, they're an internal detail here, if use the api path or just in "... optional in the upload cert API endpoint" > causing the JSONSchema validation to fail. > > Signed-off-by: Max Carrara <m.carrara@proxmox.com> > --- > www/manager6/node/Certificates.js | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/www/manager6/node/Certificates.js b/www/manager6/node/Certificates.js > index 34013b44..84fc12ff 100644 > --- a/www/manager6/node/Certificates.js > +++ b/www/manager6/node/Certificates.js > @@ -173,6 +173,9 @@ Ext.define('PVE.node.CertUpload', { > emptyText: gettext('No change'), > name: 'key', > xtype: 'textarea', > + getSubmitValue: function() { > + return this.getValue() || null; > + }, This works by luck, submitData is a boolean config for the textarea [0], and getSubmitValue has nothing to do with the actual value, but just is the getter for the submitValue bool, which the private actual getSubmitData fn checks [1] [0]: https://docs.sencha.com/extjs/7.0.0/classic/Ext.form.field.TextArea.html#cfg-submitValue [1]: https://docs.sencha.com/extjs/7.0.0/classic/src/Base.js-2.html#Ext.form.field.Base-method-getSubmitData So if, then this would need to return boolean false not null to avoid being confusing - but there's an alternative: wrap this in a inputpanel and use it's onGetValues helper to filter this out, as that allows to also set the hard-coded `force` and `reload` params directly, we could drop the hidden fields too, with also dropping the now finally visible (but ugly) 'hr' autoEl we actually would need fewer lines. I.e., something like: items: { xtype: 'inputpanel', onGetValues: function(values) { values.restart = 1; values.force = 1; if (!values.key) { delete values.key; } return values; }, items: [ .... > }, > { > xtype: 'filebutton', ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string 2023-03-07 18:33 ` Thomas Lamprecht @ 2023-03-08 6:34 ` Thomas Lamprecht 0 siblings, 0 replies; 13+ messages in thread From: Thomas Lamprecht @ 2023-03-08 6:34 UTC (permalink / raw) To: Proxmox VE development discussion, Max Carrara Am 07/03/2023 um 19:33 schrieb Thomas Lamprecht: >> diff --git a/www/manager6/node/Certificates.js b/www/manager6/node/Certificates.js >> index 34013b44..84fc12ff 100644 >> --- a/www/manager6/node/Certificates.js >> +++ b/www/manager6/node/Certificates.js >> @@ -173,6 +173,9 @@ Ext.define('PVE.node.CertUpload', { >> emptyText: gettext('No change'), >> name: 'key', >> xtype: 'textarea', >> + getSubmitValue: function() { >> + return this.getValue() || null; >> + }, > > This works by luck, submitData is a boolean config for the textarea [0], and > getSubmitValue has nothing to do with the actual value, but just is the getter > for the submitValue bool, which the private actual getSubmitData fn checks [1] > Actually I was mistaken here (thanks Dominik for the reminder), here it indeed is the getter for the processed raw value even if there's a boolean config for submitValue - meh. But I'd still go for the inputpanel way in two patches, as that cleans the whole thing up nicer and avoid depending on a bit more internal details, making it more explicit. > [0]: https://docs.sencha.com/extjs/7.0.0/classic/Ext.form.field.TextArea.html#cfg-submitValue > [1]: https://docs.sencha.com/extjs/7.0.0/classic/src/Base.js-2.html#Ext.form.field.Base-method-getSubmitData > ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2023-03-08 6:34 UTC | newest] Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-03-03 17:57 [pve-devel] [PATCH-SERIES v2 common/manager] Fix SSL Certificate and Key Upload Issues Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 1/2] certificate: add subroutine that checks if cert and key match Max Carrara 2023-03-07 10:52 ` [pve-devel] applied: " Fabian Grünbichler 2023-03-07 17:47 ` Thomas Lamprecht 2023-03-03 17:57 ` [pve-devel] [PATCH v2 common 2/2] certificate: fix formatting and whitespace Max Carrara 2023-03-07 10:52 ` [pve-devel] applied: " Fabian Grünbichler 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 1/2] fix #4552: certhelpers: check if custom cert and key match on change Max Carrara 2023-03-07 11:07 ` Fabian Grünbichler 2023-03-07 15:45 ` Max Carrara 2023-03-03 17:57 ` [pve-devel] [PATCH v2 manager 2/2] ui: cert upload: fix private key field sending empty string Max Carrara 2023-03-07 10:53 ` Fabian Grünbichler 2023-03-07 18:33 ` Thomas Lamprecht 2023-03-08 6:34 ` Thomas Lamprecht
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox