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

Fabian Grünbichler f.gruenbichler at proxmox.com
Wed Apr 11 10:08:51 CEST 2018


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');
+	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});
+
+	    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');
+	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' },
 	    ];
 
-- 
2.14.2





More information about the pve-devel mailing list