[pve-devel] [RFC manager 5/5] add certificates API endpoints

Dominik Csapak d.csapak at proxmox.com
Tue Apr 17 16:27:21 CEST 2018


comments inline

On 04/11/2018 10:08 AM, Fabian Grünbichler wrote:
> to allow retrieval of certificate information, and uploading or removing
> of custom certificate files.
> 
> Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
> ---
>   PVE/API2/Makefile        |   1 +
>   PVE/API2/Certificates.pm | 202 +++++++++++++++++++++++++++++++++++++++++++++++
>   PVE/API2/Nodes.pm        |   8 ++
>   3 files changed, 211 insertions(+)
>   create mode 100644 PVE/API2/Certificates.pm
> 
> diff --git a/PVE/API2/Makefile b/PVE/API2/Makefile
> index 44b9cf7c..9862e498 100644
> --- a/PVE/API2/Makefile
> +++ b/PVE/API2/Makefile
> @@ -14,6 +14,7 @@ PERLSOURCE = 			\
>   	Pool.pm			\
>   	Tasks.pm		\
>   	Network.pm		\
> +	Certificates.pm		\
>   	ACME.pm			\
>   	ACMEAccount.pm		\
>   	NodeConfig.pm		\
> diff --git a/PVE/API2/Certificates.pm b/PVE/API2/Certificates.pm
> new file mode 100644
> index 00000000..6b66ea67
> --- /dev/null
> +++ b/PVE/API2/Certificates.pm
> @@ -0,0 +1,202 @@
> +package PVE::API2::Certificates;
> +
> +use strict;
> +use warnings;
> +
> +use PVE::API2::ACME;
> +use PVE::Certificate;
> +use PVE::CertHelpers;;
> +use PVE::Exception qw(raise_param_exc);
> +use PVE::JSONSchema qw(get_standard_option);
> +use PVE::Tools qw(extract_param file_get_contents file_set_contents);
> +
> +use base qw(PVE::RESTHandler);
> +
> +
> +__PACKAGE__->register_method ({
> +    subclass => "PVE::API2::ACME",
> +    path => 'acme',
> +});
> +
> +__PACKAGE__->register_method ({
> +    name => 'index',
> +    path => '',
> +    method => 'GET',
> +    permissions => { user => 'all' },
> +    description => "Node index.",
> +    parameters => {
> +    	additionalProperties => 0,
> +	properties => {
> +	    node => get_standard_option('pve-node'),
> +	},
> +    },
> +    returns => {
> +	type => 'array',
> +	items => {
> +	    type => "object",
> +	    properties => {},
> +	},
> +	links => [ { rel => 'child', href => "{name}" } ],
> +    },
> +    code => sub {
> +	my ($param) = @_;
> +
> +	return [
> +	    { name => 'acme' },
> +	    { name => 'custom' },
> +	    { name => 'info' },
> +	];
> +    },
> +});
> +
> +__PACKAGE__->register_method ({
> +    name => 'info',
> +    path => 'info',
> +    method => 'GET',
> +    permissions => { user => 'all' },
> +    description => "Get information about node's certificates.",
> +    parameters => {
> +	additionalProperties => 0,
> +	properties => {
> +	    node => get_standard_option('pve-node'),
> +	},
> +    },
> +    returns => {
> +	type => 'object',
> +	properties => {
> +	    clusterca => {
> +		type => 'object',
> +	    },
> +	    custom => {
> +		type => 'object',
> +		optional => 1,
> +	    }
> +	},
> +    },
> +    code => sub {
> +	my ($param) = @_;
> +
> +	my $node_path = "/etc/pve/nodes/$param->{node}";
> +
> +	my $selfsigned_info = eval { PVE::Certificate::get_certificate_info("${node_path}/pve-ssl.pem")};
> +	$selfsigned_info = { error => "$@" } if $@;
> +	my $custom_info = eval { PVE::Certificate::get_certificate_info("${node_path}/pveproxy-ssl.pem")};
> +	$custom_info = { error => "$@" } if $@;
> +	return {
> +	    clusterca => $selfsigned_info,
> +	    custom => $custom_info,
> +	};
> +    },
> +});
> +
> +__PACKAGE__->register_method ({
> +    name => 'upload_custom_cert',
> +    path => 'custom',
> +    method => 'POST',
> +    description => 'Upload or update custom certificate chain and key.',
> +    protected => 1,
> +    parameters => {
> +	additionalProperties => 0,
> +	properties => {
> +	    node => get_standard_option('pve-node'),
> +	    certificates => {
> +		type => 'string',
> +		description => 'PEM encoded certificate (chain).',
> +	    },
> +	    key => {
> +		type => 'string',
> +		description => 'PEM encoded private key.',
> +		optional => 1,
> +	    },
> +	    force => {
> +		type => 'boolean',
> +		description => 'Overwrite existing custom or ACME certificate files.',
> +		optional => 1,
> +		default => 0,
> +	    },
> +	    restart => {
> +		type => 'boolean',
> +		description => 'Restart pveproxy.',
> +		optional => 1,
> +		default => 0,
> +	    },
> +	},
> +    },
> +    returns => {
> +	type => 'object',
> +    },
> +    code => sub {
> +	my ($param) = @_;
> +
> +	my $certs = extract_param($param, 'certificates');
> +	$certs = eval { PVE::Certificate::check_pem($certs, multiple => 1); };
> +	raise_param_exc({ certificates => "Failed PEM format check - $@" }) if $@;
> +
> +	my $key = extract_param($param, 'key');
> +	$key = eval { PVE::Certificate::check_pem($key, label => qr/.*?/); };
> +	raise_param_exc({ key => "Failed PEM format check - $@" }) if $@;
> +	
> +	my $node = extra_param($param, 'node');

should be extract_param

> +	my $cert_prefix = PVE::CertHelpers::cert_path_prefix($node);
> +
> +	my $info;
> +
> +	my $code = sub {
> +	    print "Setting custom certificate files\n";
> +	    $info = PVE::CertHelper::set_cert_files($certs, $key, $cert_prefix, $param->{force});

should be CertHelpers

> +
> +	    if ($param->{restart}) {
> +		print "Restarting pveproxy\n";
> +		PVE::Tools::run_command(['systemctl', 'reload-or-restart', 'pveproxy']);
> +	    }
> +	};
> +
> +	PVE::CertHelpers::cert_lock(10, $code);
> +	die "$@\n" if $@;
> +
> +	return $info;
> +    }});
> +
> +__PACKAGE__->register_method ({
> +    name => 'remove_custom_cert',
> +    path => 'custom',
> +    method => 'DELETE',
> +    description => 'DELETE custom certificate chain and key.',
> +    protected => 1,
> +    parameters => {
> +	additionalProperties => 0,
> +	properties => {
> +	    node => get_standard_option('pve-node'),
> +	    restart => {
> +		type => 'boolean',
> +		description => 'Restart pveproxy.',
> +		optional => 1,
> +		default => 0,
> +	    },
> +	},
> +    },
> +    returns => {
> +	type => 'object',
> +    },
> +    code => sub {
> +	my ($param) = @_;
> +
> +	my $node = extra_param($param, 'node');

extract_param

> +	my $cert_prefix = PVE::CertHelpers::cert_path_prefix($node);
> +
> +	my $code = sub {
> +	    print "Deleting custom certificate files\n";
> +	    unlink "${cert_prefix}.pem";
> +	    unlink "${cert_prefix}.key";
> +
> +	    if ($param->{restart}) {
> +		print "Restarting pveproxy\n";
> +		PVE::Tools::run_command(['systemctl', 'reload-or-restart', 'pveproxy']);
> +	    }
> +	};
> +
> +	PVE::CertHelpers::cert_lock(10, $code);
> +	die "$@\n" if $@;
> +    }});
> +
> +1;
> diff --git a/PVE/API2/Nodes.pm b/PVE/API2/Nodes.pm
> index 42b932cf..25f0e180 100644
> --- a/PVE/API2/Nodes.pm
> +++ b/PVE/API2/Nodes.pm
> @@ -41,6 +41,7 @@ use PVE::API2::APT;
>   use PVE::API2::Ceph;
>   use PVE::API2::Firewall::Host;
>   use PVE::API2::Replication;
> +use PVE::API2::Certificates;
>   use PVE::API2::NodeConfig;
>   use Digest::MD5;
>   use Digest::SHA;
> @@ -119,6 +120,12 @@ __PACKAGE__->register_method ({
>       path => 'replication',
>   });
>   
> +__PACKAGE__->register_method ({
> +    subclass => "PVE::API2::Certificates",
> +    path => 'certificates',
> +});
> +
> +
>   __PACKAGE__->register_method ({
>       subclass => "PVE::API2::NodeConfig",
>       path => 'config',
> @@ -177,6 +184,7 @@ __PACKAGE__->register_method ({
>   	    { name => 'stopall' },
>   	    { name => 'netstat' },
>   	    { name => 'firewall' },
> +	    { name => 'certificates' },
>   	    { name => 'config' },
>   	    ];
>   
> 





More information about the pve-devel mailing list