[pve-devel] [Patch V3 manager 4/8] Add function that selects the desired plugin.

2020-04-15 Thread Wolfgang Link
These functions also extract the data required for the plugin.

Signed-off-by: Wolfgang Link 
---
 PVE/API2/ACME.pm | 27 +++
 1 file changed, 27 insertions(+)

diff --git a/PVE/API2/ACME.pm b/PVE/API2/ACME.pm
index e69a563b..7bb3ab95 100644
--- a/PVE/API2/ACME.pm
+++ b/PVE/API2/ACME.pm
@@ -45,6 +45,33 @@ __PACKAGE__->register_method ({
];
 }});
 
+my $get_plugin_type = sub {
+my ($domain, $acme_node_config) = @_;
+
+my $plugin;
+my $alias;
+foreach my $index (keys %$acme_node_config) {
+   next if $index eq 'domains';
+
+   my $domain_config = $acme_node_config->{$index};
+   if (defined($domain_config->{domain}) &&
+   $domain_config->{domain} eq $domain) {
+   $plugin = $domain_config->{plugin};
+   $alias = $domain_config->{alias};
+   last;
+   }
+}
+return "standalone" if !defined($plugin);
+
+my $plugin_conf = PVE::API2::ACMEPlugin::load_config();
+my $data = $plugin_conf->{ids}->{$plugin};
+my $plugin_type = $data->{type};
+
+$data->{alias} = $alias;
+
+return ($plugin_type, $data);
+};
+
 my $order_certificate = sub {
 my ($acme, $domains) = @_;
 print "Placing ACME order\n";
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 manager 5/8] Adapt acme node config parser and rename the function.

2020-04-15 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 PVE/API2/ACME.pm  | 26 ++
 PVE/NodeConfig.pm | 44 ++--
 2 files changed, 48 insertions(+), 22 deletions(-)

diff --git a/PVE/API2/ACME.pm b/PVE/API2/ACME.pm
index 7bb3ab95..d215739b 100644
--- a/PVE/API2/ACME.pm
+++ b/PVE/API2/ACME.pm
@@ -73,9 +73,9 @@ my $get_plugin_type = sub {
 };
 
 my $order_certificate = sub {
-my ($acme, $domains) = @_;
+my ($acme, $acme_node_config) = @_;
 print "Placing ACME order\n";
-my ($order_url, $order) = $acme->new_order($domains);
+my ($order_url, $order) = $acme->new_order($acme_node_config->{domains});
 print "Order URL: $order_url\n";
 my $index = 0;
 for my $auth_url (@{$order->{authorizations}}) {
@@ -213,11 +213,9 @@ __PACKAGE__->register_method ({
if !$param->{force} && -e "${cert_prefix}.pem";
 
my $node_config = PVE::NodeConfig::load_config($node);
-   raise("ACME settings in node configuration are missing!", 400)
-   if !$node_config || !$node_config->{acme};
-   my $acme_node_config = 
PVE::NodeConfig::parse_acme($node_config->{acme});
+   my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
raise("ACME domain list in node configuration is missing!", 400)
-   if !$acme_node_config;
+   if !$acme_node_config || !$acme_node_config->{domains};
 
my $rpcenv = PVE::RPCEnvironment::get();
 
@@ -235,7 +233,7 @@ __PACKAGE__->register_method ({
print "Loading ACME account details\n";
$acme->load();
 
-   my ($cert, $key) = $order_certificate->($acme, 
$acme_node_config->{domains});
+   my ($cert, $key) = $order_certificate->($acme, $acme_node_config);
 
my $code = sub {
print "Setting pveproxy certificate and key\n";
@@ -287,11 +285,9 @@ __PACKAGE__->register_method ({
if !$expires_soon && !$param->{force};
 
my $node_config = PVE::NodeConfig::load_config($node);
-   raise("ACME settings in node configuration are missing!", 400)
-   if !$node_config || !$node_config->{acme};
-   my $acme_node_config = 
PVE::NodeConfig::parse_acme($node_config->{acme});
+   my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
raise("ACME domain list in node configuration is missing!", 400)
-   if !$acme_node_config;
+   if !$acme_node_config || !$acme_node_config->{domains};
 
my $rpcenv = PVE::RPCEnvironment::get();
 
@@ -311,7 +307,7 @@ __PACKAGE__->register_method ({
print "Loading ACME account details\n";
$acme->load();
 
-   my ($cert, $key) = $order_certificate->($acme, 
$acme_node_config->{domains});
+   my ($cert, $key) = $order_certificate->($acme, $acme_node_config);
 
my $code = sub {
print "Setting pveproxy certificate and key\n";
@@ -353,11 +349,9 @@ __PACKAGE__->register_method ({
my $cert_prefix = PVE::CertHelpers::cert_path_prefix($node);
 
my $node_config = PVE::NodeConfig::load_config($node);
-   raise("ACME settings in node configuration are missing!", 400)
-   if !$node_config || !$node_config->{acme};
-   my $acme_node_config = 
PVE::NodeConfig::parse_acme($node_config->{acme});
+   my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
raise("ACME domain list in node configuration is missing!", 400)
-   if !$acme_node_config;
+   if !$acme_node_config || !$acme_node_config->{domains};
 
my $rpcenv = PVE::RPCEnvironment::get();
 
diff --git a/PVE/NodeConfig.pm b/PVE/NodeConfig.pm
index 6ea2dac1..ae2f916c 100644
--- a/PVE/NodeConfig.pm
+++ b/PVE/NodeConfig.pm
@@ -227,18 +227,50 @@ sub write_node_config {
 return $raw;
 }
 
-sub parse_acme {
+sub get_acme_conf {
 my ($data, $noerr) = @_;
 
 $data //= '';
 
-my $res = eval { PVE::JSONSchema::parse_property_string($acmedesc, $data); 
};
-if ($@) {
-   return undef if $noerr;
-   die $@;
+my $res = {};
+
+if (defined($data->{acme})) {
+   $res->{0} = eval {
+   PVE::JSONSchema::parse_property_string($acmedesc, $data->{acme});
+   };
+   if ($@) {
+   return undef if $noerr;
+   die $@;
+   }
 }
+$res->{0}->{account} = $res->{0}->{account} // "default";
+my $domainlist = [];
+
+for my $index (0..$MAXDOMAINS) {
+   my $domain_rec = $data->{"acme_additional_domain$index"};
+   next if !defined($domain_rec);
+
+   # index = 0 is used by acme see above
+   $res->{($index+1)} = eval {
+   PVE::JSONSchema::parse_proper

[pve-devel] [Patch V3 manager 8/8] Add libproxmox-acme to the dependencies.

2020-04-15 Thread Wolfgang Link
It is a build dependency as it is needed for the man generator.

Signed-off-by: Wolfgang Link 
---
 debian/control | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/debian/control b/debian/control
index 5df624df..4b0de491 100644
--- a/debian/control
+++ b/debian/control
@@ -9,8 +9,8 @@ Build-Depends: debhelper (>= 11~),
libpve-access-control (>= 5.1-5),
libpve-cluster-perl,
libpve-cluster-api-perl,
-   libpve-common-perl,
   libproxmox-acme-perl,
+  libpve-common-perl,
libpve-guest-common-perl (>= 3.0-7~),
libpve-http-server-perl (>= 2.0-12),
libpve-storage-perl (>= 5.0-35),
@@ -47,9 +47,9 @@ Depends: apt-transport-https | apt (>= 1.5~),
  libnet-dns-perl,
  libpve-access-control (>= 6.0-6),
  libpve-cluster-perl,
+libproxmox-acme-perl,
  libpve-cluster-api-perl,
  libpve-common-perl (>= 6.0-11),
-libproxmox-acme-perl,
  libpve-guest-common-perl (>= 3.0-3~),
  libpve-http-server-perl (>= 3.0-4),
  libpve-storage-perl (>= 6.0-1),
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 08/13] Refactor extract_callenge for code reuse.

2020-04-15 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME/Challenge.pm | 47 +++
 1 file changed, 47 insertions(+)

diff --git a/src/PVE/ACME/Challenge.pm b/src/PVE/ACME/Challenge.pm
index 40d32b6..649c228 100644
--- a/src/PVE/ACME/Challenge.pm
+++ b/src/PVE/ACME/Challenge.pm
@@ -3,10 +3,57 @@ package PVE::ACME::Challenge;
 use strict;
 use warnings;
 
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::SectionConfig);
+
+my $defaultData = {
+additionalProperties => 0,
+propertyList => {
+   id => {
+   description => "ACME Plugin ID name",
+   type => 'string',
+   },
+   type => {
+   description => "ACME challenge type.",
+   type => 'string',
+   },
+   disable => {
+   description => "Flag to disable the config.",
+   type => 'boolean',
+   optional => 1,
+   },
+   nodes => get_standard_option('pve-node-list', { optional => 1 }),
+},
+};
+
+sub private {
+return $defaultData;
+}
+
 sub supported_challenge_types {
 return {};
 }
 
+sub extract_challenge {
+my ($self, $challenges, $c_type) = @_;
+
+die "no challenges defined\n" if !$challenges;
+die "no challenge type is defined \n" if !$c_type;
+
+my $tmp_challenges = [ grep {$_->{type} eq $c_type} @$challenges ];
+die "no $c_type challenge defined in authorization\n"
+   if ! scalar $tmp_challenges;
+
+my $challenge = $tmp_challenges->[0];
+
+return $challenge;
+}
+
+sub get_subplugins {
+return [];
+}
+
 sub setup {
 my ($class, $acme, $authorization) = @_;
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 09/13] Create the plugin config.

2020-04-15 Thread Wolfgang Link
At the moment, Proxmox has two different configurations that require different 
properties.
DNSChallange requires credentials for the DNSAPI.
Standalone has no settings because Letsencrypt only supports port 80 with the 
http-01 challenge.
This configuration is registered in the pve-manager.

Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME/StandAlone.pm | 21 +
 1 file changed, 21 insertions(+)

diff --git a/src/PVE/ACME/StandAlone.pm b/src/PVE/ACME/StandAlone.pm
index f48d638..0b4aaae 100644
--- a/src/PVE/ACME/StandAlone.pm
+++ b/src/PVE/ACME/StandAlone.pm
@@ -12,6 +12,27 @@ sub supported_challenge_types {
 return { 'http-01' => 1 };
 }
 
+sub type {
+return 'standalone';
+}
+
+sub properties {
+return {};
+}
+
+sub options {
+return {
+   nodes => { optional => 1 },
+   disable => { optional => 1 },
+};
+}
+
+sub extract_challenge {
+my ($self, $challenge) = @_;
+
+return PVE::ACME::Challenge->extract_challenge($challenge, 'http-01');
+}
+
 sub setup {
 my ($class, $acme, $authorization) = @_;
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] (no subject)

2020-04-15 Thread Wolfgang Link
>From Wolfgang Link  # This line is ignored.
From: Wolfgang Link 
Reply-To: 
Subject:  RFC for ACME DNS Challenge V3
In-Reply-To: 

The acme_sh project is used as a DNS API plugin system.
So we can reuse the already defiend plugins.
It is used as subplugins.

The acme.sh script is replaced by proxmox-acme,
which contains the function required to operate the DNSAPI plug-ins.

The login information is saved in the file plugin.cfg.
The values are encoded in base64 and transferred directly to proxmox-acme.
There they are decoded again

The DNSAPI plugin credentials are not standardized, so each plugin expects 
different parameters.

These patches are only tested against the OVH API because of missing 
alternative possibilities.

The V3 is mainly based on V2, but has the improvements of Fabian's feedback.
For more information see  below.

Build conflicts arise due to the code movements.
The prerequisite for this series is the installation of Curl.
For this series you have to create the deb packages pve-common, pve-cluster and 
proxmox-acme.
Then apply these packages and you can now build and install the pve-manager 
package.

The GUI works at the moment only with the standalone Plugin(HTTP Challenge).

For the alias mode a CNAME record is needed
_acme-challenge...   CNAME   _acme-challenge.

Steps to test.

1.) pvenode acme account register default 
2.) pvenode acme plugin add   --data  
3.) pvenode config set --acme 
domain=,plugin=[,alias=]
4.) pvenode acme cert order

[Patch V3 cluster] Add ACME plugin config file to cluster files
V2 -> V3 Change path of plugin conf

[Patch V2 acme 01/13] Add Debian Buildsystem config
V2 -> V3 Add a hint that acme.sh is GPL-3 

[Patch V3 common] Move the code to proxmox-acme and add a dependency
V2 -> V3 No Change

note(Fabian G.): this one requires a breaks+replaces on the other side 
(proxmox-acme), and a version bump here (so that proxmox-acme can have 
an appropriate versioned depends).

since the other two pve-common are independent I already applied them - 
otherwise this one should have probably been 3/3 

[Patch V3 manager 1/8] Use the plugin architecture.
V2 -> V3 Make proxmox-acme independent of PVE code.
 The data used by the plugins is collected in this patch.
 
[Patch V2 acme 02/13] Copy the needed function form acme.sh
V2 -> V3 No Change

[Patch V3 manager 2/8] Extend node config in the acme section.

V2 -> V3 Start Additional Domains with 0 and do not change acme desc.

[Patch V2 acme 03/13] Remove unnecessary Code and fixes.
V2 -> V3 No Change

[Patch V3 manager 3/8] Remove unused code
V2 -> V3 No Change

[Patch V3 manager 4/8] Add function that selects the desired plugin.
New

[Patch V2 acme 04/13] Add funtion to set DNSAPI variable
V2 -> V3 Decode values
 Use a different splitting method because cutting with base64 does not 
work.

[Patch V3 manager 5/8] Adapt acme node config parser and rename the
V2 -> V3 Rename function.
 Remove the conversion from the old Acme configuration as it is no 
longer required.

[Patch V2 acme 05/13] Implement feature setup and teardown
V2 -> V3 change path of dnsapi

[Patch V3 manager 6/8] Add libproxmox-acme-perl to pveversion
V2 -> V3 No Change

Note: needs a version dependency?

[Patch V2 acme 06/13] Add submodule acme.sh for DNS plugins
V2 -> V3 change path

[Patch V3 manager 7/8] Create ACME Plugin config.
V2 -> V3 Move the code from proxmox-acme here to make it independent.
 Add base64 encoding for plugin data.

[Patch V2 acme 07/13] Move code from pve-common
V2 -> V3 change path in the Makefile

[Patch V3 manager 8/8] Add libproxmox-acme to the dependencies.
V2 -> V3 No Change

Note: needs a version dependency?

[Patch V2 acme 08/13] Refactor extract_callenge for code reuse.
V2 -> V3 Move in Challange.pm 

[Patch V2 acme 09/13] Create the plugin config.
V2 -> V3 No Change

[Patch V2 acme 10/13] Use-the-caller-s-data-instead-of-extracting-it-yours
New

[Patch V2 acme 11/13] Add DNSChallenge Plugin
V2 -> V3 Use the caller's data to extract the data themselves.

[Patch V2 acme 12/13] Add debug mode
V2 -> V3 No Change

[Patch V2 acme 13/13] Implement function to resolve all subplugins
New

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 04/13] Add funtion to set DNSAPI variable

2020-04-15 Thread Wolfgang Link
acme.sh DNS plugins expect a configuration in which the login information
is stored.
We pass the credentials with the command.
This function supports the expected behavior of the plugins.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 19 +++
 1 file changed, 19 insertions(+)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index 662c39a..b4e01d8 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -568,3 +568,22 @@ _source_plugin_config() {
   return
 }
 
+# Proxmox implementation to inject the DNSAPI variables
+_load_plugin_config() {
+tmp_str="${plugin_conf_string//[^,]}"
+index="$(_math ${#tmp_str} + 1)"
+while [ "$index" -gt "0" ]
+do
+   field=$(_getfield $plugin_conf_string "$index" ",")
+   ADDR=(${field/=/ })
+   key="${ADDR[0]}"
+   value="${ADDR[1]}"
+
+   # decode base64 encoded values
+   value=$(echo $value | /usr/bin/openssl base64 -d -A)
+
+   # acme.sh uses eval insted of export
+   export "$key"="$value"
+   index="$(_math "$index" - 1)"
+done
+}
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 manager 1/8] Use the plugin architecture.

2020-04-15 Thread Wolfgang Link
And remove the call of standalone plugin directly.
Prepare all necessary data for the plugin.

Signed-off-by: Wolfgang Link 
---
 PVE/API2/ACME.pm | 35 +++
 1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/PVE/API2/ACME.pm b/PVE/API2/ACME.pm
index b1bb6261..e69a563b 100644
--- a/PVE/API2/ACME.pm
+++ b/PVE/API2/ACME.pm
@@ -4,7 +4,6 @@ use strict;
 use warnings;
 
 use PVE::ACME;
-use PVE::ACME::StandAlone;
 use PVE::CertHelpers;
 use PVE::Certificate;
 use PVE::Exception qw(raise raise_param_exc);
@@ -51,20 +50,39 @@ my $order_certificate = sub {
 print "Placing ACME order\n";
 my ($order_url, $order) = $acme->new_order($domains);
 print "Order URL: $order_url\n";
+my $index = 0;
 for my $auth_url (@{$order->{authorizations}}) {
print "\nGetting authorization details from '$auth_url'\n";
my $auth = $acme->get_authorization($auth_url);
+   my $domain = $auth->{identifier}->{value};
if ($auth->{status} eq 'valid') {
-   print "... already validated!\n";
+   $domain = %{@{$order->{identifiers}}[$index]}{value};
+   print "$domain is already validated!\n";
} else {
-   print "... pending!\n";
-   print "Setting up webserver\n";
-   my $validation = eval { PVE::ACME::StandAlone->setup($acme, $auth) 
};
-   die "failed setting up webserver - $@\n" if $@;
+   print "The validation for $domain is pending!\n";
+
+   my ($plugin_type, $plugin_config) = &$get_plugin_type($domain, 
$acme_node_config);
+
+   my $plugin = PVE::ACME::Challenge->lookup($plugin_type);
+
+   my $challenge = $plugin->extract_challenge($auth->{challenges});
+   my $key_auth = $acme->key_authorization($challenge->{token});
+   my $data = {
+   key_authorization => $key_auth,
+   token => $challenge->{token},
+   url => $challenge->{url},
+   domain => $domain,
+   };
+
+   foreach my $key (keys %$plugin_config) {
+   $data->{plugin}->{$key} = $plugin_config->{$key};
+   }
+
+   $plugin->setup($data);
 
print "Triggering validation\n";
eval {
-   $acme->request_challenge_validation($validation->{url}, 
$validation->{key_auth});
+   $acme->request_challenge_validation($data->{url}, 
$data->{key_authorization});
print "Sleeping for 5 seconds\n";
sleep 5;
while (1) {
@@ -81,10 +99,11 @@ my $order_certificate = sub {
}
};
my $err = $@;
-   eval { $validation->teardown() };
+   eval { $plugin->teardown($data) };
warn "$@\n" if $@;
die $err if $err;
}
+   $index++;
 }
 print "\nAll domains validated!\n";
 print "\nCreating CSR\n";
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 05/13] Implement feature setup and teardown functionality.

2020-04-15 Thread Wolfgang Link
We use these functions to add and remove a txt record via the dnsapi.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 68 
 1 file changed, 68 insertions(+)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index b4e01d8..ff9fec8 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -1,5 +1,12 @@
 #!/usr/bin/env sh
 
+VER=0.9
+
+PROJECT_NAME="ProxmoxACME"
+
+USER_AGENT="$PROJECT_NAME/$VER"
+
+DNS_PLUGIN_PATH="/usr/share/proxmox-acme/dnsapi"
 HTTP_HEADER="$(mktemp)"
 
 _base64() {
@@ -587,3 +594,64 @@ _load_plugin_config() {
index="$(_math "$index" - 1)"
 done
 }
+
+# call setup and teardown direct
+# the parameter must be set in the correct order
+# $1  DNS Plugin name
+# $2  Fully Qualified Domain Name
+# $3  value for TXT record
+# $4  DNS plugin auth and config parameter separated by ","
+
+setup() {
+  dns_plugin="dns_$1"
+  dns_plugin_path="${DNS_PLUGIN_PATH}/${dns_plugin}.sh"
+  fqdn="_acme-challenge.$2"
+  txtvalue=$3
+  plugin_conf_string=$4
+
+  _load_plugin_config
+
+  if ! . "$dns_plugin_path"; then
+_err "Load file $dns_plugin error."
+return 1
+  fi
+
+  addcommand="${dns_plugin}_add"
+  if ! _exists "$addcommand"; then
+_err "It seems that your api file is not correct, it must have a function 
named: $addcommand"
+return 1
+  fi
+
+  if ! $addcommand "$fqdn" "$txtvalue"; then
+_err "Error add txt for domain:$fulldomain"
+return 1
+  fi
+}
+
+teardown() {
+  dns_plugin="dns_$1"
+  dns_plugin_path="${DNS_PLUGIN_PATH}/${dns_plugin}.sh"
+  fqdn="_acme-challenge.$2"
+  txtvalue=$3
+  plugin_conf_string=$4
+
+  _load_plugin_config
+
+  if ! . "$dns_plugin_path"; then
+_err "Load file $dns_plugin error."
+return 1
+  fi
+
+  rmcommand="${dns_plugin}_rm"
+  if ! _exists "$rmcommand"; then
+_err "It seems that your api file is not correct, it must have a function 
named: $rmcommand"
+return 1
+  fi
+
+  if ! $rmcommand "$fqdn" "$txtvalue"; then
+_err "Error add txt for domain:$fulldomain"
+return 1
+  fi
+}
+
+"$@"
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 01/13] Add Debian Buildsystem config

2020-04-15 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 .gitignore   |  5 +
 Makefile | 40 
 debian/changelog |  5 +
 debian/compat|  1 +
 debian/control   | 17 +
 debian/copyright | 22 ++
 debian/lintian-overrides |  1 +
 debian/rules |  7 +++
 8 files changed, 98 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Makefile
 create mode 100644 debian/changelog
 create mode 100644 debian/compat
 create mode 100644 debian/control
 create mode 100644 debian/copyright
 create mode 100644 debian/lintian-overrides
 create mode 100755 debian/rules

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000..334442b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.deb
+*.dsc
+*.buildinfo
+*.changes
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 000..5ed0522
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+include /usr/share/dpkg/pkg-info.mk
+
+PACKAGE=libproxmox-acme-perl
+
+BUILDDIR ?= ${PACKAGE}-${DEB_VERSION_UPSTREAM}
+GITVERSION:=$(shell git rev-parse HEAD)
+
+DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_all.deb
+DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc
+
+all: $(DEB)
+
+$(BUILDDIR): src debian
+   rm -rf $(BUILDDIR)
+   rsync -a src/ debian $(BUILDDIR)
+   # remove if repository exists
+   # echo "git clone git://git.proxmox.com/git/proxmox-acme\\ngit checkout 
$(GITVERSION)" > $(BUILDDIR)/debian/SOURCE
+
+.PHONY: deb
+deb: $(DEB)
+$(DEB): $(BUILDDIR)
+   cd $(BUILDDIR); dpkg-buildpackage -b -us -uc
+   lintian $(DEB)
+
+.PHONY: dsc
+dsc: ${DSC}
+${DSC}: ${BUILDDIR}
+   cd ${BUILDDIR}; dpkg-buildpackage -S -us -uc -d
+   lintian ${DSC}
+
+dinstall: $(DEB)
+   dpkg -i $(DEB)
+
+.PHONY: clean
+clean:
+   rm -rf $(BUILDDIR) *.deb *.buildinfo *.changes *.dsc *.tar.gz
+
+.PHONY: upload
+upload: ${DEB}
+   tar cf - ${DEB}|ssh -X repo...@repo.proxmox.com -- upload --product 
pve,pmg --dist buster --arch ${DEB_BUILD_ARCH}
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 000..8edee45
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+libproxmox-acme-perl (1.0.0-1+pvetest1) unstable; urgency=medium
+
+  * Initial 1.0.0 package
+
+ -- Proxmox Support Team   Wed, 06 Nov 2019 09:14:59 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 000..f599e28
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+10
diff --git a/debian/control b/debian/control
new file mode 100644
index 000..87ba731
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,17 @@
+Source: libproxmox-acme-perl
+Section: admin
+Priority: optional
+Maintainer: Proxmox Support Team 
+Build-Depends: debhelper (>= 10),
+   dh-systemd,
+Standards-Version: 1.0.0
+Homepage: https://www.proxmox.com
+
+Package: libproxmox-acme-perl
+Architecture: all
+Description: easy and small shell script to automatically issue
+ and renew the free certificates from Let's Encrypt.
+Depends: curl (>= 7.64.0-1),
+coreutils (>= 8.30-1),
+sed (>= 4.7-1)
+Recommends: idn
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 000..bbacf4e
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,22 @@
+Copyright (C) 2010-2019 Proxmox Server Solutions GmbH
+
+This software is written by Proxmox Server Solutions GmbH 
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+The part DNSAPI of the project acme.sh[1] which is under GLP-3[2]
+was used for this packet.
+
+1.) https://github.com/acmesh-official/acme.sh
+2.) https://www.gnu.org/licenses/gpl-3.0.txt
diff --git a/debian/lintian-overrides b/debian/lintian-overrides
new file mode 100644
index 000..4f3e835
--- /dev/null
+++ b/debian/lintian-overrides
@@ -0,0 +1 @@
+libproxmox-acme-perl: script-not-executable
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 000..f00dbc2
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+# See debhelper(7) (uncomment to enable)
+# output every command that modifies files on the build system.
+#export DH_VERBOSE = 1
+
+%:
+   dh $@
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 manager 2/8] Extend node config in the acme section.

2020-04-15 Thread Wolfgang Link
Allow additional domains with different sub-plugins,
However, only one domain per additional entry is permitted.

Signed-off-by: Wolfgang Link 
---
 PVE/NodeConfig.pm | 38 ++
 1 file changed, 38 insertions(+)

diff --git a/PVE/NodeConfig.pm b/PVE/NodeConfig.pm
index 7a663f46..560da116 100644
--- a/PVE/NodeConfig.pm
+++ b/PVE/NodeConfig.pm
@@ -6,6 +6,10 @@ use warnings;
 use PVE::CertHelpers;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Tools qw(file_get_contents file_set_contents lock_file);
+use PVE::ACME;
+
+# register up to 20 domain names
+my $MAXDOMAINS = 20;
 
 my $node_config_lock = '/var/lock/pvenode.lock';
 
@@ -77,6 +81,29 @@ my $confdesc = {
 },
 };
 
+my $acme_additional_desc = {
+domain => {
+   type => 'string',
+   format => 'pve-acme-domain',
+   format_description => 'domain',
+   description => 'domain for this node\'s ACME certificate',
+},
+plugin => {
+   type => 'string',
+   format => 'pve-configid',
+   description => 'The plugin ID, default is standalone http',
+   format_description => 'name of the plugin configuration',
+},
+alias => {
+   type => 'string',
+   format => 'pve-acme-domain',
+   format_description => 'domain',
+   description => 'Alias for the Domain to verify ACME Challenge over DNS',
+   optional => 1,
+},
+};
+PVE::JSONSchema::register_format('pve-acme-additional-node-conf', 
$acme_additional_desc);
+
 my $acmedesc = {
 account => get_standard_option('pve-acme-account-name'),
 domains => {
@@ -84,6 +111,7 @@ my $acmedesc = {
format => 'pve-acme-domain-list',
format_description => 'domain[;domain;...]',
description => 'List of domains for this node\'s ACME certificate',
+   optional => 1,
 },
 };
 PVE::JSONSchema::register_format('pve-acme-node-conf', $acmedesc);
@@ -95,6 +123,15 @@ $confdesc->{acme} = {
 optional => 1,
 };
 
+for my $i (0..$MAXDOMAINS) {
+$confdesc->{"acme_additional_domain$i"} = {
+   type => 'string',
+   description => 'ACME additional Domain',
+   format => $acme_additional_desc,
+   optional => 1,
+};
+};
+
 sub check_type {
 my ($key, $value) = @_;
 
@@ -214,6 +251,7 @@ sub print_acme {
 }
 
 sub get_nodeconfig_schema {
+
 return $confdesc;
 }
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 cluster] Add ACME plugin config file to cluster files

2020-04-15 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 data/PVE/Cluster.pm | 1 +
 data/src/status.c   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/data/PVE/Cluster.pm b/data/PVE/Cluster.pm
index 068d626..56704ec 100644
--- a/data/PVE/Cluster.pm
+++ b/data/PVE/Cluster.pm
@@ -54,6 +54,7 @@ my $observed = {
 'priv/shadow.cfg' => 1,
 'priv/tfa.cfg' => 1,
 'priv/token.cfg' => 1,
+'priv/acme/plugins.cfg' => 1,
 '/qemu-server/' => 1,
 '/openvz/' => 1,
 '/lxc/' => 1,
diff --git a/data/src/status.c b/data/src/status.c
index 5e0cebe..0820533 100644
--- a/data/src/status.c
+++ b/data/src/status.c
@@ -83,6 +83,7 @@ static memdb_change_t memdb_change_array[] = {
{ .path = "user.cfg" },
{ .path = "domains.cfg" },
{ .path = "priv/shadow.cfg" },
+   { .path = "priv/acme/plugins.cfg" },
{ .path = "priv/tfa.cfg" },
{ .path = "priv/token.cfg" },
{ .path = "datacenter.cfg" },
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 11/13] Add DNSChallenge Plugin

2020-04-15 Thread Wolfgang Link
This plugin calls the custom script acme.sh and uses the implementation of the 
DNS API.

Signed-off-by: Wolfgang Link 
---
 debian/control   |   3 +-
 src/Makefile |   1 +
 src/PVE/ACME/DNSChallenge.pm | 198 +++
 3 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/ACME/DNSChallenge.pm

diff --git a/debian/control b/debian/control
index 87ba731..bb85c98 100644
--- a/debian/control
+++ b/debian/control
@@ -13,5 +13,6 @@ Description: easy and small shell script to automatically 
issue
  and renew the free certificates from Let's Encrypt.
 Depends: curl (>= 7.64.0-1),
 coreutils (>= 8.30-1),
-sed (>= 4.7-1)
+sed (>= 4.7-1),
+libpve-common-perl,
 Recommends: idn
diff --git a/src/Makefile b/src/Makefile
index b65e330..aff47b5 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -107,6 +107,7 @@ LIB_SOURCES = \
ACME.pm \
ACME/Challenge.pm \
ACME/StandAlone.pm \
+   ACME/DNSChallenge.pm \
 
 all:
 
diff --git a/src/PVE/ACME/DNSChallenge.pm b/src/PVE/ACME/DNSChallenge.pm
new file mode 100644
index 000..7af442e
--- /dev/null
+++ b/src/PVE/ACME/DNSChallenge.pm
@@ -0,0 +1,198 @@
+package PVE::ACME::DNSChallenge;
+
+use strict;
+use warnings;
+
+use Digest::SHA qw(sha256);
+use PVE::Tools;
+
+use base qw(PVE::ACME::Challenge);
+
+my $ACME_PATH = '/usr/share/proxmox-acme/proxmox-acme';
+
+sub supported_challenge_types {
+return { 'dns-01' => 1 };
+}
+
+sub type {
+return 'dns';
+}
+
+my $api_name_list = [
+'acmedns',
+'acmeproxy',
+'active24',
+'ad',
+'ali',
+'autodns',
+'aws',
+'azure',
+'cf',
+'clouddns',
+'cloudns',
+'cn',
+'conoha',
+'constellix',
+'cx',
+'cyon',
+'da',
+'ddnss',
+'desec',
+'dgon',
+'dnsimple',
+'do',
+'doapi',
+'domeneshop',
+'dp',
+'dpi',
+'dreamhost',
+'duckdns',
+'durabledns',
+'dyn',
+'dynu',
+'dynv6',
+'easydns',
+'euserv',
+'exoscale',
+'freedns',
+'gandi_livedns',
+'gcloud',
+'gd',
+'gdnsdk',
+'he',
+'hexonet',
+'hostingde',
+'infoblox',
+'internetbs',
+'inwx',
+'ispconfig',
+'jd',
+'kas',
+'kinghost',
+'knot',
+'leaseweb',
+'lexicon',
+'linode',
+'linode_v4',
+'loopia',
+'lua',
+'maradns',
+'me',
+'miab',
+'misaka',
+'myapi',
+'mydevil',
+'mydnsjp',
+'namecheap',
+'namecom',
+'namesilo',
+'nederhost',
+'neodigit',
+'netcup',
+'nic',
+'nsd',
+'nsone',
+'nsupdate',
+'nw',
+'one',
+'online',
+'openprovider',
+'opnsense',
+'ovh',
+'pdns',
+'pleskxml',
+'pointhq',
+'rackspace',
+'rcode0',
+'regru',
+'schlundtech',
+'selectel',
+'servercow',
+'tele3',
+'ultra',
+'unoeuro',
+'variomedia',
+'vscale',
+'vultr',
+'yandex',
+'zilore',
+'zone',
+'zonomi',
+];
+
+sub properties {
+return {
+   api => {
+   description => "API plugin name",
+   type => 'string',
+   enum => $api_name_list,
+   },
+   data => {
+   type => 'string',
+   description => 'DNS plugin data.',
+   },
+};
+}
+
+sub options {
+return {
+   api => {},
+   data => {},
+   nodes => { optional => 1 },
+   disable => { optional => 1 },
+};
+}
+
+my $outfunc = sub {
+my $line = shift;
+print "$line\n";
+};
+
+sub extract_challenge {
+my ($self, $challenge) = @_;
+
+return PVE::ACME::Challenge->extract_challenge($challenge, 'dns-01');
+}
+
+# The order of the parameters passed to proxmox-acme is important
+# proxmox-acme setup $plugin [$domain|$alias] $txtvalue $plugin_conf_string
+sub setup {
+my ($self, $data) = @_;
+
+die "No plugin data for DNSChallenge\n" if !defined($data->{plugin});
+my $domain = $data->{plugin}->{alias} ? $data->{plugin}->{alias} : 
$data->{domain};
+my $txtvalue = PVE::ACME::encode(sha256($data->{key_authorization}));
+my $dnsplugin = $data->{plugin}->{api};
+my $plugin_conf_string = $data->{plugin}->{data};
+
+# for security reasons, we execute the command as nobody
+# we can't verify that the code of the DNSPlugins are harmless.
+my $cmd = ["setpriv", "--reuid", "nobody", "--regid", "nogroup", 
"--clear-groups", "--"];
+push @$cmd, "/usr/bin/bash", $ACME_PATH, "setup", $dnsplugin, $domain;
+push @$cmd,$txtvalue, $plugin_conf_string;
+
+PVE::Tools::run_command($cmd, outfunc => $outfunc);
+print "Add TXT record: _acme-challenge.$domain\n";
+}
+
+# The order of the parameters passed to p

[pve-devel] [Patch V3 acme 03/13] Remove unnecessary Code and fixes.

2020-04-15 Thread Wolfgang Link
This Code is not required in the Proxmox environment.
We know in our environment what we have as a tool-change.

Fix Code what does not work because variable or functions are missing.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 476 ---
 1 file changed, 120 insertions(+), 356 deletions(-)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index 007c58d..662c39a 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -1,49 +1,27 @@
 #!/usr/bin/env sh
 
-# copied from acme.sh
-# Usage: multiline
+HTTP_HEADER="$(mktemp)"
+
 _base64() {
-  [ "" ] #urgly
-  if [ "$1" ]; then
-_debug3 "base64 multiline:'$1'"
-${ACME_OPENSSL_BIN:-openssl} base64 -e
-  else
-_debug3 "base64 single line."
-${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
-  fi
+  openssl base64 -e | tr -d '\r\n'
 }
 
-# Usage: multiline
 _dbase64() {
-  if [ "$1" ]; then
-${ACME_OPENSSL_BIN:-openssl} base64 -d -A
-  else
-${ACME_OPENSSL_BIN:-openssl} base64 -d
-  fi
+  openssl base64 -d
 }
 
 # Usage: hashalg  [outputhex]
 # Output Base64-encoded digest
 _digest() {
   alg="$1"
-  if [ -z "$alg" ]; then
-_usage "Usage: _digest hashalg"
-return 1
-  fi
-
-  outputhex="$2"
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
-if [ "$outputhex" ]; then
-  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' 
'
+if [ "$2" ]; then
+  openssl dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
 else
-  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
+  openssl dgst -"$alg" -binary | _base64
 fi
-  else
-_err "$alg is not supported yet"
-return 1
   fi
-
 }
 
 _upper_case() {
@@ -80,11 +58,6 @@ _getfield() {
   _findex="$2"
   _sep="$3"
 
-  if [ -z "$_findex" ]; then
-_usage "Usage: str field  [sep]"
-return 1
-  fi
-
   if [ -z "$_sep" ]; then
 _sep=","
   fi
@@ -105,20 +78,12 @@ _getfield() {
 
 _exists() {
   cmd="$1"
-  if [ -z "$cmd" ]; then
-_usage "Usage: _exists cmd"
-return 1
-  fi
-
   if eval type type >/dev/null 2>&1; then
-eval type "$cmd" >/dev/null 2>&1
-  elif command >/dev/null 2>&1; then
+type "$cmd" >/dev/null 2>&1
+  else command
 command -v "$cmd" >/dev/null 2>&1
-  else
-which "$cmd" >/dev/null 2>&1
   fi
   ret="$?"
-  _debug3 "$cmd exists=$ret"
   return $ret
 }
 
@@ -134,59 +99,6 @@ _egrep_o() {
   fi
 }
 
-_inithttp() {
-
-  if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
-HTTP_HEADER="$(_mktemp)"
-_debug2 HTTP_HEADER "$HTTP_HEADER"
-  fi
-
-  if [ "$__HTTP_INITIALIZED" ]; then
-if [ "$_ACME_CURL$_ACME_WGET" ]; then
-  _debug2 "Http already initialized."
-  return 0
-fi
-  fi
-
-  if [ -z "$_ACME_CURL" ] && _exists "curl"; then
-_ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER "
-if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
-  _CURL_DUMP="$(_mktemp)"
-  _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
-fi
-
-if [ "$CA_PATH" ]; then
-  _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
-elif [ "$CA_BUNDLE" ]; then
-  _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
-fi
-
-if _contains "$(curl --help 2>&1)" "--globoff"; then
-  _ACME_CURL="$_ACME_CURL -g "
-fi
-  fi
-
-  if [ -z "$_ACME_WGET" ] && _exists "wget"; then
-_ACME_WGET="wget -q"
-if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
-  _ACME_WGET="$_ACME_WGET -d "
-fi
-if [ "$CA_PATH" ]; then
-  _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
-elif [ "$CA_BUNDLE" ]; then
-  _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
-fi
-  fi
-
-  #from wget 1.14: do not skip body on 404 error
-  if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" 
"--content-on-error"; then
-_ACME_WGET="$_ACME_WGET --content-on-error "
-  fi
-
-  __HTTP_INITIALIZED=1
-
-}
-
 # body  url [needbase64] [POST|PUT|DELETE] [ContentType]
 _post() {
   body="$1"
@@ -198,181 +110,73 @@ _post() {
   if [ -z "$httpmethod" ]; then
 httpmethod="POST"
   fi
-  _debug $httpmethod

[pve-devel] [Patch V3 acme 06/13] Add submodule acme.sh for DNS plugins

2020-04-15 Thread Wolfgang Link
Copy the DNS plugins form acme.sh

The project acme.sh can be found here.
https://github.com/Neilpang/acme.sh

Signed-off-by: Wolfgang Link 
---
 .gitmodules  |   3 ++
 Makefile |  10 -
 acme.sh  |   1 +
 src/Makefile | 117 +++
 4 files changed, 129 insertions(+), 2 deletions(-)
 create mode 100644 .gitmodules
 create mode 16 acme.sh
 create mode 100644 src/Makefile

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000..9e2f450
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "acme.sh"]
+   path = acme.sh
+   url = https://github.com/acmesh-official/acme.sh.git
diff --git a/Makefile b/Makefile
index 0a78b63..9d2655a 100644
--- a/Makefile
+++ b/Makefile
@@ -8,12 +8,18 @@ GITVERSION:=$(shell git rev-parse HEAD)
 DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_all.deb
 DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc
 
+DNSAPI="acme.sh/dnsapi"
+
 all: $(DEB)
 
-$(BUILDDIR): src debian
-   rm -rf $(BUILDDIR)
+.PHONY: submodule
+submodule:
+   test -d $(DNSAPI) || git submodule update --init --recursive
 
+$(BUILDDIR): src debian submodule
+   rm -rf $(BUILDDIR)
rsync -a src/ debian $(BUILDDIR)
+   rsync -a $(DNSAPI) $(BUILDDIR)
# remove if repository exists
# echo "git clone git://git.proxmox.com/git/proxmox-acme\\ngit checkout 
$(GITVERSION)" > $(BUILDDIR)/debian/SOURCE
 
diff --git a/acme.sh b/acme.sh
new file mode 16
index 000..d437d6f
--- /dev/null
+++ b/acme.sh
@@ -0,0 +1 @@
+Subproject commit d437d6fde95dc7368d4fa76c05648a8dd4cbe69e
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 000..aa482aa
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,117 @@
+PREFIX=/usr
+ACMEDIR=${PREFIX}/share/proxmox-acme/
+
+ACME_SOURCES = \
+   dnsapi/dns_nsone.sh \
+   dnsapi/dns_cf.sh \
+   dnsapi/dns_linode.sh \
+   dnsapi/dns_dnsimple.sh \
+   dnsapi/dns_lexicon.sh \
+   dnsapi/dns_da.sh \
+   dnsapi/dns_desec.sh \
+   dnsapi/dns_he.sh \
+   dnsapi/dns_doapi.sh \
+   dnsapi/dns_pdns.sh \
+   dnsapi/dns_yandex.sh \
+   dnsapi/dns_nic.sh \
+   dnsapi/dns_conoha.sh \
+   dnsapi/dns_schlundtech.sh \
+   dnsapi/dns_do.sh \
+   dnsapi/dns_autodns.sh \
+   dnsapi/dns_tele3.sh \
+   dnsapi/dns_gd.sh \
+   dnsapi/dns_exoscale.sh \
+   dnsapi/dns_kas.sh \
+   dnsapi/dns_easydns.sh \
+   dnsapi/dns_pleskxml.sh \
+   dnsapi/dns_variomedia.sh \
+   dnsapi/dns_duckdns.sh \
+   dnsapi/dns_kinghost.sh \
+   dnsapi/dns_myapi.sh \
+   dnsapi/dns_nw.sh \
+   dnsapi/dns_misaka.sh \
+   dnsapi/dns_one.sh \
+   dnsapi/dns_mydevil.sh \
+   dnsapi/dns_acmedns.sh \
+   dnsapi/dns_gandi_livedns.sh \
+   dnsapi/dns_dgon.sh \
+   dnsapi/dns_nsd.sh \
+   dnsapi/dns_selectel.sh \
+   dnsapi/dns_linode_v4.sh \
+   dnsapi/dns_hostingde.sh \
+   dnsapi/dns_freedns.sh \
+   dnsapi/dns_opnsense.sh \
+   dnsapi/dns_servercow.sh \
+   dnsapi/dns_hexonet.sh \
+   dnsapi/dns_aws.sh \
+   dnsapi/dns_dyn.sh \
+   dnsapi/dns_leaseweb.sh \
+   dnsapi/dns_online.sh \
+   dnsapi/dns_gcloud.sh \
+   dnsapi/dns_active24.sh \
+   dnsapi/dns_jd.sh \
+   dnsapi/dns_internetbs.sh \
+   dnsapi/dns_vscale.sh \
+   dnsapi/dns_durabledns.sh \
+   dnsapi/dns_dynv6.sh \
+   dnsapi/dns_openprovider.sh \
+   dnsapi/dns_nsupdate.sh \
+   dnsapi/dns_inwx.sh \
+   dnsapi/dns_namecom.sh \
+   dnsapi/dns_infoblox.sh \
+   dnsapi/dns_ultra.sh \
+   dnsapi/dns_unoeuro.sh \
+   dnsapi/dns_cloudns.sh \
+   dnsapi/dns_ovh.sh \
+   dnsapi/dns_cn.sh \
+   dnsapi/dns_azure.sh \
+   dnsapi/dns_namecheap.sh \
+   dnsapi/dns_vultr.sh \
+   dnsapi/dns_acmeproxy.sh \
+   dnsapi/dns_ddnss.sh \
+   dnsapi/dns_maradns.sh \
+   dnsapi/dns_cx.sh \
+   dnsapi/dns_dreamhost.sh \
+   dnsapi/dns_ispconfig.sh \
+   dnsapi/dns_nederhost.sh \
+   dnsapi/dns_loopia.sh \
+   dnsapi/dns_ad.sh \
+   dnsapi/dns_cyon.sh \
+   dnsapi/dns_ali.sh \
+   dnsapi/dns_netcup.sh \
+   dnsapi/dns_knot.sh \
+   dnsapi/dns_zone.sh \
+   dnsapi/dns_me.sh \
+   dnsapi/dns_namesilo.sh \
+   dnsapi/dns_pointhq.sh \
+   dnsapi/dns_gdnsdk.sh \
+   dnsapi/dns_zilore.sh \
+   dnsapi/dns_domen.sh \op.sh \
+   dnsapi/dns_miab.sh \
+   dnsapi/dns_lua.sh \
+   dnsapi/dns_rcode0.sh \
+   dnsapi/dns_constellix.sh \
+   dnsapi/dns_dpi.sh \
+   dnsapi/dns_dynu.sh \
+   dnsapi/dns_dp.sh \
+   dnsapi/dns_euserv.sh \
+   dnsapi/dns_zonomi.sh \
+   dnsapi/dns_rackspace.sh \
+   dnsapi/dns_regru.sh \
+   dnsapi/dns_clouddns.sh \
+   dnsapi/dns_neodigit.sh \
+   dnsapi/dns_mydnsjp.sh \
+
+all:
+
+.PHONY: install
+install:
+   install -D -m 0744 proxmox-

[pve-devel] [Patch V3 acme 13/13] Implement function to resolve all subplugins

2020-04-15 Thread Wolfgang Link
This function helps to retrieve all subplugins
that are supported by the plugins.
This will later be used as an enumeration for entering parameters.

Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME.pm  | 21 +
 src/PVE/ACME/DNSChallenge.pm |  4 
 src/PVE/ACME/StandAlone.pm   |  4 
 3 files changed, 29 insertions(+)

diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
index 114eb41..a4da8d4 100644
--- a/src/PVE/ACME.pm
+++ b/src/PVE/ACME.pm
@@ -493,6 +493,27 @@ sub request_challenge_validation {
 return $return;
 }
 
+# return all availible subplugins from the plugins
+sub get_subplugins {
+
+my $tmp = [];
+my $plugins = PVE::ACME::Challenge->lookup_types();
+
+foreach my $plugin_name (@$plugins) {
+   my $plugin = PVE::ACME::Challenge->lookup($plugin_name);
+   push @$tmp, $plugin->get_subplugins();
+}
+
+my $subplugins = [];
+foreach my $array (@$tmp) {
+   foreach my $subplugin ( @$array) {
+   push @$subplugins, $subplugin;
+   }
+}
+
+return $subplugins;
+}
+
 # actually 'do' a $method request on $url
 # $data: input for JWS, optional
 # $use_jwk: use JWK instead of KID in JWD (see sub jws)
diff --git a/src/PVE/ACME/DNSChallenge.pm b/src/PVE/ACME/DNSChallenge.pm
index 7af442e..f62333b 100644
--- a/src/PVE/ACME/DNSChallenge.pm
+++ b/src/PVE/ACME/DNSChallenge.pm
@@ -153,6 +153,10 @@ sub extract_challenge {
 
 return PVE::ACME::Challenge->extract_challenge($challenge, 'dns-01');
 }
+
+sub get_subplugins {
+return $api_name_list;
+}
 
 # The order of the parameters passed to proxmox-acme is important
 # proxmox-acme setup $plugin [$domain|$alias] $txtvalue $plugin_conf_string
diff --git a/src/PVE/ACME/StandAlone.pm b/src/PVE/ACME/StandAlone.pm
index 73caef6..310e627 100644
--- a/src/PVE/ACME/StandAlone.pm
+++ b/src/PVE/ACME/StandAlone.pm
@@ -33,6 +33,10 @@ sub extract_challenge {
 return PVE::ACME::Challenge->extract_challenge($challenge, 'http-01');
 }
 
+sub get_subplugins {
+return [];
+}
+
 sub setup {
 my ($class, $data) = @_;
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 02/13] Copy the needed function form acme.sh

2020-04-15 Thread Wolfgang Link
For the thin wrapper around acme.sh DNS plugins, the required functions are 
copied.
The project acme.sh can be found here.
https://github.com/Neilpang/acme.sh

Signed-off-by: Wolfgang Link 
---
 Makefile |   1 +
 src/proxmox-acme | 806 +++
 2 files changed, 807 insertions(+)
 create mode 100644 src/proxmox-acme

diff --git a/Makefile b/Makefile
index 5ed0522..0a78b63 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ all: $(DEB)
 
 $(BUILDDIR): src debian
rm -rf $(BUILDDIR)
+
rsync -a src/ debian $(BUILDDIR)
# remove if repository exists
# echo "git clone git://git.proxmox.com/git/proxmox-acme\\ngit checkout 
$(GITVERSION)" > $(BUILDDIR)/debian/SOURCE
diff --git a/src/proxmox-acme b/src/proxmox-acme
new file mode 100644
index 000..007c58d
--- /dev/null
+++ b/src/proxmox-acme
@@ -0,0 +1,806 @@
+#!/usr/bin/env sh
+
+# copied from acme.sh
+# Usage: multiline
+_base64() {
+  [ "" ] #urgly
+  if [ "$1" ]; then
+_debug3 "base64 multiline:'$1'"
+${ACME_OPENSSL_BIN:-openssl} base64 -e
+  else
+_debug3 "base64 single line."
+${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
+  fi
+}
+
+# Usage: multiline
+_dbase64() {
+  if [ "$1" ]; then
+${ACME_OPENSSL_BIN:-openssl} base64 -d -A
+  else
+${ACME_OPENSSL_BIN:-openssl} base64 -d
+  fi
+}
+
+# Usage: hashalg  [outputhex]
+# Output Base64-encoded digest
+_digest() {
+  alg="$1"
+  if [ -z "$alg" ]; then
+_usage "Usage: _digest hashalg"
+return 1
+  fi
+
+  outputhex="$2"
+
+  if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
+if [ "$outputhex" ]; then
+  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' 
'
+else
+  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
+fi
+  else
+_err "$alg is not supported yet"
+return 1
+  fi
+
+}
+
+_upper_case() {
+  # shellcheck disable=SC2018,SC2019
+  tr 'a-z' 'A-Z'
+}
+
+_lower_case() {
+  # shellcheck disable=SC2018,SC2019
+  tr 'A-Z' 'a-z'
+}
+
+_startswith() {
+  _str="$1"
+  _sub="$2"
+  echo "$_str" | grep "^$_sub" >/dev/null 2>&1
+}
+
+_endswith() {
+  _str="$1"
+  _sub="$2"
+  echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
+}
+
+_contains() {
+  _str="$1"
+  _sub="$2"
+  echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
+}
+
+# str index [sep]
+_getfield() {
+  _str="$1"
+  _findex="$2"
+  _sep="$3"
+
+  if [ -z "$_findex" ]; then
+_usage "Usage: str field  [sep]"
+return 1
+  fi
+
+  if [ -z "$_sep" ]; then
+_sep=","
+  fi
+
+  _ffi="$_findex"
+  while [ "$_ffi" -gt "0" ]; do
+_fv="$(echo "$_str" | cut -d "$_sep" -f "$_ffi")"
+if [ "$_fv" ]; then
+  printf -- "%s" "$_fv"
+  return 0
+fi
+_ffi="$(_math "$_ffi" - 1)"
+  done
+
+  printf -- "%s" "$_str"
+
+}
+
+_exists() {
+  cmd="$1"
+  if [ -z "$cmd" ]; then
+_usage "Usage: _exists cmd"
+return 1
+  fi
+
+  if eval type type >/dev/null 2>&1; then
+eval type "$cmd" >/dev/null 2>&1
+  elif command >/dev/null 2>&1; then
+command -v "$cmd" >/dev/null 2>&1
+  else
+which "$cmd" >/dev/null 2>&1
+  fi
+  ret="$?"
+  _debug3 "$cmd exists=$ret"
+  return $ret
+}
+
+# a + b
+_math() {
+  _m_opts="$@"
+  printf "%s" "$(($_m_opts))"
+}
+
+_egrep_o() {
+  if ! egrep -o "$1" 2>/dev/null; then
+sed -n 's/.*\('"$1"'\).*/\1/p'
+  fi
+}
+
+_inithttp() {
+
+  if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
+HTTP_HEADER="$(_mktemp)"
+_debug2 HTTP_HEADER "$HTTP_HEADER"
+  fi
+
+  if [ "$__HTTP_INITIALIZED" ]; then
+if [ "$_ACME_CURL$_ACME_WGET" ]; then
+  _debug2 "Http already initialized."
+  return 0
+fi
+  fi
+
+  if [ -z "$_ACME_CURL" ] && _exists "curl"; then
+_ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER "
+if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
+  _CURL_DUMP="$(_mktemp)"
+  _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
+fi
+
+if [ "$CA_PATH" ]; then
+  _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
+elif [

[pve-devel] [Patch V3 acme 10/13] Use the caller's data instead of extracting it yourself.

2020-04-15 Thread Wolfgang Link
Add the server in the data structure to return it.

Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME/StandAlone.pm | 41 --
 1 file changed, 13 insertions(+), 28 deletions(-)

diff --git a/src/PVE/ACME/StandAlone.pm b/src/PVE/ACME/StandAlone.pm
index 0b4aaae..73caef6 100644
--- a/src/PVE/ACME/StandAlone.pm
+++ b/src/PVE/ACME/StandAlone.pm
@@ -34,41 +34,26 @@ sub extract_challenge {
 }
 
 sub setup {
-my ($class, $acme, $authorization) = @_;
+my ($class, $data) = @_;
 
-my $challenges = $authorization->{challenges};
-die "no challenges defined in authorization\n" if !$challenges;
+print "Setting up webserver\n";
 
-my $http_challenges = [ grep {$_->{type} eq 'http-01'} @$challenges ];
-die "no http-01 challenge defined in authorization\n"
-   if ! scalar $http_challenges;
-
-my $http_challenge = $http_challenges->[0];
-
-die "no token found in http-01 challenge\n" if !$http_challenge->{token};
-
-my $key_authorization = $acme->key_authorization($http_challenge->{token});
+my $key_auth = $data->{key_authorization};
 
 my $server = HTTP::Daemon->new(
LocalPort => 80,
ReuseAddr => 1,
-) or die "Failed to initialize HTTP daemon\n";
+   ) or die "Failed to initialize HTTP daemon\n";
 my $pid = fork() // die "Failed to fork HTTP daemon - $!\n";
 if ($pid) {
-   my $self = {
-   server => $server,
-   pid => $pid,
-   authorization => $authorization,
-   key_auth => $key_authorization,
-   url => $http_challenge->{url},
-   };
-
-   return bless $self, $class;
+   $data->{server} = $server;
+   $data->{pid} = $pid;
 } else {
while (my $c = $server->accept()) {
while (my $r = $c->get_request()) {
-   if ($r->method() eq 'GET' and $r->uri->path eq 
"/.well-known/acme-challenge/$http_challenge->{token}") {
-   my $resp = HTTP::Response->new(200, 'OK', undef, 
$key_authorization);
+   if ($r->method() eq 'GET' and
+   $r->uri->path eq 
"/.well-known/acme-challenge/$data->{token}") {
+   my $resp = HTTP::Response->new(200, 'OK', undef, $key_auth);
$resp->request($r);
$c->send_response($resp);
} else {
@@ -82,11 +67,11 @@ sub setup {
 }
 
 sub teardown {
-my ($self) = @_;
+my ($self, $data) = @_;
 
-eval { $self->{server}->close() };
-kill('KILL', $self->{pid});
-waitpid($self->{pid}, 0);
+eval { $data->{server}->close() };
+kill('KILL', $data->{pid});
+waitpid($data->{pid}, 0);
 }
 
 1;
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V3 acme 07/13] Move code from pve-common

2020-04-15 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 src/Makefile   |   9 +
 src/PVE/ACME.pm| 533 +
 src/PVE/ACME/Challenge.pm  |  22 ++
 src/PVE/ACME/StandAlone.pm |  71 +
 4 files changed, 635 insertions(+)
 create mode 100644 src/PVE/ACME.pm
 create mode 100644 src/PVE/ACME/Challenge.pm
 create mode 100644 src/PVE/ACME/StandAlone.pm

diff --git a/src/Makefile b/src/Makefile
index aa482aa..b65e330 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,6 @@
 PREFIX=/usr
 ACMEDIR=${PREFIX}/share/proxmox-acme/
+PERLDIR=${PREFIX}/share/perl5
 
 ACME_SOURCES = \
dnsapi/dns_nsone.sh \
@@ -102,12 +103,20 @@ ACME_SOURCES = \
dnsapi/dns_neodigit.sh \
dnsapi/dns_mydnsjp.sh \
 
+LIB_SOURCES = \
+   ACME.pm \
+   ACME/Challenge.pm \
+   ACME/StandAlone.pm \
+
 all:
 
 .PHONY: install
 install:
install -D -m 0744 proxmox-acme ${DESTDIR}${ACMEDIR}/proxmox-acme
for i in ${ACME_SOURCES}; do install -D -m 0644 $$i 
${DESTDIR}${ACMEDIR}/$$i; done
+   install -d -m 0755 ${DESTDIR}${PERLDIR}/PVE
+   install -d -m 0755 ${DESTDIR}${PERLDIR}/PVE/ACME
+   for i in ${LIB_SOURCES}; do install -D -m 0644 PVE/$$i 
${DESTDIR}${PERLDIR}/PVE/$$i; done
 
 .PHONY: clean
 clean:
diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
new file mode 100644
index 000..114eb41
--- /dev/null
+++ b/src/PVE/ACME.pm
@@ -0,0 +1,533 @@
+package PVE::ACME;
+
+use strict;
+use warnings;
+
+use POSIX;
+
+use Data::Dumper;
+use Date::Parse;
+use MIME::Base64 qw(encode_base64url);
+use File::Path qw(make_path);
+use JSON;
+use Digest::SHA qw(sha256 sha256_hex);
+
+use HTTP::Request;
+use LWP::UserAgent;
+
+use Crypt::OpenSSL::RSA;
+
+use PVE::Certificate;
+use PVE::Tools qw(
+file_set_contents
+file_get_contents
+);
+
+Crypt::OpenSSL::RSA->import_random_seed();
+
+my $LETSENCRYPT_STAGING = 
'https://acme-staging-v02.api.letsencrypt.org/directory';
+
+### ACME library (compatible with Let's Encrypt v2 API)
+#
+# sample usage:
+#
+# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
+# 2) $acme->init(4096); # generate account key
+# 4) my $tos_url = $acme->get_meta()->{termsOfService}; # optional, display if 
applicable
+# 5) $acme->new_account($tos_url, contact => ['mailto:exam...@example.com']);
+#
+# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
+# 2) $acme->load();
+# 3) my ($order_url, $order) = $acme->new_order(['foo.example.com', 
'bar.example.com']);
+# 4) # repeat a-f for each $auth_url in $order->{authorizations}
+# a) my $authorization = $acme->get_authorization($auth_url);
+# b) # pick $challenge from $authorization->{challenges} according to desired 
type
+# c) my $key_auth = $acme->key_authorization($challenge->{token});
+# d) # setup challenge validation according to specification
+# e) $acme->request_challenge_validation($challenge->{url}, $key_auth);
+# f) # poll $acme->get_authorization($auth_url) until status is 'valid'
+# 5) # generate CSR in PEM format
+# 6) $acme->finalize_order($order, $csr);
+# 7) # poll $acme->get_order($order_url) until status is 'valid'
+# 8) my $cert = $acme->get_certificate($order);
+# 9) # $key is path to key file, $cert contains PEM-encoded certificate chain
+#
+# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
+# 2) $acme->load();
+# 3) $acme->revoke_certificate($cert);
+
+# Tools
+sub encode($) { # acme requires 'base64url' encoding
+return encode_base64url($_[0]);
+}
+
+sub tojs($;%) { # shortcut for to_json with utf8=>1
+my ($data, %data) = @_;
+return to_json($data, { utf8 => 1, %data });
+}
+
+sub fromjs($) {
+return from_json($_[0]);
+}
+
+sub fatal($$;$$) {
+my ($self, $msg, $dump, $noerr) = @_;
+
+warn Dumper($dump), "\n" if $self->{debug} && $dump;
+if ($noerr) {
+   warn "$msg\n";
+} else {
+   die "$msg\n";
+}
+}
+
+# Implementation
+
+# $path: account JSON file
+# $directory: the ACME directory URL used to find method URLs
+sub new($$$) {
+my ($class, $path, $directory) = @_;
+
+$directory //= $LETSENCRYPT_STAGING;
+
+my $ua = LWP::UserAgent->new();
+$ua->env_proxy();
+$ua->agent('pve-acme/0.1');
+$ua->protocols_allowed(['https']);
+
+my $self = {
+   ua => $ua,
+   path => $path,
+   directory => $directory,
+   nonce => undef,
+   key => undef,
+   location => undef,
+   account => undef,
+   tos => undef,
+};
+
+return bless $self, $class;
+}
+
+# RS256: PKCS#1 padding, no OAEP, SHA256
+my $configure_key = sub {
+my ($key) = @_;
+$key->use_pkcs1_padding();
+$key->use_sha256_hash();
+};
+
+# Create account key with $keybits bits
+# use instead of load, overwrites existing account JSON file!
+sub init {
+my ($

[pve-devel] [Patch V3 acme 12/13] Add debug mode

2020-04-15 Thread Wolfgang Link
This can be used at setup time to get feedback on the DNS plugin parameters.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 21 +++--
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index ff9fec8..566588f 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -9,6 +9,8 @@ USER_AGENT="$PROJECT_NAME/$VER"
 DNS_PLUGIN_PATH="/usr/share/proxmox-acme/dnsapi"
 HTTP_HEADER="$(mktemp)"
 
+DEBUG="0"
+
 _base64() {
   openssl base64 -e | tr -d '\r\n'
 }
@@ -532,27 +534,31 @@ _cleardomainconf() {
 }
 
 _debug() {
-  return
+  if [[ $DEBUG -eq 0 ]]; then
+return
+  fi
+  printf -- "%s" "[$(date)] " >&1
+  echo "$1 $2"
 }
 
 _debug2() {
-  return
+  _debug $1 $2
 }
 
 _debug3() {
-  return
+  _debug $1 $2
 }
 
 _secure_debug() {
-  return
+  _debug $1 $2
 }
 
 _secure_debug2() {
-  return
+  _debug $1 $2
 }
 
 _secure_debug3() {
-  return
+  _debug $1 $2
 }
 
 _saveaccountconf() {
@@ -601,6 +607,7 @@ _load_plugin_config() {
 # $2  Fully Qualified Domain Name
 # $3  value for TXT record
 # $4  DNS plugin auth and config parameter separated by ","
+# $5  0 is off, and the default all others are on.
 
 setup() {
   dns_plugin="dns_$1"
@@ -608,6 +615,7 @@ setup() {
   fqdn="_acme-challenge.$2"
   txtvalue=$3
   plugin_conf_string=$4
+  DEBUG=$5
 
   _load_plugin_config
 
@@ -634,6 +642,7 @@ teardown() {
   fqdn="_acme-challenge.$2"
   txtvalue=$3
   plugin_conf_string=$4
+  DEBUG=$5
 
   _load_plugin_config
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 07/12] Move code from pve-common

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 src/Makefile   |   9 +
 src/PVE/ACME.pm| 533 +
 src/PVE/ACME/Challenge.pm  |  22 ++
 src/PVE/ACME/StandAlone.pm |  71 +
 4 files changed, 635 insertions(+)
 create mode 100644 src/PVE/ACME.pm
 create mode 100644 src/PVE/ACME/Challenge.pm
 create mode 100644 src/PVE/ACME/StandAlone.pm

diff --git a/src/Makefile b/src/Makefile
index a5e8fb9..c56a354 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,6 @@
 PREFIX=/usr
 ACMEDIR=${PREFIX}/share/proxmox-ve/
+PERLDIR=${PREFIX}/share/perl5
 
 ACME_SOURCES = \
dnsapi/dns_nsone.sh \
@@ -102,6 +103,11 @@ ACME_SOURCES = \
dnsapi/dns_neodigit.sh \
dnsapi/dns_mydnsjp.sh \
 
+LIB_SOURCES = \
+   ACME.pm \
+   ACME/Challenge.pm \
+   ACME/StandAlone.pm \
+
 all:
 
 .PHONY: install
@@ -110,6 +116,9 @@ install:
install -d -m 0755 ${DESTDIR}${ACMEDIR}/proxmox-acme/dnsapi
install -D -m 0744 proxmox-acme 
${DESTDIR}${ACMEDIR}/proxmox-acme/proxmox-acme
for i in ${ACME_SOURCES}; do install -D -m 0644 $$i 
${DESTDIR}${ACMEDIR}/proxmox-acme/$$i; done
+   install -d -m 0755 ${DESTDIR}${PERLDIR}/PVE
+   install -d -m 0755 ${DESTDIR}${PERLDIR}/PVE/ACME
+   for i in ${LIB_SOURCES}; do install -D -m 0644 PVE/$$i 
${DESTDIR}${PERLDIR}/PVE/$$i; done
 
 .PHONY: clean
 clean:
diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
new file mode 100644
index 000..114eb41
--- /dev/null
+++ b/src/PVE/ACME.pm
@@ -0,0 +1,533 @@
+package PVE::ACME;
+
+use strict;
+use warnings;
+
+use POSIX;
+
+use Data::Dumper;
+use Date::Parse;
+use MIME::Base64 qw(encode_base64url);
+use File::Path qw(make_path);
+use JSON;
+use Digest::SHA qw(sha256 sha256_hex);
+
+use HTTP::Request;
+use LWP::UserAgent;
+
+use Crypt::OpenSSL::RSA;
+
+use PVE::Certificate;
+use PVE::Tools qw(
+file_set_contents
+file_get_contents
+);
+
+Crypt::OpenSSL::RSA->import_random_seed();
+
+my $LETSENCRYPT_STAGING = 
'https://acme-staging-v02.api.letsencrypt.org/directory';
+
+### ACME library (compatible with Let's Encrypt v2 API)
+#
+# sample usage:
+#
+# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
+# 2) $acme->init(4096); # generate account key
+# 4) my $tos_url = $acme->get_meta()->{termsOfService}; # optional, display if 
applicable
+# 5) $acme->new_account($tos_url, contact => ['mailto:exam...@example.com']);
+#
+# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
+# 2) $acme->load();
+# 3) my ($order_url, $order) = $acme->new_order(['foo.example.com', 
'bar.example.com']);
+# 4) # repeat a-f for each $auth_url in $order->{authorizations}
+# a) my $authorization = $acme->get_authorization($auth_url);
+# b) # pick $challenge from $authorization->{challenges} according to desired 
type
+# c) my $key_auth = $acme->key_authorization($challenge->{token});
+# d) # setup challenge validation according to specification
+# e) $acme->request_challenge_validation($challenge->{url}, $key_auth);
+# f) # poll $acme->get_authorization($auth_url) until status is 'valid'
+# 5) # generate CSR in PEM format
+# 6) $acme->finalize_order($order, $csr);
+# 7) # poll $acme->get_order($order_url) until status is 'valid'
+# 8) my $cert = $acme->get_certificate($order);
+# 9) # $key is path to key file, $cert contains PEM-encoded certificate chain
+#
+# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
+# 2) $acme->load();
+# 3) $acme->revoke_certificate($cert);
+
+# Tools
+sub encode($) { # acme requires 'base64url' encoding
+return encode_base64url($_[0]);
+}
+
+sub tojs($;%) { # shortcut for to_json with utf8=>1
+my ($data, %data) = @_;
+return to_json($data, { utf8 => 1, %data });
+}
+
+sub fromjs($) {
+return from_json($_[0]);
+}
+
+sub fatal($$;$$) {
+my ($self, $msg, $dump, $noerr) = @_;
+
+warn Dumper($dump), "\n" if $self->{debug} && $dump;
+if ($noerr) {
+   warn "$msg\n";
+} else {
+   die "$msg\n";
+}
+}
+
+# Implementation
+
+# $path: account JSON file
+# $directory: the ACME directory URL used to find method URLs
+sub new($$$) {
+my ($class, $path, $directory) = @_;
+
+$directory //= $LETSENCRYPT_STAGING;
+
+my $ua = LWP::UserAgent->new();
+$ua->env_proxy();
+$ua->agent('pve-acme/0.1');
+$ua->protocols_allowed(['https']);
+
+my $self = {
+   ua => $ua,
+   path => $path,
+   directory => $directory,
+   nonce => undef,
+   key => undef,
+   location => undef,
+   account => undef,
+   tos => undef,
+};
+
+return bless $self, $class;
+}
+
+# RS256: PKCS#1 padding, no OAEP, SHA256
+my $configure_key = sub {
+my ($key) = @_;
+$key->use_pkcs1_padding();
+$key->use_sha256_hash();
+};
+
+# Create account key with $k

[pve-devel] [Patch V2 acme 09/12] Add function setup and teardown.

2020-03-31 Thread Wolfgang Link
These are the two main functions that a plugin should offer.
Setup creates the endpoint at which Letsencrypt does the validation, teardown 
does the cleanup.

Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME.pm| 43 ++
 src/PVE/ACME/StandAlone.pm | 16 +-
 2 files changed, 48 insertions(+), 11 deletions(-)

diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
index 114eb41..c10dca2 100644
--- a/src/PVE/ACME.pm
+++ b/src/PVE/ACME.pm
@@ -23,6 +23,9 @@ file_set_contents
 file_get_contents
 );
 
+use PVE::ACME::Challenge;
+use PVE::ACME::StandAlone;
+
 Crypt::OpenSSL::RSA->import_random_seed();
 
 my $LETSENCRYPT_STAGING = 
'https://acme-staging-v02.api.letsencrypt.org/directory';
@@ -493,6 +496,46 @@ sub request_challenge_validation {
 return $return;
 }
 
+# Setup the challange
+# At the moment two plugin types are supproted
+# standalone: start an webserver an wait for the challenge
+# dns: add an txt record over the given API
+sub setup {
+my ($acme, $auth, $node_config) = @_;
+
+my $fqdn = $auth->{identifier}->{value};
+my $plugin_data = {};
+my $plugin_type = "standalone";
+my $plugin_conf = PVE::ACME::Challenge->load_config();
+
+# default is standalone if no plugin is set (old config)
+my $index = 0;
+while (defined($node_config->{$index})) {
+   if ($node_config->{$index}->{domain} eq $fqdn &&
+   defined($node_config->{$index}->{plugin})) {
+   $plugin_data = 
$plugin_conf->{ids}->{$node_config->{$index}->{plugin}};
+   $plugin_type = $plugin_data->{type};
+
+   # Alias mode is only supported for DNSChallange
+   $plugin_data->{alias} = $node_config->{$index}->{alias} if
+   $node_config->{$index}->{alias};
+   last;
+   }
+   $index++;
+}
+die if $plugin_type eq "standalone";
+my $plugin = PVE::ACME::Challenge->lookup($plugin_data->{type});
+return $plugin->setup($acme, $auth, $plugin_data);
+}
+
+#
+#
+sub teardown {
+my ($self) = @_;
+
+$self->teardown();
+}
+
 # actually 'do' a $method request on $url
 # $data: input for JWS, optional
 # $use_jwk: use JWK instead of KID in JWD (see sub jws)
diff --git a/src/PVE/ACME/StandAlone.pm b/src/PVE/ACME/StandAlone.pm
index ac75184..8fc8dc9 100644
--- a/src/PVE/ACME/StandAlone.pm
+++ b/src/PVE/ACME/StandAlone.pm
@@ -31,15 +31,8 @@ sub options {
 sub setup {
 my ($class, $acme, $authorization) = @_;
 
-my $challenges = $authorization->{challenges};
-die "no challenges defined in authorization\n" if !$challenges;
-
-my $http_challenges = [ grep {$_->{type} eq 'http-01'} @$challenges ];
-die "no http-01 challenge defined in authorization\n"
-   if ! scalar $http_challenges;
-
-my $http_challenge = $http_challenges->[0];
-
+print "Setting up webserver\n";
+my $http_challenge = 
PVE::ACME::extract_challenge($authorization->{challenges}, "http-01");
 die "no token found in http-01 challenge\n" if !$http_challenge->{token};
 
 my $key_authorization = $acme->key_authorization($http_challenge->{token});
@@ -47,7 +40,7 @@ sub setup {
 my $server = HTTP::Daemon->new(
LocalPort => 80,
ReuseAddr => 1,
-) or die "Failed to initialize HTTP daemon\n";
+   ) or die "Failed to initialize HTTP daemon\n";
 my $pid = fork() // die "Failed to fork HTTP daemon - $!\n";
 if ($pid) {
my $self = {
@@ -62,7 +55,8 @@ sub setup {
 } else {
while (my $c = $server->accept()) {
while (my $r = $c->get_request()) {
-   if ($r->method() eq 'GET' and $r->uri->path eq 
"/.well-known/acme-challenge/$http_challenge->{token}") {
+   if ($r->method() eq 'GET' and
+   $r->uri->path eq 
"/.well-known/acme-challenge/$http_challenge->{token}") {
my $resp = HTTP::Response->new(200, 'OK', undef, 
$key_authorization);
$resp->request($r);
$c->send_response($resp);
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 cluster] Add ACME plugin config file to cluster files

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 data/PVE/Cluster.pm | 1 +
 data/src/status.c   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/data/PVE/Cluster.pm b/data/PVE/Cluster.pm
index 068d626..b410003 100644
--- a/data/PVE/Cluster.pm
+++ b/data/PVE/Cluster.pm
@@ -54,6 +54,7 @@ my $observed = {
 'priv/shadow.cfg' => 1,
 'priv/tfa.cfg' => 1,
 'priv/token.cfg' => 1,
+'priv/plugins.cfg' => 1,
 '/qemu-server/' => 1,
 '/openvz/' => 1,
 '/lxc/' => 1,
diff --git a/data/src/status.c b/data/src/status.c
index 5e0cebe..0c881b8 100644
--- a/data/src/status.c
+++ b/data/src/status.c
@@ -83,6 +83,7 @@ static memdb_change_t memdb_change_array[] = {
{ .path = "user.cfg" },
{ .path = "domains.cfg" },
{ .path = "priv/shadow.cfg" },
+   { .path = "priv/plugins.cfg" },
{ .path = "priv/tfa.cfg" },
{ .path = "priv/token.cfg" },
{ .path = "datacenter.cfg" },
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 06/12] Add submodule acme.sh for DNS plugins

2020-03-31 Thread Wolfgang Link
Copy the DNS plugins form acme.sh

The project acme.sh can be found here.
https://github.com/Neilpang/acme.sh

Signed-off-by: Wolfgang Link 
---
 .gitmodules  |   3 ++
 Makefile |  10 -
 acme.sh  |   1 +
 src/Makefile | 119 +++
 4 files changed, 131 insertions(+), 2 deletions(-)
 create mode 100644 .gitmodules
 create mode 16 acme.sh
 create mode 100644 src/Makefile

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000..9e2f450
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "acme.sh"]
+   path = acme.sh
+   url = https://github.com/acmesh-official/acme.sh.git
diff --git a/Makefile b/Makefile
index 0a78b63..9d2655a 100644
--- a/Makefile
+++ b/Makefile
@@ -8,12 +8,18 @@ GITVERSION:=$(shell git rev-parse HEAD)
 DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_all.deb
 DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc
 
+DNSAPI="acme.sh/dnsapi"
+
 all: $(DEB)
 
-$(BUILDDIR): src debian
-   rm -rf $(BUILDDIR)
+.PHONY: submodule
+submodule:
+   test -d $(DNSAPI) || git submodule update --init --recursive
 
+$(BUILDDIR): src debian submodule
+   rm -rf $(BUILDDIR)
rsync -a src/ debian $(BUILDDIR)
+   rsync -a $(DNSAPI) $(BUILDDIR)
# remove if repository exists
# echo "git clone git://git.proxmox.com/git/proxmox-acme\\ngit checkout 
$(GITVERSION)" > $(BUILDDIR)/debian/SOURCE
 
diff --git a/acme.sh b/acme.sh
new file mode 16
index 000..d437d6f
--- /dev/null
+++ b/acme.sh
@@ -0,0 +1 @@
+Subproject commit d437d6fde95dc7368d4fa76c05648a8dd4cbe69e
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 000..a5e8fb9
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,119 @@
+PREFIX=/usr
+ACMEDIR=${PREFIX}/share/proxmox-ve/
+
+ACME_SOURCES = \
+   dnsapi/dns_nsone.sh \
+   dnsapi/dns_cf.sh \
+   dnsapi/dns_linode.sh \
+   dnsapi/dns_dnsimple.sh \
+   dnsapi/dns_lexicon.sh \
+   dnsapi/dns_da.sh \
+   dnsapi/dns_desec.sh \
+   dnsapi/dns_he.sh \
+   dnsapi/dns_doapi.sh \
+   dnsapi/dns_pdns.sh \
+   dnsapi/dns_yandex.sh \
+   dnsapi/dns_nic.sh \
+   dnsapi/dns_conoha.sh \
+   dnsapi/dns_schlundtech.sh \
+   dnsapi/dns_do.sh \
+   dnsapi/dns_autodns.sh \
+   dnsapi/dns_tele3.sh \
+   dnsapi/dns_gd.sh \
+   dnsapi/dns_exoscale.sh \
+   dnsapi/dns_kas.sh \
+   dnsapi/dns_easydns.sh \
+   dnsapi/dns_pleskxml.sh \
+   dnsapi/dns_variomedia.sh \
+   dnsapi/dns_duckdns.sh \
+   dnsapi/dns_kinghost.sh \
+   dnsapi/dns_myapi.sh \
+   dnsapi/dns_nw.sh \
+   dnsapi/dns_misaka.sh \
+   dnsapi/dns_one.sh \
+   dnsapi/dns_mydevil.sh \
+   dnsapi/dns_acmedns.sh \
+   dnsapi/dns_gandi_livedns.sh \
+   dnsapi/dns_dgon.sh \
+   dnsapi/dns_nsd.sh \
+   dnsapi/dns_selectel.sh \
+   dnsapi/dns_linode_v4.sh \
+   dnsapi/dns_hostingde.sh \
+   dnsapi/dns_freedns.sh \
+   dnsapi/dns_opnsense.sh \
+   dnsapi/dns_servercow.sh \
+   dnsapi/dns_hexonet.sh \
+   dnsapi/dns_aws.sh \
+   dnsapi/dns_dyn.sh \
+   dnsapi/dns_leaseweb.sh \
+   dnsapi/dns_online.sh \
+   dnsapi/dns_gcloud.sh \
+   dnsapi/dns_active24.sh \
+   dnsapi/dns_jd.sh \
+   dnsapi/dns_internetbs.sh \
+   dnsapi/dns_vscale.sh \
+   dnsapi/dns_durabledns.sh \
+   dnsapi/dns_dynv6.sh \
+   dnsapi/dns_openprovider.sh \
+   dnsapi/dns_nsupdate.sh \
+   dnsapi/dns_inwx.sh \
+   dnsapi/dns_namecom.sh \
+   dnsapi/dns_infoblox.sh \
+   dnsapi/dns_ultra.sh \
+   dnsapi/dns_unoeuro.sh \
+   dnsapi/dns_cloudns.sh \
+   dnsapi/dns_ovh.sh \
+   dnsapi/dns_cn.sh \
+   dnsapi/dns_azure.sh \
+   dnsapi/dns_namecheap.sh \
+   dnsapi/dns_vultr.sh \
+   dnsapi/dns_acmeproxy.sh \
+   dnsapi/dns_ddnss.sh \
+   dnsapi/dns_maradns.sh \
+   dnsapi/dns_cx.sh \
+   dnsapi/dns_dreamhost.sh \
+   dnsapi/dns_ispconfig.sh \
+   dnsapi/dns_nederhost.sh \
+   dnsapi/dns_loopia.sh \
+   dnsapi/dns_ad.sh \
+   dnsapi/dns_cyon.sh \
+   dnsapi/dns_ali.sh \
+   dnsapi/dns_netcup.sh \
+   dnsapi/dns_knot.sh \
+   dnsapi/dns_zone.sh \
+   dnsapi/dns_me.sh \
+   dnsapi/dns_namesilo.sh \
+   dnsapi/dns_pointhq.sh \
+   dnsapi/dns_gdnsdk.sh \
+   dnsapi/dns_zilore.sh \
+   dnsapi/dns_domen.sh \op.sh \
+   dnsapi/dns_miab.sh \
+   dnsapi/dns_lua.sh \
+   dnsapi/dns_rcode0.sh \
+   dnsapi/dns_constellix.sh \
+   dnsapi/dns_dpi.sh \
+   dnsapi/dns_dynu.sh \
+   dnsapi/dns_dp.sh \
+   dnsapi/dns_euserv.sh \
+   dnsapi/dns_zonomi.sh \
+   dnsapi/dns_rackspace.sh \
+   dnsapi/dns_regru.sh \
+   dnsapi/dns_clouddns.sh \
+   dnsapi/dns_neodigit.sh \
+   dnsapi/dns_mydnsjp.sh \
+
+all:
+
+.PHONY: install
+install:
+   install -d -m 0755 

[pve-devel] [Patch V2 common 3/3] Register acme-plugin-format

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 src/PVE/JSONSchema.pm | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/src/PVE/JSONSchema.pm b/src/PVE/JSONSchema.pm
index 3eb38eb..0c655bc 100644
--- a/src/PVE/JSONSchema.pm
+++ b/src/PVE/JSONSchema.pm
@@ -183,6 +183,13 @@ sub parse_storage_id {
 return parse_id($storeid, 'storage', $noerr);
 }
 
+PVE::JSONSchema::register_format('acme-plugin-id', \_acme_plugin_id);
+sub parse_acme_plugin_id {
+my ($pluginid, $noerr) = @_;
+
+return parse_id($pluginid, 'ACME plugin', $noerr);
+}
+
 sub parse_id {
 my ($id, $msg, $noerr) = @_;
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 12/12] Add debug mode

2020-03-31 Thread Wolfgang Link
This can be used at setup time to get feedback on the DNS plugin parameters.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 21 +++--
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index 1cfb8f6..89eee1c 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -9,6 +9,8 @@ USER_AGENT="$PROJECT_NAME/$VER"
 DNS_PLUGIN_PATH="/usr/share/proxmox-ve/proxmox-acme/dnsapi"
 HTTP_HEADER="$(mktemp)"
 
+DEBUG="0"
+
 _base64() {
   openssl base64 -e | tr -d '\r\n'
 }
@@ -532,27 +534,31 @@ _cleardomainconf() {
 }
 
 _debug() {
-  return
+  if [[ $DEBUG -eq 0 ]]; then
+return
+  fi
+  printf -- "%s" "[$(date)] " >&1
+  echo "$1 $2"
 }
 
 _debug2() {
-  return
+  _debug $1 $2
 }
 
 _debug3() {
-  return
+  _debug $1 $2
 }
 
 _secure_debug() {
-  return
+  _debug $1 $2
 }
 
 _secure_debug2() {
-  return
+  _debug $1 $2
 }
 
 _secure_debug3() {
-  return
+  _debug $1 $2
 }
 
 _saveaccountconf() {
@@ -595,6 +601,7 @@ _load_plugin_config() {
 # $2  Fully Qualified Domain Name
 # $3  value for TXT record
 # $4  DNS plugin auth and config parameter separated by ","
+# $5  0 is off, and the default all others are on.
 
 setup() {
   dns_plugin="dns_$1"
@@ -602,6 +609,7 @@ setup() {
   fqdn="_acme-challenge.$2"
   txtvalue=$3
   plugin_conf_sting=$4
+  DEBUG=$5
 
   _load_plugin_config
 
@@ -628,6 +636,7 @@ teardown() {
   fqdn="_acme-challenge.$2"
   txtvalue=$3
   plugin_conf_sting=$4
+  DEBUG=$5
 
   _load_plugin_config
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 01/12] Add Debian Buildsystem config

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 .gitignore   |  5 +
 Makefile | 40 
 debian/changelog |  5 +
 debian/compat|  1 +
 debian/control   | 17 +
 debian/copyright | 16 
 debian/lintian-overrides |  1 +
 debian/rules |  7 +++
 8 files changed, 92 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Makefile
 create mode 100644 debian/changelog
 create mode 100644 debian/compat
 create mode 100644 debian/control
 create mode 100644 debian/copyright
 create mode 100644 debian/lintian-overrides
 create mode 100755 debian/rules

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000..334442b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.deb
+*.dsc
+*.buildinfo
+*.changes
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 000..5ed0522
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+include /usr/share/dpkg/pkg-info.mk
+
+PACKAGE=libproxmox-acme-perl
+
+BUILDDIR ?= ${PACKAGE}-${DEB_VERSION_UPSTREAM}
+GITVERSION:=$(shell git rev-parse HEAD)
+
+DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_all.deb
+DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc
+
+all: $(DEB)
+
+$(BUILDDIR): src debian
+   rm -rf $(BUILDDIR)
+   rsync -a src/ debian $(BUILDDIR)
+   # remove if repository exists
+   # echo "git clone git://git.proxmox.com/git/proxmox-acme\\ngit checkout 
$(GITVERSION)" > $(BUILDDIR)/debian/SOURCE
+
+.PHONY: deb
+deb: $(DEB)
+$(DEB): $(BUILDDIR)
+   cd $(BUILDDIR); dpkg-buildpackage -b -us -uc
+   lintian $(DEB)
+
+.PHONY: dsc
+dsc: ${DSC}
+${DSC}: ${BUILDDIR}
+   cd ${BUILDDIR}; dpkg-buildpackage -S -us -uc -d
+   lintian ${DSC}
+
+dinstall: $(DEB)
+   dpkg -i $(DEB)
+
+.PHONY: clean
+clean:
+   rm -rf $(BUILDDIR) *.deb *.buildinfo *.changes *.dsc *.tar.gz
+
+.PHONY: upload
+upload: ${DEB}
+   tar cf - ${DEB}|ssh -X repo...@repo.proxmox.com -- upload --product 
pve,pmg --dist buster --arch ${DEB_BUILD_ARCH}
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 000..8edee45
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+libproxmox-acme-perl (1.0.0-1+pvetest1) unstable; urgency=medium
+
+  * Initial 1.0.0 package
+
+ -- Proxmox Support Team   Wed, 06 Nov 2019 09:14:59 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 000..f599e28
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+10
diff --git a/debian/control b/debian/control
new file mode 100644
index 000..87ba731
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,17 @@
+Source: libproxmox-acme-perl
+Section: admin
+Priority: optional
+Maintainer: Proxmox Support Team 
+Build-Depends: debhelper (>= 10),
+   dh-systemd,
+Standards-Version: 1.0.0
+Homepage: https://www.proxmox.com
+
+Package: libproxmox-acme-perl
+Architecture: all
+Description: easy and small shell script to automatically issue
+ and renew the free certificates from Let's Encrypt.
+Depends: curl (>= 7.64.0-1),
+coreutils (>= 8.30-1),
+sed (>= 4.7-1)
+Recommends: idn
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 000..7466db1
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,16 @@
+Copyright (C) 2010-2019 Proxmox Server Solutions GmbH
+
+This software is written by Proxmox Server Solutions GmbH 
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/debian/lintian-overrides b/debian/lintian-overrides
new file mode 100644
index 000..4f3e835
--- /dev/null
+++ b/debian/lintian-overrides
@@ -0,0 +1 @@
+libproxmox-acme-perl: script-not-executable
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 000..f00dbc2
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+# See debhelper(7) (uncomment to enable)
+# output every command that modifies files on the build system.
+#export DH_VERBOSE = 1
+
+%:
+   dh $@
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 11/12] Add DNSChallenge Plugin

2020-03-31 Thread Wolfgang Link
This plugin calls the custom script acme.sh and uses the implementation of the 
DNS API.

Signed-off-by: Wolfgang Link 
---
 debian/control   |   3 +-
 src/Makefile |   1 +
 src/PVE/ACME.pm  |   1 +
 src/PVE/ACME/DNSChallenge.pm | 197 +++
 4 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/ACME/DNSChallenge.pm

diff --git a/debian/control b/debian/control
index 87ba731..bb85c98 100644
--- a/debian/control
+++ b/debian/control
@@ -13,5 +13,6 @@ Description: easy and small shell script to automatically 
issue
  and renew the free certificates from Let's Encrypt.
 Depends: curl (>= 7.64.0-1),
 coreutils (>= 8.30-1),
-sed (>= 4.7-1)
+sed (>= 4.7-1),
+libpve-common-perl,
 Recommends: idn
diff --git a/src/Makefile b/src/Makefile
index c56a354..11b35ff 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -107,6 +107,7 @@ LIB_SOURCES = \
ACME.pm \
ACME/Challenge.pm \
ACME/StandAlone.pm \
+   ACME/DNSChallenge.pm \
 
 all:
 
diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
index 7c0794a..86fb9b0 100644
--- a/src/PVE/ACME.pm
+++ b/src/PVE/ACME.pm
@@ -25,6 +25,7 @@ file_get_contents
 
 use PVE::ACME::Challenge;
 use PVE::ACME::StandAlone;
+use PVE::ACME::DNSChallenge;
 
 Crypt::OpenSSL::RSA->import_random_seed();
 
diff --git a/src/PVE/ACME/DNSChallenge.pm b/src/PVE/ACME/DNSChallenge.pm
new file mode 100644
index 000..77d143f
--- /dev/null
+++ b/src/PVE/ACME/DNSChallenge.pm
@@ -0,0 +1,197 @@
+package PVE::ACME::DNSChallenge;
+
+use strict;
+use warnings;
+
+use Digest::SHA qw(sha256);
+use PVE::Tools;
+
+use base qw(PVE::ACME::Challenge);
+
+my $ACME_PATH = '/usr/share/proxmox-ve/proxmox-acme/proxmox-acme';
+
+sub supported_challenge_types {
+return { 'dns-01' => 1 };
+}
+
+sub type {
+return 'dns';
+}
+
+my $api_name_list = [
+'acmedns',
+'acmeproxy',
+'active24',
+'ad',
+'ali',
+'autodns',
+'aws',
+'azure',
+'cf',
+'clouddns',
+'cloudns',
+'cn',
+'conoha',
+'constellix',
+'cx',
+'cyon',
+'da',
+'ddnss',
+'desec',
+'dgon',
+'dnsimple',
+'do',
+'doapi',
+'domeneshop',
+'dp',
+'dpi',
+'dreamhost',
+'duckdns',
+'durabledns',
+'dyn',
+'dynu',
+'dynv6',
+'easydns',
+'euserv',
+'exoscale',
+'freedns',
+'gandi_livedns',
+'gcloud',
+'gd',
+'gdnsdk',
+'he',
+'hexonet',
+'hostingde',
+'infoblox',
+'internetbs',
+'inwx',
+'ispconfig',
+'jd',
+'kas',
+'kinghost',
+'knot',
+'leaseweb',
+'lexicon',
+'linode',
+'linode_v4',
+'loopia',
+'lua',
+'maradns',
+'me',
+'miab',
+'misaka',
+'myapi',
+'mydevil',
+'mydnsjp',
+'namecheap',
+'namecom',
+'namesilo',
+'nederhost',
+'neodigit',
+'netcup',
+'nic',
+'nsd',
+'nsone',
+'nsupdate',
+'nw',
+'one',
+'online',
+'openprovider',
+'opnsense',
+'ovh',
+'pdns',
+'pleskxml',
+'pointhq',
+'rackspace',
+'rcode0',
+'regru',
+'schlundtech',
+'selectel',
+'servercow',
+'tele3',
+'ultra',
+'unoeuro',
+'variomedia',
+'vscale',
+'vultr',
+'yandex',
+'zilore',
+'zone',
+'zonomi',
+];
+
+sub properties {
+return {
+   api => {
+   description => "API plugin name",
+   type => 'string',
+   enum => $api_name_list,
+   },
+   data => {
+   type => 'string',
+   description => 'DNS plugin data.',
+   },
+};
+}
+
+sub options {
+return {
+   api => {},
+   data => {},
+   nodes => { optional => 1 },
+   disable => { optional => 1 },
+};
+}
+
+my $outfunc = sub {
+my $line = shift;
+print "$line\n";
+};
+
+# The order of the parameters passed to proxmox-acme is important
+# proxmox-acme setup $plugin [$domain|$alias] $txtvalue $plugin_conf_string
+sub setup {
+my ($class, $acme, $authorization, $plugin) = @_;
+
+my $plugin_conf_string = PVE::Tools::decode_text($plugin->{data});
+
+my $dns_challenge =
+   PVE::ACME::extract_challenge($authorization->{'challenges'}, "dns-01");
+my $url = $dns_challenge->{'url'};
+
+my $domain = $plugin->{alias} ?
+   $plugin->{'alias'} : $authorization->{'identifier'}->{'value'};
+
+my $key_auth = $acme->key_authorization($dns_challenge->{'token'});
+my $txtvalue = PVE::ACME::encode(sha256($key_auth));
+
+my $dns_plugin = $plugin->{api};
+my $cmd = ["bash", $ACME_PATH, "setup", $dns_plugin, $domain];
+push @$cmd, $txtvalue, $plugin_conf_string;
+
+PVE::Tools::run_command($cmd, outfunc => $outfunc);
+print "Add TXT

[pve-devel] [Patch V2 acme 03/12] Remove unnecessary Code and fixes.

2020-03-31 Thread Wolfgang Link
This Code is not required in the Proxmox environment.
We know in our environment what we have as a tool-change.

Fix Code what does not work because variable or functions are missing.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 476 ---
 1 file changed, 120 insertions(+), 356 deletions(-)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index 007c58d..662c39a 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -1,49 +1,27 @@
 #!/usr/bin/env sh
 
-# copied from acme.sh
-# Usage: multiline
+HTTP_HEADER="$(mktemp)"
+
 _base64() {
-  [ "" ] #urgly
-  if [ "$1" ]; then
-_debug3 "base64 multiline:'$1'"
-${ACME_OPENSSL_BIN:-openssl} base64 -e
-  else
-_debug3 "base64 single line."
-${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
-  fi
+  openssl base64 -e | tr -d '\r\n'
 }
 
-# Usage: multiline
 _dbase64() {
-  if [ "$1" ]; then
-${ACME_OPENSSL_BIN:-openssl} base64 -d -A
-  else
-${ACME_OPENSSL_BIN:-openssl} base64 -d
-  fi
+  openssl base64 -d
 }
 
 # Usage: hashalg  [outputhex]
 # Output Base64-encoded digest
 _digest() {
   alg="$1"
-  if [ -z "$alg" ]; then
-_usage "Usage: _digest hashalg"
-return 1
-  fi
-
-  outputhex="$2"
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
-if [ "$outputhex" ]; then
-  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' 
'
+if [ "$2" ]; then
+  openssl dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
 else
-  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
+  openssl dgst -"$alg" -binary | _base64
 fi
-  else
-_err "$alg is not supported yet"
-return 1
   fi
-
 }
 
 _upper_case() {
@@ -80,11 +58,6 @@ _getfield() {
   _findex="$2"
   _sep="$3"
 
-  if [ -z "$_findex" ]; then
-_usage "Usage: str field  [sep]"
-return 1
-  fi
-
   if [ -z "$_sep" ]; then
 _sep=","
   fi
@@ -105,20 +78,12 @@ _getfield() {
 
 _exists() {
   cmd="$1"
-  if [ -z "$cmd" ]; then
-_usage "Usage: _exists cmd"
-return 1
-  fi
-
   if eval type type >/dev/null 2>&1; then
-eval type "$cmd" >/dev/null 2>&1
-  elif command >/dev/null 2>&1; then
+type "$cmd" >/dev/null 2>&1
+  else command
 command -v "$cmd" >/dev/null 2>&1
-  else
-which "$cmd" >/dev/null 2>&1
   fi
   ret="$?"
-  _debug3 "$cmd exists=$ret"
   return $ret
 }
 
@@ -134,59 +99,6 @@ _egrep_o() {
   fi
 }
 
-_inithttp() {
-
-  if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
-HTTP_HEADER="$(_mktemp)"
-_debug2 HTTP_HEADER "$HTTP_HEADER"
-  fi
-
-  if [ "$__HTTP_INITIALIZED" ]; then
-if [ "$_ACME_CURL$_ACME_WGET" ]; then
-  _debug2 "Http already initialized."
-  return 0
-fi
-  fi
-
-  if [ -z "$_ACME_CURL" ] && _exists "curl"; then
-_ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER "
-if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
-  _CURL_DUMP="$(_mktemp)"
-  _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
-fi
-
-if [ "$CA_PATH" ]; then
-  _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
-elif [ "$CA_BUNDLE" ]; then
-  _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
-fi
-
-if _contains "$(curl --help 2>&1)" "--globoff"; then
-  _ACME_CURL="$_ACME_CURL -g "
-fi
-  fi
-
-  if [ -z "$_ACME_WGET" ] && _exists "wget"; then
-_ACME_WGET="wget -q"
-if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
-  _ACME_WGET="$_ACME_WGET -d "
-fi
-if [ "$CA_PATH" ]; then
-  _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
-elif [ "$CA_BUNDLE" ]; then
-  _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
-fi
-  fi
-
-  #from wget 1.14: do not skip body on 404 error
-  if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" 
"--content-on-error"; then
-_ACME_WGET="$_ACME_WGET --content-on-error "
-  fi
-
-  __HTTP_INITIALIZED=1
-
-}
-
 # body  url [needbase64] [POST|PUT|DELETE] [ContentType]
 _post() {
   body="$1"
@@ -198,181 +110,73 @@ _post() {
   if [ -z "$httpmethod" ]; then
 httpmethod="POST"
   fi
-  _debug $httpmethod

[pve-devel] [Patch V2 acme 04/12] Add funtion to set DNSAPI variable

2020-03-31 Thread Wolfgang Link
acme.sh DNS plugins expect a configuration in which the login information
is stored.
We pass the credentials with the command.
This function supports the expected behavior of the plugins.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 13 +
 1 file changed, 13 insertions(+)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index 662c39a..f7a6757 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -568,3 +568,16 @@ _source_plugin_config() {
   return
 }
 
+# Proxmox implementation to inject the DNSAPI variables
+_load_plugin_config() {
+tmp_str="${plugin_conf_sting//[^,]}"
+index="$(_math ${#tmp_str} + 1)"
+while [ "$index" -gt "0" ]
+do
+   field=$(_getfield $plugin_conf_sting "$index" ",")
+   key=$(_getfield $field 1 "=")
+   value=$(_getfield $field 2 "=")
+   eval "$key"="$value"
+   index="$(_math "$index" - 1)"
+done
+}
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 05/12] Implement feature setup and teardown functionality.

2020-03-31 Thread Wolfgang Link
We use these functions to add and remove a txt record via the dnsapi.

Signed-off-by: Wolfgang Link 
---
 src/proxmox-acme | 68 
 1 file changed, 68 insertions(+)

diff --git a/src/proxmox-acme b/src/proxmox-acme
index f7a6757..1cfb8f6 100644
--- a/src/proxmox-acme
+++ b/src/proxmox-acme
@@ -1,5 +1,12 @@
 #!/usr/bin/env sh
 
+VER=0.9
+
+PROJECT_NAME="ProxmoxACME"
+
+USER_AGENT="$PROJECT_NAME/$VER"
+
+DNS_PLUGIN_PATH="/usr/share/proxmox-ve/proxmox-acme/dnsapi"
 HTTP_HEADER="$(mktemp)"
 
 _base64() {
@@ -581,3 +588,64 @@ _load_plugin_config() {
index="$(_math "$index" - 1)"
 done
 }
+
+# call setup and teardown direct
+# the parameter must be set in the correct order
+# $1  DNS Plugin name
+# $2  Fully Qualified Domain Name
+# $3  value for TXT record
+# $4  DNS plugin auth and config parameter separated by ","
+
+setup() {
+  dns_plugin="dns_$1"
+  dns_plugin_path="${DNS_PLUGIN_PATH}/${dns_plugin}.sh"
+  fqdn="_acme-challenge.$2"
+  txtvalue=$3
+  plugin_conf_sting=$4
+
+  _load_plugin_config
+
+  if ! . "$dns_plugin_path"; then
+_err "Load file $dns_plugin error."
+return 1
+  fi
+
+  addcommand="${dns_plugin}_add"
+  if ! _exists "$addcommand"; then
+_err "It seems that your api file is not correct, it must have a function 
named: $addcommand"
+return 1
+  fi
+
+  if ! $addcommand "$fqdn" "$txtvalue"; then
+_err "Error add txt for domain:$fulldomain"
+return 1
+  fi
+}
+
+teardown() {
+  dns_plugin="dns_$1"
+  dns_plugin_path="${DNS_PLUGIN_PATH}/${dns_plugin}.sh"
+  fqdn="_acme-challenge.$2"
+  txtvalue=$3
+  plugin_conf_sting=$4
+
+  _load_plugin_config
+
+  if ! . "$dns_plugin_path"; then
+_err "Load file $dns_plugin error."
+return 1
+  fi
+
+  rmcommand="${dns_plugin}_rm"
+  if ! _exists "$rmcommand"; then
+_err "It seems that your api file is not correct, it must have a function 
named: $rmcommand"
+return 1
+  fi
+
+  if ! $rmcommand "$fqdn" "$txtvalue"; then
+_err "Error add txt for domain:$fulldomain"
+return 1
+  fi
+}
+
+"$@"
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] RFC for ACME DNS Challenge V2

2020-03-31 Thread Wolfgang Link
The acme_sh project is used as a DNS API plugin system.
So we can reuse the already defiend plugins.
I add it as a submodule.

The acme.sh script is replaced by proxmox-acme,
which contains the function required to operate the DNSAPI plug-ins.

The login information is saved in the file plugin.cfg
and passt directly on the proxmox-acme.

The DNSAPI plugin credentials are not standardized, so each plugin expects 
different parameters.

These patches are only tested against the OVH API because of missing 
alternative possibilities.

This implementation uses the design that we discuss at the pve-devel list.
It doesn't have much to do with V1.

Build conflicts arise due to the code movements.
The prerequisite for this series is the installation of Curl.
For this series you have to create the deb packages pve-common, pve-cluster and 
proxmox-acme.
Then apply these packages and you can now build and install the pve-manager 
package.

The GUI is broken at the moment.
Fixes will follow shortly.
Old configurations are converted and can be used without any problems.
The new configuration must be defined via the CLI.

For the alias mode a CNAME record is needed
_acme-challenge...   CNAME   _acme-challenge.

Steps to test.

1.) pvenode acme account register default 
2.) pvenode acme plugin add   --data  
3.) pvenode config set --acme 
domain=,plugin=[,alias=]
4.) pvenode acme cert order



___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 02/12] Copy the needed function form acme.sh

2020-03-31 Thread Wolfgang Link
For the thin wrapper around acme.sh DNS plugins, the required functions are 
copied.
The project acme.sh can be found here.
https://github.com/Neilpang/acme.sh

Signed-off-by: Wolfgang Link 
---
 Makefile |   1 +
 src/proxmox-acme | 806 +++
 2 files changed, 807 insertions(+)
 create mode 100644 src/proxmox-acme

diff --git a/Makefile b/Makefile
index 5ed0522..0a78b63 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ all: $(DEB)
 
 $(BUILDDIR): src debian
rm -rf $(BUILDDIR)
+
rsync -a src/ debian $(BUILDDIR)
# remove if repository exists
# echo "git clone git://git.proxmox.com/git/proxmox-acme\\ngit checkout 
$(GITVERSION)" > $(BUILDDIR)/debian/SOURCE
diff --git a/src/proxmox-acme b/src/proxmox-acme
new file mode 100644
index 000..007c58d
--- /dev/null
+++ b/src/proxmox-acme
@@ -0,0 +1,806 @@
+#!/usr/bin/env sh
+
+# copied from acme.sh
+# Usage: multiline
+_base64() {
+  [ "" ] #urgly
+  if [ "$1" ]; then
+_debug3 "base64 multiline:'$1'"
+${ACME_OPENSSL_BIN:-openssl} base64 -e
+  else
+_debug3 "base64 single line."
+${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
+  fi
+}
+
+# Usage: multiline
+_dbase64() {
+  if [ "$1" ]; then
+${ACME_OPENSSL_BIN:-openssl} base64 -d -A
+  else
+${ACME_OPENSSL_BIN:-openssl} base64 -d
+  fi
+}
+
+# Usage: hashalg  [outputhex]
+# Output Base64-encoded digest
+_digest() {
+  alg="$1"
+  if [ -z "$alg" ]; then
+_usage "Usage: _digest hashalg"
+return 1
+  fi
+
+  outputhex="$2"
+
+  if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
+if [ "$outputhex" ]; then
+  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' 
'
+else
+  ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
+fi
+  else
+_err "$alg is not supported yet"
+return 1
+  fi
+
+}
+
+_upper_case() {
+  # shellcheck disable=SC2018,SC2019
+  tr 'a-z' 'A-Z'
+}
+
+_lower_case() {
+  # shellcheck disable=SC2018,SC2019
+  tr 'A-Z' 'a-z'
+}
+
+_startswith() {
+  _str="$1"
+  _sub="$2"
+  echo "$_str" | grep "^$_sub" >/dev/null 2>&1
+}
+
+_endswith() {
+  _str="$1"
+  _sub="$2"
+  echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
+}
+
+_contains() {
+  _str="$1"
+  _sub="$2"
+  echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
+}
+
+# str index [sep]
+_getfield() {
+  _str="$1"
+  _findex="$2"
+  _sep="$3"
+
+  if [ -z "$_findex" ]; then
+_usage "Usage: str field  [sep]"
+return 1
+  fi
+
+  if [ -z "$_sep" ]; then
+_sep=","
+  fi
+
+  _ffi="$_findex"
+  while [ "$_ffi" -gt "0" ]; do
+_fv="$(echo "$_str" | cut -d "$_sep" -f "$_ffi")"
+if [ "$_fv" ]; then
+  printf -- "%s" "$_fv"
+  return 0
+fi
+_ffi="$(_math "$_ffi" - 1)"
+  done
+
+  printf -- "%s" "$_str"
+
+}
+
+_exists() {
+  cmd="$1"
+  if [ -z "$cmd" ]; then
+_usage "Usage: _exists cmd"
+return 1
+  fi
+
+  if eval type type >/dev/null 2>&1; then
+eval type "$cmd" >/dev/null 2>&1
+  elif command >/dev/null 2>&1; then
+command -v "$cmd" >/dev/null 2>&1
+  else
+which "$cmd" >/dev/null 2>&1
+  fi
+  ret="$?"
+  _debug3 "$cmd exists=$ret"
+  return $ret
+}
+
+# a + b
+_math() {
+  _m_opts="$@"
+  printf "%s" "$(($_m_opts))"
+}
+
+_egrep_o() {
+  if ! egrep -o "$1" 2>/dev/null; then
+sed -n 's/.*\('"$1"'\).*/\1/p'
+  fi
+}
+
+_inithttp() {
+
+  if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
+HTTP_HEADER="$(_mktemp)"
+_debug2 HTTP_HEADER "$HTTP_HEADER"
+  fi
+
+  if [ "$__HTTP_INITIALIZED" ]; then
+if [ "$_ACME_CURL$_ACME_WGET" ]; then
+  _debug2 "Http already initialized."
+  return 0
+fi
+  fi
+
+  if [ -z "$_ACME_CURL" ] && _exists "curl"; then
+_ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER "
+if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
+  _CURL_DUMP="$(_mktemp)"
+  _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
+fi
+
+if [ "$CA_PATH" ]; then
+  _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
+elif [

[pve-devel] [Patch V2 manager 3/8] Remove unused code

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 PVE/NodeConfig.pm | 7 ---
 1 file changed, 7 deletions(-)

diff --git a/PVE/NodeConfig.pm b/PVE/NodeConfig.pm
index 94f7287e..6a75ee32 100644
--- a/PVE/NodeConfig.pm
+++ b/PVE/NodeConfig.pm
@@ -235,13 +235,6 @@ sub parse_acme {
 return $res;
 }
 
-sub print_acme {
-my ($acme) = @_;
-
-$acme->{domains} = join(';', $acme->{domains}) if $acme->{domains};
-return PVE::JSONSchema::print_property_string($acme, $acmedesc);
-}
-
 sub get_nodeconfig_schema {
 for my $i (1..$MAXDOMAINS) {
$confdesc->{"acme_additional_domain$i"} = {
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 common 1/3] Move the code to proxmox-acme and add a dependency on it.

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 debian/control |   1 +
 src/PVE/ACME.pm| 533 -
 src/PVE/ACME/Challenge.pm  |  22 --
 src/PVE/ACME/StandAlone.pm |  71 -
 4 files changed, 1 insertion(+), 626 deletions(-)
 delete mode 100644 src/PVE/ACME.pm
 delete mode 100644 src/PVE/ACME/Challenge.pm
 delete mode 100644 src/PVE/ACME/StandAlone.pm

diff --git a/debian/control b/debian/control
index c467dd6..8faa22e 100644
--- a/debian/control
+++ b/debian/control
@@ -32,6 +32,7 @@ Depends: libclone-perl,
  libstring-shellquote-perl,
  liburi-perl,
  libwww-perl,
+libproxmox-acme-perl,
  ${misc:Depends},
  ${perl:Depends},
 Breaks: ifupdown2 (<< 2.0.1-1+pve5),
diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
deleted file mode 100644
index 114eb41..000
--- a/src/PVE/ACME.pm
+++ /dev/null
@@ -1,533 +0,0 @@
-package PVE::ACME;
-
-use strict;
-use warnings;
-
-use POSIX;
-
-use Data::Dumper;
-use Date::Parse;
-use MIME::Base64 qw(encode_base64url);
-use File::Path qw(make_path);
-use JSON;
-use Digest::SHA qw(sha256 sha256_hex);
-
-use HTTP::Request;
-use LWP::UserAgent;
-
-use Crypt::OpenSSL::RSA;
-
-use PVE::Certificate;
-use PVE::Tools qw(
-file_set_contents
-file_get_contents
-);
-
-Crypt::OpenSSL::RSA->import_random_seed();
-
-my $LETSENCRYPT_STAGING = 
'https://acme-staging-v02.api.letsencrypt.org/directory';
-
-### ACME library (compatible with Let's Encrypt v2 API)
-#
-# sample usage:
-#
-# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
-# 2) $acme->init(4096); # generate account key
-# 4) my $tos_url = $acme->get_meta()->{termsOfService}; # optional, display if 
applicable
-# 5) $acme->new_account($tos_url, contact => ['mailto:exam...@example.com']);
-#
-# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
-# 2) $acme->load();
-# 3) my ($order_url, $order) = $acme->new_order(['foo.example.com', 
'bar.example.com']);
-# 4) # repeat a-f for each $auth_url in $order->{authorizations}
-# a) my $authorization = $acme->get_authorization($auth_url);
-# b) # pick $challenge from $authorization->{challenges} according to desired 
type
-# c) my $key_auth = $acme->key_authorization($challenge->{token});
-# d) # setup challenge validation according to specification
-# e) $acme->request_challenge_validation($challenge->{url}, $key_auth);
-# f) # poll $acme->get_authorization($auth_url) until status is 'valid'
-# 5) # generate CSR in PEM format
-# 6) $acme->finalize_order($order, $csr);
-# 7) # poll $acme->get_order($order_url) until status is 'valid'
-# 8) my $cert = $acme->get_certificate($order);
-# 9) # $key is path to key file, $cert contains PEM-encoded certificate chain
-#
-# 1) my $acme = PVE::ACME->new('path/to/account.json', 'API directory URL');
-# 2) $acme->load();
-# 3) $acme->revoke_certificate($cert);
-
-# Tools
-sub encode($) { # acme requires 'base64url' encoding
-return encode_base64url($_[0]);
-}
-
-sub tojs($;%) { # shortcut for to_json with utf8=>1
-my ($data, %data) = @_;
-return to_json($data, { utf8 => 1, %data });
-}
-
-sub fromjs($) {
-return from_json($_[0]);
-}
-
-sub fatal($$;$$) {
-my ($self, $msg, $dump, $noerr) = @_;
-
-warn Dumper($dump), "\n" if $self->{debug} && $dump;
-if ($noerr) {
-   warn "$msg\n";
-} else {
-   die "$msg\n";
-}
-}
-
-# Implementation
-
-# $path: account JSON file
-# $directory: the ACME directory URL used to find method URLs
-sub new($$$) {
-my ($class, $path, $directory) = @_;
-
-$directory //= $LETSENCRYPT_STAGING;
-
-my $ua = LWP::UserAgent->new();
-$ua->env_proxy();
-$ua->agent('pve-acme/0.1');
-$ua->protocols_allowed(['https']);
-
-my $self = {
-   ua => $ua,
-   path => $path,
-   directory => $directory,
-   nonce => undef,
-   key => undef,
-   location => undef,
-   account => undef,
-   tos => undef,
-};
-
-return bless $self, $class;
-}
-
-# RS256: PKCS#1 padding, no OAEP, SHA256
-my $configure_key = sub {
-my ($key) = @_;
-$key->use_pkcs1_padding();
-$key->use_sha256_hash();
-};
-
-# Create account key with $keybits bits
-# use instead of load, overwrites existing account JSON file!
-sub init {
-my ($self, $keybits) = @_;
-die "Already have a key\n" if defined($self->{key});
-$keybits //= 4096;
-my $key = Crypt::OpenSSL::RSA->generate_key($keybits);
-$configure_key->($key);
-$self->{key} = $key;
-$self->save();
-}
-
-my @SAVED_VALUES = qw(location account tos debug directory);
-# Serialize persistent parts of $self to $self->{path} as JSON
-sub save {
-my ($self) = @_;
-my $o = {};
-my $keystr;
-if (my $key = $self->{key}) {
- 

[pve-devel] [Patch V2 acme 10/12] Refactor extract_callenge for code reuse.

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME.pm| 15 +++
 src/PVE/ACME/StandAlone.pm |  2 ++
 2 files changed, 17 insertions(+)

diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
index c10dca2..7c0794a 100644
--- a/src/PVE/ACME.pm
+++ b/src/PVE/ACME.pm
@@ -573,4 +573,19 @@ sub do {
 return $res;
 }
 
+sub extract_challenge ($$) {
+my ($challenges, $c_type) = @_;
+
+die "no challenges defined\n" if !$challenges;
+die "no challenge type is defined \n" if !$c_type;
+
+my $tmp_challenges = [ grep {$_->{type} eq $c_type} @$challenges ];
+die "no $c_type challenge defined in authorization\n"
+   if ! scalar $tmp_challenges;
+
+my $challenge = $tmp_challenges->[0];
+
+return $challenge;
+}
+
 1;
diff --git a/src/PVE/ACME/StandAlone.pm b/src/PVE/ACME/StandAlone.pm
index 8fc8dc9..38db3f8 100644
--- a/src/PVE/ACME/StandAlone.pm
+++ b/src/PVE/ACME/StandAlone.pm
@@ -6,6 +6,8 @@ use warnings;
 use HTTP::Daemon;
 use HTTP::Response;
 
+use PVE::ACME;
+
 use base qw(PVE::ACME::Challenge);
 
 sub supported_challenge_types {
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 acme 08/12] Create the plugin config.

2020-03-31 Thread Wolfgang Link
At the moment, Proxmox has two different configurations that require different 
properties.
DNSChallange requires credentials for the DNSAPI.
Standalone has no settings because Letsencrypt only supports port 80 with the 
http-01 challenge.

Make Standalone.pm Plugin compliant.

Signed-off-by: Wolfgang Link 
---
 src/PVE/ACME/Challenge.pm  | 62 ++
 src/PVE/ACME/StandAlone.pm | 16 ++
 2 files changed, 78 insertions(+)

diff --git a/src/PVE/ACME/Challenge.pm b/src/PVE/ACME/Challenge.pm
index 40d32b6..b261476 100644
--- a/src/PVE/ACME/Challenge.pm
+++ b/src/PVE/ACME/Challenge.pm
@@ -3,16 +3,78 @@ package PVE::ACME::Challenge;
 use strict;
 use warnings;
 
+use PVE::Cluster qw(cfs_register_file);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(lock_file);
+
+use base qw(PVE::SectionConfig);
+
+my $FILENAME = "/etc/pve/priv/plugins.cfg";
+
+cfs_register_file ('priv/plugins.cfg',
+  sub { __PACKAGE__->parse_config(@_); },
+  sub { __PACKAGE__->write_config(@_); });
+
+my $defaultData = {
+additionalProperties => 0,
+propertyList => {
+   id => {
+   description => "ACME Plugin ID name",
+   type => 'string',
+   },
+   type => {
+   description => "ACME challenge type.",
+   type => 'string',
+   },
+   nodes => get_standard_option('pve-node-list', { optional => 1 }),
+   disable => {
+   description => "Flag to disable the config.",
+   type => 'boolean',
+   optional => 1,
+   },
+},
+};
+
+sub private {
+return $defaultData;
+}
+
 sub supported_challenge_types {
 return {};
 }
 
+sub load_config {
+
+my $raw = eval { PVE::Tools::file_get_contents($FILENAME); };
+return {} if !$raw;
+
+return __PACKAGE__->parse_config($FILENAME, $raw);
+}
+
+sub write_conf {
+my ($conf) = @_;
+
+my $raw = __PACKAGE__->write_config($FILENAME, $conf);
+
+PVE::Tools::file_set_contents($FILENAME, $raw);
+}
+
 sub setup {
 my ($class, $acme, $authorization) = @_;
 
 die "implement me\n";
 }
 
+sub lock_config {
+my ($code, @param) = @_;
+
+my $res = lock_file($FILENAME, 3, $code, @param);
+
+die $@ if $@;
+
+return $res;
+}
+
 sub teardown {
 my ($self) = @_;
 
diff --git a/src/PVE/ACME/StandAlone.pm b/src/PVE/ACME/StandAlone.pm
index f48d638..ac75184 100644
--- a/src/PVE/ACME/StandAlone.pm
+++ b/src/PVE/ACME/StandAlone.pm
@@ -12,6 +12,22 @@ sub supported_challenge_types {
 return { 'http-01' => 1 };
 }
 
+sub type {
+return 'standalone';
+}
+
+sub properties {
+return {
+};
+}
+
+sub options {
+return {
+   nodes => { optional => 1 },
+   disable => { optional => 1 },
+};
+}
+
 sub setup {
 my ($class, $acme, $authorization) = @_;
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 manager 1/8] Use the plugin architecture.

2020-03-31 Thread Wolfgang Link
And remove the call of standalone plugin directly.

Signed-off-by: Wolfgang Link 
---
 PVE/API2/ACME.pm | 5 +
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/PVE/API2/ACME.pm b/PVE/API2/ACME.pm
index b1bb6261..8bd6a924 100644
--- a/PVE/API2/ACME.pm
+++ b/PVE/API2/ACME.pm
@@ -4,7 +4,6 @@ use strict;
 use warnings;
 
 use PVE::ACME;
-use PVE::ACME::StandAlone;
 use PVE::CertHelpers;
 use PVE::Certificate;
 use PVE::Exception qw(raise raise_param_exc);
@@ -58,9 +57,7 @@ my $order_certificate = sub {
print "... already validated!\n";
} else {
print "... pending!\n";
-   print "Setting up webserver\n";
-   my $validation = eval { PVE::ACME::StandAlone->setup($acme, $auth) 
};
-   die "failed setting up webserver - $@\n" if $@;
+   my $validation = PVE::ACME::setup($acme, $auth);
 
print "Triggering validation\n";
eval {
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 manager 7/8] Add the ACME plugin capabilities to the API.

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 PVE/CLI/pvenode.pm | 8 +++-
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/PVE/CLI/pvenode.pm b/PVE/CLI/pvenode.pm
index d9e41a8e..e8c2005e 100644
--- a/PVE/CLI/pvenode.pm
+++ b/PVE/CLI/pvenode.pm
@@ -5,12 +5,11 @@ use warnings;
 
 use PVE::API2::ACME;
 use PVE::API2::ACMEAccount;
-use PVE::API2::ACMEPlugin;
 use PVE::API2::Certificates;
 use PVE::API2::NodeConfig;
 use PVE::API2::Nodes;
+use PVE::API2::ACMEPlugin; 
 use PVE::API2::Tasks;
-
 use PVE::CertHelpers;
 use PVE::Certificate;
 use PVE::Exception qw(raise_param_exc raise);
@@ -211,13 +210,12 @@ our $cmddef = {
plugin => {
get => [ 'PVE::API2::ACMEPlugin', 'get_plugin_options', [], {},
 sub {
-my $line = shift;
-print $line;
+   my $line = shift;
+   print $line;
 } ],
add => [ 'PVE::API2::ACMEPlugin', 'add_plugin', ['type', 'id'] ],
del => [ 'PVE::API2::ACMEPlugin', 'delete_plugin', ['id'] ],
},
-
 },
 
 wakeonlan => [ 'PVE::API2::Nodes::Nodeinfo', 'wakeonlan', [ 'node' ], {}, 
sub {
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 manager 4/8] Add the config parser and convert the old config to a new one.

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 PVE/API2/ACME.pm  | 16 +--
 PVE/NodeConfig.pm | 67 +--
 2 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/PVE/API2/ACME.pm b/PVE/API2/ACME.pm
index 8bd6a924..ba25d153 100644
--- a/PVE/API2/ACME.pm
+++ b/PVE/API2/ACME.pm
@@ -46,9 +46,9 @@ __PACKAGE__->register_method ({
 }});
 
 my $order_certificate = sub {
-my ($acme, $domains) = @_;
+my ($acme, $acme_node_config) = @_;
 print "Placing ACME order\n";
-my ($order_url, $order) = $acme->new_order($domains);
+my ($order_url, $order) = $acme->new_order($acme_node_config->{domains});
 print "Order URL: $order_url\n";
 for my $auth_url (@{$order->{authorizations}}) {
print "\nGetting authorization details from '$auth_url'\n";
@@ -57,7 +57,7 @@ my $order_certificate = sub {
print "... already validated!\n";
} else {
print "... pending!\n";
-   my $validation = PVE::ACME::setup($acme, $auth);
+   my $validation = PVE::ACME::setup($acme, $auth, $acme_node_config);
 
print "Triggering validation\n";
eval {
@@ -166,7 +166,7 @@ __PACKAGE__->register_method ({
my $node_config = PVE::NodeConfig::load_config($node);
raise("ACME settings in node configuration are missing!", 400)
if !$node_config || !$node_config->{acme};
-   my $acme_node_config = 
PVE::NodeConfig::parse_acme($node_config->{acme});
+   my $acme_node_config = PVE::NodeConfig::parse_acme($node_config);
raise("ACME domain list in node configuration is missing!", 400)
if !$acme_node_config;
 
@@ -186,7 +186,7 @@ __PACKAGE__->register_method ({
print "Loading ACME account details\n";
$acme->load();
 
-   my ($cert, $key) = $order_certificate->($acme, 
$acme_node_config->{domains});
+   my ($cert, $key) = $order_certificate->($acme, $acme_node_config);
 
my $code = sub {
print "Setting pveproxy certificate and key\n";
@@ -240,7 +240,7 @@ __PACKAGE__->register_method ({
my $node_config = PVE::NodeConfig::load_config($node);
raise("ACME settings in node configuration are missing!", 400)
if !$node_config || !$node_config->{acme};
-   my $acme_node_config = 
PVE::NodeConfig::parse_acme($node_config->{acme});
+   my $acme_node_config = PVE::NodeConfig::parse_acme($node_config);
raise("ACME domain list in node configuration is missing!", 400)
if !$acme_node_config;
 
@@ -262,7 +262,7 @@ __PACKAGE__->register_method ({
print "Loading ACME account details\n";
$acme->load();
 
-   my ($cert, $key) = $order_certificate->($acme, 
$acme_node_config->{domains});
+   my ($cert, $key) = $order_certificate->($acme, $acme_node_config);
 
my $code = sub {
print "Setting pveproxy certificate and key\n";
@@ -306,7 +306,7 @@ __PACKAGE__->register_method ({
my $node_config = PVE::NodeConfig::load_config($node);
raise("ACME settings in node configuration are missing!", 400)
if !$node_config || !$node_config->{acme};
-   my $acme_node_config = 
PVE::NodeConfig::parse_acme($node_config->{acme});
+   my $acme_node_config = PVE::NodeConfig::parse_acme($node_config);
raise("ACME domain list in node configuration is missing!", 400)
if !$acme_node_config;
 
diff --git a/PVE/NodeConfig.pm b/PVE/NodeConfig.pm
index 6a75ee32..78827ded 100644
--- a/PVE/NodeConfig.pm
+++ b/PVE/NodeConfig.pm
@@ -8,6 +8,7 @@ use Storable qw(dclone);
 use PVE::CertHelpers;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Tools qw(file_get_contents file_set_contents lock_file);
+use PVE::INotify;
 
 # regitster up to 20 domain names
 my $MAXDOMAINS = 20;
@@ -115,6 +116,7 @@ $acmedesc->{domains} = {
 format => 'pve-acme-domain-list',
 optional => 1,
 };
+$acmedesc->{domain}->{optional} = 1;
 PVE::JSONSchema::register_format('pve-acme-node-conf', $acmedesc);
 
 $confdesc->{acme} = {
@@ -219,18 +221,79 @@ sub write_node_config {
 return $raw;
 }
 
+my $convert_domains = sub {
+my ($node, $conf) = @_;
+
+$conf = load_config($node);
+
+my $acme = PVE::JSONSchema::parse_property_string($acmedesc, 
$conf->{acme});
+my $domainstring = delete $acme->{domains};
+die "No Domains to convert found.\n" if !defined($domainstring);
+# Extract domain list
+my @domains = ( PVE::Tools::split_list($domainstring) );
+
+$acme->{domain} = $domains[0];
+$conf->{acme} = PVE::JSONSchema::print_property_string($acme, $acmedesc);
+
+for m

[pve-devel] [Patch V2 manager 5/8] Add libproxmox-acme-perl to pveversion

2020-03-31 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 PVE/API2/APT.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/PVE/API2/APT.pm b/PVE/API2/APT.pm
index 08345ec9..0bb9cf0f 100644
--- a/PVE/API2/APT.pm
+++ b/PVE/API2/APT.pm
@@ -550,6 +550,7 @@ __PACKAGE__->register_method({
libpve-guest-common-perl
libpve-http-server-perl
libpve-storage-perl
+   libproxmox-acme-perl
libqb0
libspice-server1
lvm2
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 manager 6/8] Create ACME Plugin config.

2020-03-31 Thread Wolfgang Link
With this configuration it is possible to use many different plugins
with different providers and users.

Signed-off-by: Wolfgang Link 
---
 PVE/API2/ACMEPlugin.pm | 120 +
 PVE/API2/Cluster.pm|   6 +++
 PVE/API2/Makefile  |   1 +
 PVE/CLI/pvenode.pm |  11 
 4 files changed, 138 insertions(+)
 create mode 100644 PVE/API2/ACMEPlugin.pm

diff --git a/PVE/API2/ACMEPlugin.pm b/PVE/API2/ACMEPlugin.pm
new file mode 100644
index ..46d9b19e
--- /dev/null
+++ b/PVE/API2/ACMEPlugin.pm
@@ -0,0 +1,120 @@
+package PVE::API2::ACMEPlugin;
+
+use strict;
+use warnings;
+
+use PVE::ACME::Challenge;
+use PVE::Tools qw(extract_param);
+
+PVE::ACME::DNSChallenge->register();
+PVE::ACME::StandAlone->register();
+PVE::ACME::Challenge->init();
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method({
+name => 'get_plugin_options',
+path => 'plugin',
+method => 'GET',
+description => "Get ACME DNS plugin configuration options.",
+permissions => {
+   check => ['perm', '/', [ 'Sys.Audit' ]],
+},
+parameters => {
+   additionalProperties => 0,
+   properties => {
+   },
+},
+returns => {
+   type => 'string',
+},
+code => sub {
+
+   my $config = PVE::ACME::Challenge::load_config();
+   my $line = '';
+   foreach my $ids (sort (keys %{$config->{ids}})) {
+   $line .= "name: $ids\n";
+   foreach my $k (sort (keys %{$config->{ids}->{$ids}})) {
+   my $v = $config->{ids}->{$ids}->{$k};
+   if ($k eq 'data') {
+   $v = PVE::Tools::encode_text($config->{ids}->{$ids}->{$k});
+   }
+   $line .= "$k: $v\n";
+   }
+   $line .="\n";
+   }
+   return $line;
+}});
+
+my $update_config = sub {
+my ($id, $op, $type, $param) = @_;
+
+my $conf = PVE::ACME::Challenge::load_config();
+
+if ( $op eq "add" ) {
+   die "Section with ID: $id already exists\n"
+   if defined($conf->{ids}->{$id});
+   $conf->{ids}->{$id}->{type} = $type;
+} elsif ($op eq "del") {
+   delete $conf->{ids}->{$id};
+}
+
+foreach my $opt (keys %$param) {
+   $conf->{ids}->{$id}->{$opt} = $param->{$opt};
+}
+
+PVE::ACME::Challenge::write_conf($conf);
+};
+
+__PACKAGE__->register_method({
+name => 'add_plugin',
+path => 'plugin',
+method => 'POST',
+description => "Add ACME DNS plugin configuration.",
+permissions => {
+   check => ['perm', '/', [ 'Sys.Modify' ]],
+},
+protected => 1,
+parameters => PVE::ACME::Challenge->createSchema(),
+returns => { type => "null" },
+code => sub {
+   my ($param) = @_;
+
+   my $id = extract_param($param, 'id');
+   my $type = extract_param($param, 'type');
+
+   PVE::ACME::Challenge::lock_config($update_config, $id, "add", $type, 
$param);
+
+   return undef;
+}});
+
+__PACKAGE__->register_method({
+name => 'delete_plugin',
+path => 'plugin',
+method => 'DELETE',
+description => "Delete ACME DNS plugin configuration.",
+permissions => {
+   check => ['perm', '/', [ 'Sys.Modify' ]],
+},
+protected => 1,
+parameters => {
+   additionalProperties => 0,
+   properties => {
+   id => {
+   description => "Plugin configuration name",
+   type => 'string',
+   },
+   },
+},
+returns => { type => "null" },
+code => sub {
+   my ($param) = @_;
+
+   my $id = extract_param($param, 'id');
+
+   PVE::ACME::Challenge::lock_config($update_config, $id, "del", undef, 
$param);
+
+   return undef;
+}});
+
+1;
diff --git a/PVE/API2/Cluster.pm b/PVE/API2/Cluster.pm
index c802d440..0810da0a 100644
--- a/PVE/API2/Cluster.pm
+++ b/PVE/API2/Cluster.pm
@@ -21,6 +21,7 @@ use PVE::Storage;
 use PVE::Tools qw(extract_param);
 
 use PVE::API2::ACMEAccount;
+use PVE::API2::ACMEPlugin;
 use PVE::API2::Backup;
 use PVE::API2::Cluster::Ceph;
 use PVE::API2::ClusterConfig;
@@ -66,6 +67,11 @@ __PACKAGE__->register_method ({
 path => 'acme',
 });
 
+__PACKAGE__->register_method ({
+subclass => "PVE::API2::ACMEPlugin",
+path => 'acmeplugin',
+});
+
 __PACKAGE__->register_method ({
 subclass => "PVE::API2::Cluster::Ceph",
 path => 'ceph',
diff --git a/PVE/API2/Makefile b/PVE/API2/Makefile
index 8554efa1..28ecc070 100644
--- a/PVE/API2/Makefile
+++ b/PVE/API2/Makefile
@@ -19,6 +19,7 @@ PERLSOURCE =  \
Certificate

[pve-devel] [Patch V2 manager 8/8] Add libproxmox-acme to the dependencies.

2020-03-31 Thread Wolfgang Link
It is a build dependency as it is needed for the man generator.

Signed-off-by: Wolfgang Link 
---
 debian/control | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/debian/control b/debian/control
index ec5267a4..1bb292e9 100644
--- a/debian/control
+++ b/debian/control
@@ -9,6 +9,7 @@ Build-Depends: debhelper (>= 11~),
libpve-access-control (>= 5.1-5),
libpve-cluster-perl,
libpve-cluster-api-perl,
+   libproxmox-acme-perl,
libpve-common-perl,
libpve-guest-common-perl (>= 3.0-3~),
libpve-http-server-perl (>= 2.0-12),
@@ -46,6 +47,7 @@ Depends: apt-transport-https | apt (>= 1.5~),
  libnet-dns-perl,
  libpve-access-control (>= 6.0-6),
  libpve-cluster-perl,
+libproxmox-acme-perl,
  libpve-cluster-api-perl,
  libpve-common-perl (>= 6.0-11),
  libpve-guest-common-perl (>= 3.0-3~),
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 common 2/3] Reuse id parse code.

2020-03-31 Thread Wolfgang Link
The storage_id is the same as the plugin_id.

Signed-off-by: Wolfgang Link 
---
 src/PVE/JSONSchema.pm | 13 +
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/src/PVE/JSONSchema.pm b/src/PVE/JSONSchema.pm
index fa405ac..3eb38eb 100644
--- a/src/PVE/JSONSchema.pm
+++ b/src/PVE/JSONSchema.pm
@@ -180,14 +180,19 @@ PVE::JSONSchema::register_format('pve-storage-id', 
\_storage_id);
 sub parse_storage_id {
 my ($storeid, $noerr) = @_;
 
-if ($storeid !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
+return parse_id($storeid, 'storage', $noerr);
+}
+
+sub parse_id {
+my ($id, $msg, $noerr) = @_;
+
+ if ($id !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
return undef if $noerr;
-   die "storage ID '$storeid' contains illegal characters\n";
+   die "$msg ID '$id' contains illegal characters\n";
 }
-return $storeid;
+return $id;
 }
 
-
 register_format('pve-vmid', \_verify_vmid);
 sub pve_verify_vmid {
 my ($vmid, $noerr) = @_;
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [Patch V2 manager 2/8] Extend node config in the acme section.

2020-03-31 Thread Wolfgang Link
Allow more than one domain entry, but only one domain per entry is allowed.
Before that, the Acme parameter could have multiple domains.

Signed-off-by: Wolfgang Link 
---
 PVE/NodeConfig.pm | 47 ++-
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/PVE/NodeConfig.pm b/PVE/NodeConfig.pm
index 7a663f46..94f7287e 100644
--- a/PVE/NodeConfig.pm
+++ b/PVE/NodeConfig.pm
@@ -3,10 +3,15 @@ package PVE::NodeConfig;
 use strict;
 use warnings;
 
+use Storable qw(dclone);
+
 use PVE::CertHelpers;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Tools qw(file_get_contents file_set_contents lock_file);
 
+# regitster up to 20 domain names
+my $MAXDOMAINS = 20;
+
 my $node_config_lock = '/var/lock/pvenode.lock';
 
 PVE::JSONSchema::register_format('pve-acme-domain', sub {
@@ -77,14 +82,38 @@ my $confdesc = {
 },
 };
 
-my $acmedesc = {
+my $acme_additional_desc = {
 account => get_standard_option('pve-acme-account-name'),
-domains => {
+domain => {
type => 'string',
-   format => 'pve-acme-domain-list',
-   format_description => 'domain[;domain;...]',
-   description => 'List of domains for this node\'s ACME certificate',
+   format => 'pve-acme-domain',
+   format_description => 'domain',
+   description => 'domain for this node ACME certificate',
 },
+plugin => {
+   type => 'string',
+   description => 'The plugin ID, default is standalone http',
+   optional => 1,
+   format_description => 'name of the plugin configuration',
+},
+alias => {
+   type => 'string',
+   format => 'pve-acme-domain',
+   format_description => 'domain',
+   description => 'Alias for the Domain to verify ACME Challenge over DNS',
+   optional => 1,
+},
+};
+PVE::JSONSchema::register_format('pve-acme-additional-node-conf', 
$acme_additional_desc);
+
+my $acmedesc = dclone($acme_additional_desc);
+$acmedesc->{account} = get_standard_option('pve-acme-account-name');
+$acmedesc->{domains} = {
+type => 'string',
+format_description => 'domain[;domain;...]',
+description => 'List of domains for this node\'s ACME certificate',
+format => 'pve-acme-domain-list',
+optional => 1,
 };
 PVE::JSONSchema::register_format('pve-acme-node-conf', $acmedesc);
 
@@ -214,6 +243,14 @@ sub print_acme {
 }
 
 sub get_nodeconfig_schema {
+for my $i (1..$MAXDOMAINS) {
+   $confdesc->{"acme_additional_domain$i"} = {
+   type => 'string',
+   description => 'ACME additional Domain',
+   format => $acmedesc,
+   optional => 1,
+   };
+};
 return $confdesc;
 }
 
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [PATCH docs] update link for YubiKey documentation

2020-05-11 Thread Wolfgang Link
Signed-off-by: Wolfgang Link 
---
 pveum.adoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pveum.adoc b/pveum.adoc
index 7998d16..4fbaa86 100644
--- a/pveum.adoc
+++ b/pveum.adoc
@@ -279,7 +279,7 @@ password into the user's 'Key IDs' field.
 Please refer to the https://developers.yubico.com/OTP/[YubiKey OTP]
 documentation for how to use the
 https://www.yubico.com/products/services-software/yubicloud/[YubiCloud] or
-https://developers.yubico.com/Software_Projects/YubiKey_OTP/YubiCloud_Validation_Servers/[host
+https://developers.yubico.com/Software_Projects/Yubico_OTP/YubiCloud_Validation_Servers/[host
 your own verification server].
 
 [[pveum_user_configured_totp]]
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


Re: [pve-devel] applied: [zsync] fix: check for incremental sync snapshot.

2020-03-19 Thread Wolfgang Link


> On March 19, 2020 1:05 PM Thomas Lamprecht  wrote:
> 
>  
> On 3/19/20 11:51 AM, Fabian Ebner wrote:
> > Hi,
> > this does fix an issue when the receiving side has the most recent 
> > snapshot, but not the 'old_snap' one. And of course testing for 'last_snap' 
> > is correct, since that one is the relevant one for the incremental sync. 
> > With that:
> > 
> > Reviewed-By: Fabian Ebner 
> > Tested-By: Fabian Ebner 
> 
> With that applied, thanks to all.
> Wolfgang you missed your signed-off-by (FYI).

Will do the next time.

> > 
> > Some ideas for further improvements:
> > * If on the destination there are older snapshots but not most recent one, 
> > pve-zsync tries to do a full sync and fails with: "destination has 
> > snapshots". One could get the list of snapshots from the destination, the 
> > list of snapshots from the source and use the most recent common one as the 
> > starting point for an incremental sync (and fail if both sides do have 
> > snapshots but no match).
> > * Knowing the list of snapshots for the destination could also be used to 
> > prevent issuing a useless remote 'zfs destroy' when the snapshot to be 
> > deleted does not exist for the destination.
> 
> Sounds reasonable, IMO.

I've already thought about it, but I wouldn't do it automatically.
More Like this 
pve-zsync cleanup: Removes all snapshots with rep_ on behalf of the source host 
that are not available on the target.
pve-zsync fixSync: searches for the last common synchronization point and 
removes all subsequent ones at the destination

One problem with automatic resolution is that multiple replications can 
accidentally delete snapshots.
> 
> 
> ___
> pve-devel mailing list
> pve-devel@pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [PATCH V3] Revision of the pvesr documentation

2020-03-17 Thread Wolfgang Link
Improvement of grammar and punctuation.
Clarify the HA limitations.
Remove future tense in some sentences.
It is not good to use it in technical/scientific papers.
Rewrite some sentences to improve understanding.
---
 pvesr.adoc | 112 ++---
 1 file changed, 56 insertions(+), 56 deletions(-)

Patch V3: changes like Aaron says in his review 

diff --git a/pvesr.adoc b/pvesr.adoc
index 72cea4a..a1a366c 100644
--- a/pvesr.adoc
+++ b/pvesr.adoc
@@ -31,34 +31,34 @@ local storage and reduces migration time.
 It replicates guest volumes to another node so that all data is available
 without using shared storage. Replication uses snapshots to minimize traffic
 sent over the network. Therefore, new data is sent only incrementally after
-an initial full sync. In the case of a node failure, your guest data is
+the initial full sync. In the case of a node failure, your guest data is
 still available on the replicated node.
 
-The replication will be done automatically in configurable intervals.
-The minimum replication interval is one minute and the maximal interval is
+The replication is done automatically in configurable intervals.
+The minimum replication interval is one minute, and the maximal interval
 once a week. The format used to specify those intervals is a subset of
 `systemd` calendar events, see
 xref:pvesr_schedule_time_format[Schedule Format] section:
 
-Every guest can be replicated to multiple target nodes, but a guest cannot
-get replicated twice to the same target node.
+It is possible to replicate a guest to multiple target nodes,
+but not twice to the same target node.
 
 Each replications bandwidth can be limited, to avoid overloading a storage
 or server.
 
-Virtual guest with active replication cannot currently use online migration.
-Offline migration is supported in general. If you migrate to a node where
-the guests data is already replicated only the changes since the last
-synchronisation (so called `delta`) must be sent, this reduces the required
-time significantly. In this case the replication direction will also switch
-nodes automatically after the migration finished.
+Guests with replication enabled can currently only be migrated offline.
+Only changes since the last replication (so-called `deltas`) need to be
+transferred if the guest is migrated to a node to which it already is
+replicated. This reduces the time needed significantly. The replication
+direction automatically switches if you migrate a guest to the replication
+target node.
 
 For example: VM100 is currently on `nodeA` and gets replicated to `nodeB`.
 You migrate it to `nodeB`, so now it gets automatically replicated back from
 `nodeB` to `nodeA`.
 
 If you migrate to a node where the guest is not replicated, the whole disk
-data must send over. After the migration the replication job continues to
+data must send over. After the migration, the replication job continues to
 replicate this guest to the configured nodes.
 
 [IMPORTANT]
@@ -99,24 +99,24 @@ Such a calendar event uses the following format:
 [day(s)] [[start-time(s)][/repetition-time(s)]]
 
 
-This allows you to configure a set of days on which the job should run.
-You can also set one or more start times, it tells the replication scheduler
+This format allows you to configure a set of days on which the job should run.
+You can also set one or more start times. It tells the replication scheduler
 the moments in time when a job should start.
-With this information we could create a job which runs every workday at 10
+With this information we, can create a job which runs every workday at 10
 PM: `'mon,tue,wed,thu,fri 22'` which could be abbreviated to: `'mon..fri
 22'`, most reasonable schedules can be written quite intuitive this way.
 
-NOTE: Hours are set in 24h format.
+NOTE: Hours are formatted in 24-hour format.
 
-To allow easier and shorter configuration one or more repetition times can
-be set. They indicate that on the start-time(s) itself and the start-time(s)
-plus all multiples of the repetition value replications will be done.  If
+To allow a convenient and shorter configuration, one or more repeat times per
+guest can be set. They indicate that replications are done on the start-time(s)
+itself and the start-time(s) plus all multiples of the repetition value. If
 you want to start replication at 8 AM and repeat it every 15 minutes until
 9 AM you would use: `'8:00/15'`
 
-Here you see also that if no hour separation (`:`) is used the value gets
-interpreted as minute. If such a separation is used the value on the left
-denotes the hour(s) and the value on the right denotes the minute(s).
+Here you see that if no hour separation (`:`), is used the value gets
+interpreted as minute. If such a separation is used, the value on the left
+denotes the hour(s), and the value on the right denotes the minute(s).
 Further, you can use `*` to match all possible values.
 
 To get additional ideas look at
@@ 

[pve-devel] [zsync] fix: check for incremental sync snapshot.

2020-03-18 Thread Wolfgang Link
For an incremental sync you need the last_snap on both sides.
---
 pve-zsync | 13 -
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/pve-zsync b/pve-zsync
index ea3178e..893baf0 100755
--- a/pve-zsync
+++ b/pve-zsync
@@ -931,6 +931,7 @@ sub snapshot_destroy {
 }
 }
 
+# check if snapshot for incremental sync exist on dest side
 sub snapshot_exist {
 my ($source , $dest, $method, $dest_user) = @_;
 
@@ -940,22 +941,16 @@ sub snapshot_exist {
 
 my $path = $dest->{all};
 $path .= "/$source->{last_part}" if $source->{last_part};
-$path .= "\@$source->{old_snap}";
+$path .= "\@$source->{last_snap}";
 
 push @$cmd, $path;
 
-
-my $text = "";
-eval {$text =run_cmd($cmd);};
+eval {run_cmd($cmd)};
 if (my $erro =$@) {
warn "WARN: $erro";
return undef;
 }
-
-while ($text && $text =~ s/^(.*?)(\n|$)//) {
-   my $line =$1;
-   return 1 if $line =~ m/^.*$source->{old_snap}$/;
-}
+return 1;
 }
 
 sub send_image {
-- 
2.20.1


___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


Re: [pve-devel] [zsync] fix: check for incremental sync snapshot.

2020-03-18 Thread Wolfgang Link
The call to the zfs list contains the snapshot.
If the snapshot does not exist, the command is returned with an error that we 
are catching.


> On March 18, 2020 8:02 AM Dietmar Maurer  wrote:
> 
>  
> Why does the patch ignore the output from the command?
> 
> > On March 18, 2020 7:51 AM Wolfgang Link  wrote:
> > 
> >  
> > For an incremental sync you need the last_snap on both sides.
> > ---
> >  pve-zsync | 13 -
> >  1 file changed, 4 insertions(+), 9 deletions(-)
> > 
> > diff --git a/pve-zsync b/pve-zsync
> > index ea3178e..893baf0 100755
> > --- a/pve-zsync
> > +++ b/pve-zsync
> > @@ -931,6 +931,7 @@ sub snapshot_destroy {
> >  }
> >  }
> >  
> > +# check if snapshot for incremental sync exist on dest side
> >  sub snapshot_exist {
> >  my ($source , $dest, $method, $dest_user) = @_;
> >  
> > @@ -940,22 +941,16 @@ sub snapshot_exist {
> >  
> >  my $path = $dest->{all};
> >  $path .= "/$source->{last_part}" if $source->{last_part};
> > -$path .= "\@$source->{old_snap}";
> > +$path .= "\@$source->{last_snap}";
> >  
> >  push @$cmd, $path;
> >  
> > -
> > -my $text = "";
> > -eval {$text =run_cmd($cmd);};
> > +eval {run_cmd($cmd)};
> >  if (my $erro =$@) {
> > warn "WARN: $erro";
> > return undef;
> >  }
> > -
> > -while ($text && $text =~ s/^(.*?)(\n|$)//) {
> > -   my $line =$1;
> > -   return 1 if $line =~ m/^.*$source->{old_snap}$/;
> > -}
> > +return 1;
> >  }
> >  
> >  sub send_image {
> > -- 
> > 2.20.1
> > 
> > 
> > ___
> > pve-devel mailing list
> > pve-devel@pve.proxmox.com
> > https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


Re: [pve-devel] [PATCH pve-zsync 0/1] Allow pve-zsync jobs to share dest

2020-06-16 Thread Wolfgang Link
Hi,

thank you for this patch and the work.
I will look at this patch and give you feedback.

Regards
Wolfgang

> On 06/16/2020 8:53 PM Bruce Wainer  wrote:
> 
>  
> By flipping Source and Dest in snapshot_get and snapshot_exist, we can allow
> multiple sync jobs to share the same source.
> snapshot_get now checks the destination instead of source, and sets last_sync 
> to
> the last snapshot regardless of name. old_sync and whether to delete it is 
> still
> based on the job/name.
> snapshot_exist now checks the source instead of the destination.
> Other functions and/or their calls are changed to match the new situation.
> 
> Bruce Wainer (1):
>   pve-zsync: Flip Source and Dest in functions to so jobs can share Dest
> 
>  pve-zsync | 42 +-
>  1 file changed, 25 insertions(+), 17 deletions(-)
> 
> -- 
> 2.20.1
> 
> ___
> pve-devel mailing list
> pve-devel@pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


Re: [pve-devel] [pve-zsync] Discussion/Feedback: Extending pve-zsync to handle intervening snapshots

2020-06-15 Thread Wolfgang Link
Hello,

I think that's a good idea.

However, I am not sure whether the additional flag described in point 1 is 
necessary,
since old jobs already have an independent dest dataset.
So the new logic should not conflict it.

I think it would be great to rewrite the "snapshot_get" 
and "snapshot_exists" functions to fit this use case,
In my opinion it makes little sense to copy the function 
and only change dest and source and give the function a new name.

Regards,
Wolfgang

> On 06/15/2020 9:50 PM Bruce Wainer  wrote:
> 
>  
> Hello,
> 
> I am considering making an improvement to pve-zsync and submitting it as a
> patch, but I would like some feedback before doing so.
> 
> Problem:
> pve-zsync can't be used multiple times between the same source and
> destination pool. For example, these two commands are allowed:
> pve-zsync create --source 192.168.10.18:101 --name 101-daily --dest
> 192.168.10.14:HDDs/replica-daily
> pve-zsync create --source 192.168.10.18:101 --name 101-weekly --dest
> 192.168.10.14:HDDs/replica-weekly
> But these two are not:
> pve-zsync create --source 192.168.10.18:101 --name 101-daily --dest
> 192.168.10.14:HDDs/replica
> pve-zsync create --source 192.168.10.18:101 --name 101-weekly --dest
> 192.168.10.14:HDDs/replica
> 
> The first set of commands work together because the destination volumes are
> different. However, this means that the destination host will have
> duplicate copies of the source. This is not ideal. The second set of
> commands will not work because the last_snap used for the zfs send/recv
> only takes into account the current job.
> 
> Proposal
> Flip the source and destination in snapshot_get() and snapshot_exists(),
> because if the last snapshot on the destination doesn't exist on the
> source, the sync is going to fail anyway (ZFS will error with "cannot
> receive new filesystem stream: destination has snapshots").
> 1. Add a new command option "use_last_dest_snapshot" (add to help, to cron
> generation, etc). If this is set, the new logic will be used.
> 2. Add "sub snapshot_get_dest" which is similar to snapshot_get() except it
> performs its checks on the destination, not the source. Also, last_snap
> would be the last snapshot of all, not just the last one related to the
> current job.
> 3. Add "sub snapshot_exists_source" which is the same as snapshot_exists()
> except it performs its checks on the source, not the destination. I would
> also improve the error message if the snapshot doesn't exist on the source.
> 4. In "sub send_image", if " use_last_dest_snapshot " is set then the new
> functions would be called, otherwise the old functions would be called.
> 
> This will be my first time submitting changes to a project, but I believe I
> am capable of writing code that meets your code standards. I am aware of
> the style guide and will submit the CLA.
> 
> Please let me know what you think about my proposed changes.
> 
> Thank you,
> Bruce Wainer
> ___
> pve-devel mailing list
> pve-devel@pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


Re: [pve-devel] [PATCH pve-zsync 0/1] Allow pve-zsync jobs to share dest

2020-06-23 Thread Wolfgang Link
Hi Bruce,

Once you've submitted the CLA, a member of the release team will take care of 
this patch.
You will be notified when it is committed or when something is missing.

Regards

Wolfgang

> On 06/22/2020 2:38 PM Bruce Wainer  wrote:
> 
>  
> Wolfgang,
> Thanks for the confirmation. What is the next step?
> Thank you,
> Bruce
> 
> > On Jun 22, 2020, at 7:43 AM, Wolfgang Link  wrote:
> > 
> > Look good to me
> > I tested it and it works. There are no upgrade problems. 
> > Even if jobs already exist.
> > 
> > Regards
> > 
> > Wolfgang
> > 
> >> On 06/17/2020 6:44 AM Wolfgang Link  wrote:
> >> 
> >> 
> >> Hi,
> >> 
> >> thank you for this patch and the work.
> >> I will look at this patch and give you feedback.
> >> 
> >> Regards
> >> Wolfgang
> >> 
> >>>> On 06/16/2020 8:53 PM Bruce Wainer  wrote:
> >>> 
> >>> 
> >>> By flipping Source and Dest in snapshot_get and snapshot_exist, we can 
> >>> allow
> >>> multiple sync jobs to share the same source.
> >>> snapshot_get now checks the destination instead of source, and sets 
> >>> last_sync to
> >>> the last snapshot regardless of name. old_sync and whether to delete it 
> >>> is still
> >>> based on the job/name.
> >>> snapshot_exist now checks the source instead of the destination.
> >>> Other functions and/or their calls are changed to match the new situation.
> >>> 
> >>> Bruce Wainer (1):
> >>>  pve-zsync: Flip Source and Dest in functions to so jobs can share Dest
> >>> 
> >>> pve-zsync | 42 +-
> >>> 1 file changed, 25 insertions(+), 17 deletions(-)
> >>> 
> >>> -- 
> >>> 2.20.1
> >>> 
> >>> ___
> >>> pve-devel mailing list
> >>> pve-devel@pve.proxmox.com
> >>> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> >> 
> >> ___
> >> pve-devel mailing list
> >> pve-devel@pve.proxmox.com
> >> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> > 
> > Best Regards,
> > Wolfgang Link
> > w.l...@proxmox.com
> > http://www.proxmox.com
> > 
> > Proxmox Server Solutions GmbH
> > Bräuhausgasse 37, 1050 Vienna, Austria
> > Commercial register no.: FN 258879 f
> > Registration office: Handelsgericht Wien
> >

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


Re: [pve-devel] [PATCH pve-zsync 0/1] Allow pve-zsync jobs to share dest

2020-06-22 Thread Wolfgang Link
Look good to me
I tested it and it works. There are no upgrade problems. 
Even if jobs already exist.

Regards

Wolfgang

> On 06/17/2020 6:44 AM Wolfgang Link  wrote:
> 
>  
> Hi,
> 
> thank you for this patch and the work.
> I will look at this patch and give you feedback.
> 
> Regards
> Wolfgang
> 
> > On 06/16/2020 8:53 PM Bruce Wainer  wrote:
> > 
> >  
> > By flipping Source and Dest in snapshot_get and snapshot_exist, we can allow
> > multiple sync jobs to share the same source.
> > snapshot_get now checks the destination instead of source, and sets 
> > last_sync to
> > the last snapshot regardless of name. old_sync and whether to delete it is 
> > still
> > based on the job/name.
> > snapshot_exist now checks the source instead of the destination.
> > Other functions and/or their calls are changed to match the new situation.
> > 
> > Bruce Wainer (1):
> >   pve-zsync: Flip Source and Dest in functions to so jobs can share Dest
> > 
> >  pve-zsync | 42 +-
> >  1 file changed, 25 insertions(+), 17 deletions(-)
> > 
> > -- 
> > 2.20.1
> > 
> > ___
> > pve-devel mailing list
> > pve-devel@pve.proxmox.com
> > https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> ___
> pve-devel mailing list
> pve-devel@pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Best Regards,
Wolfgang Link
w.l...@proxmox.com
http://www.proxmox.com

Proxmox Server Solutions GmbH
Bräuhausgasse 37, 1050 Vienna, Austria
Commercial register no.: FN 258879 f
Registration office: Handelsgericht Wien

___
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


<    5   6   7   8   9   10