[pve-devel] [PATCH manager 2/2] enable certificate pinning for proxied requests

Dietmar Maurer dietmar at proxmox.com
Thu Nov 17 07:49:47 CET 2016


some comments inline:

> diff --git a/PVE/HTTPServer.pm b/PVE/HTTPServer.pm
> index 1c7d033..5ddbd07 100755
> --- a/PVE/HTTPServer.pm
> +++ b/PVE/HTTPServer.pm
> @@ -105,6 +105,53 @@ sub get_login_formatter {
>      return $info->{func};
>  }
>  
> +my $cert_cache_nodes = {};
> +my $cert_cache_fingerprints = {};
> +sub update_cert_cache {
> +    my ($node) = @_;
> +
> +    if (!defined($node)) {
> +	for $node (keys %$cert_cache_nodes) {
> +	    update_cert_cache($node);

please can we avoid the recursive call here?

my $list = defined($node) ? [ $node ] : [ keys %$cert_cache_nodes ];
foreach my $node (@$list) {
   # do your work here
}


> +	}
> +	return;
> +    }
> +
> +    if (my $old_fp = $cert_cache_nodes->{$node}) {
> +	delete $cert_cache_fingerprints->{$old_fp};

I would do that later, after we successfully read the new data.

> +    }
> +
> +    my $cert_path = "/etc/pve/nodes/$node/pve-ssl.pem";
> +    my $custom_cert_path = "/etc/pve/nodes/$node/pveproxy-ssl.pem";
> +    $cert_path = $custom_cert_path if -f $custom_cert_path;
> +    my $bio = Net::SSLeay::BIO_new_file($cert_path, 'r');
> +    my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
> +    Net::SSLeay::BIO_free($bio);

What if any of these methods raise exceptions?

> +
> +    my $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
> +    return if !defined($fp);
> +
> +    $cert_cache_fingerprints->{$fp} = 1;
> +    $cert_cache_nodes->{$node} = $fp;
> +}
> +
> +sub check_cert_fp {

Please use a better name: check_cert_fingerprints()

> +    my ($fp) = @_;
> +
> +    my $check = sub {
> +	for my $expected (keys %$cert_cache_fingerprints) {
> +	    return 1 if $fp eq $expected;
> +	}
> +	return 0;
> +    };
> +
> +    return 1 if &$check();
> +
> +    # refresh cache and retry once
> +    update_cert_cache();
> +    return &$check();
> +}
> +
>  # server implementation
>  
>  sub log_request {
> @@ -601,6 +648,28 @@ sub proxy_request {
>  	    }
>  	}
>  
> +	my $tls = {
> +	    # TLS 1.x only, with certificate pinning
> +	    method => 'any',
> +	    sslv2 => 0,
> +	    sslv3 => 0,
> +	    verify => 1,
> +	    verify_cb => sub {
> +		my (undef, undef, undef, $depth, undef, undef, $cert) = @_;
> +		# we don't care about intermediate or root certificates
> +		return 0 if $depth != 0;
> +
> +		# get fingerprint of server certificate
> +		my $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
> +		return 0 if !defined($fp); # error
> +		return check_cert_fp($fp); # check against cache of pinned FPs

I would merge this code with check_cert_fp, so that we have a
single method (maybe call it 'tls_verify_callback'?

> +	    },
> +	};
> +
> +	# load and cache cert fingerprint if first time we proxy to this node
> +	update_cert_cache($node)
> +	    if defined($node) && !defined($cert_cache_nodes->{$node});
> +
>  	my $w; $w = http_request(
>  	    $method => $target,
>  	    headers => $headers,
> @@ -609,7 +678,7 @@ sub proxy_request {
>  	    proxy => undef, # avoid use of $ENV{HTTP_PROXY}
>  	    keepalive => $keep_alive,
>  	    body => $content,
> -	    tls_ctx => $self->{tls_ctx},
> +	    tls_ctx => AnyEvent::TLS->new(%{$tls}),
>  	    sub {
>  		my ($body, $hdr) = @_;
>  
> -- 
> 2.1.4
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel at pve.proxmox.com
> http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel




More information about the pve-devel mailing list