Merge branch 'upstream'
This commit is contained in:
commit
bf5c737c8f
12
Cargo.toml
12
Cargo.toml
@ -1,12 +0,0 @@
|
|||||||
[workspace]
|
|
||||||
exclude = [ "build", "common-src", "perl", "scripts" ]
|
|
||||||
members = [
|
|
||||||
"pve-rs",
|
|
||||||
"pmg-rs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
# proxmox-tfa requires -time and -uuid as well, so enable *all* or *none* of them
|
|
||||||
#proxmox-tfa = { path = "../proxmox/proxmox-tfa" }
|
|
||||||
#proxmox-time = { path = "../proxmox/proxmox-time" }
|
|
||||||
#proxmox-uuid = { path = "../proxmox/proxmox-uuid" }
|
|
101
Makefile
101
Makefile
@ -1,33 +1,11 @@
|
|||||||
CARGO ?= cargo
|
CARGO ?= cargo
|
||||||
|
|
||||||
define to_upper
|
|
||||||
$(shell echo "$(1)" | tr '[:lower:]' '[:upper:]')
|
|
||||||
endef
|
|
||||||
|
|
||||||
ifeq ($(BUILD_MODE), release)
|
ifeq ($(BUILD_MODE), release)
|
||||||
CARGO_BUILD_ARGS += --release --offline
|
CARGO_BUILD_ARGS += --release --offline
|
||||||
DEBUG_LIBPATH :=
|
DEBUG_LIBPATH :=
|
||||||
else
|
else
|
||||||
DEBUG_LIBPATH := "-L./target/debug",
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
define package_template
|
|
||||||
sed -r \
|
|
||||||
-e 's/\{\{PRODUCT\}\}/$(1)/g;' \
|
|
||||||
-e 's/\{\{LIBRARY\}\}/$(2)/g;' \
|
|
||||||
-e 's|\{\{DEBUG_LIBPATH\}\}|$(DEBUG_LIBPATH)|g;' \
|
|
||||||
Proxmox/Lib/template.pm \
|
|
||||||
>Proxmox/Lib/$(1).pm
|
|
||||||
endef
|
|
||||||
|
|
||||||
define upload_template
|
|
||||||
cd build; \
|
|
||||||
dcmd --deb lib$(1)-rs-perl*.changes \
|
|
||||||
| grep -v '.changes$$' \
|
|
||||||
| tar -cf "$@.tar" -T-; \
|
|
||||||
cat "$@.tar" | ssh -X repoman@repo.proxmox.com upload --product $(2) --dist bullseye
|
|
||||||
endef
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all:
|
all:
|
||||||
ifeq ($(BUILD_TARGET), pve)
|
ifeq ($(BUILD_TARGET), pve)
|
||||||
@ -40,74 +18,29 @@ else
|
|||||||
@echo " - make pmg"
|
@echo " - make pmg"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: pve pmg
|
|
||||||
pve pmg:
|
|
||||||
@PERLMOD_PRODUCT=$(call to_upper,$@) \
|
|
||||||
RUSTFLAGS="-L/usr/lib64/perl5/CORE -lperl" $(CARGO) build $(CARGO_BUILD_ARGS) -p $@-rs
|
|
||||||
|
|
||||||
.PHONY: gen
|
|
||||||
gen:
|
|
||||||
$(call package_template,PMG,pmg_rs)
|
|
||||||
$(call package_template,PVE,pve_rs)
|
|
||||||
perl ./scripts/genpackage.pl Common \
|
|
||||||
Proxmox::RS::APT::Repositories \
|
|
||||||
Proxmox::RS::CalendarEvent \
|
|
||||||
Proxmox::RS::Subscription
|
|
||||||
perl ./scripts/genpackage.pl PVE \
|
|
||||||
PVE::RS::APT::Repositories \
|
|
||||||
PVE::RS::OpenId \
|
|
||||||
PVE::RS::ResourceScheduling::Static \
|
|
||||||
PVE::RS::TFA
|
|
||||||
perl ./scripts/genpackage.pl PMG \
|
|
||||||
PMG::RS::APT::Repositories \
|
|
||||||
PMG::RS::Acme \
|
|
||||||
PMG::RS::CSR \
|
|
||||||
PMG::RS::OpenId \
|
|
||||||
PMG::RS::TFA
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
mkdir build
|
mkdir build
|
||||||
echo system >build/rust-toolchain
|
echo system >build/rust-toolchain
|
||||||
cp -a ./scripts ./build
|
cp -a ./Cargo.toml ./build
|
||||||
cp -a ./common ./build
|
cp -a ./common ./build
|
||||||
cp -a ./pve-rs ./build
|
cp -a ./pve-rs ./build
|
||||||
cp -a ./pmg-rs ./build
|
cp -a ./pmg-rs ./build
|
||||||
cp -a ./Proxmox ./build
|
# Replace the symlinks with copies of the common code in pve/pmg:
|
||||||
$(MAKE) BUILD_MODE=release -C build -f ../Makefile gen
|
cd build; for i in pve pmg; do \
|
||||||
mkdir -p ./build/pve-rs/Proxmox/Lib
|
rm ./$$i-rs/common ; \
|
||||||
mv ./build/Proxmox/Lib/PVE.pm ./build/pve-rs/Proxmox/Lib/PVE.pm
|
mkdir ./$$i-rs/common ; \
|
||||||
mkdir -p ./build/pmg-rs/Proxmox/Lib
|
cp -R ./common/src ./$$i-rs/common/src ; \
|
||||||
mv ./build/Proxmox/Lib/PMG.pm ./build/pmg-rs/Proxmox/Lib/PMG.pm
|
done
|
||||||
mv ./build/PVE ./build/pve-rs
|
|
||||||
mv ./build/PMG ./build/pmg-rs
|
|
||||||
mv ./build/Proxmox ./build/common/pkg
|
|
||||||
# So the common packages end up in ./build, rather than ./build/common
|
# So the common packages end up in ./build, rather than ./build/common
|
||||||
mv ./build/common/pkg ./build/common-pkg
|
mv ./build/common/pkg ./build/common-pkg
|
||||||
|
# Copy the workspace root into the sources
|
||||||
pve-deb: build
|
mkdir build/pve-rs/.workspace
|
||||||
cd ./build/pve-rs && dpkg-buildpackage -b -uc -us
|
cp -t build/pve-rs/.workspace Cargo.toml
|
||||||
touch $@
|
sed -i -e '/\[package\]/a\workspace = ".workspace"' build/pve-rs/Cargo.toml
|
||||||
|
# Clear the member array and replace it with ".."
|
||||||
pmg-deb: build
|
sed -i -e '/^members = \[/,/^]$$/d' build/pve-rs/.workspace/Cargo.toml
|
||||||
cd ./build/pmg-rs && dpkg-buildpackage -b -uc -us
|
sed -i -e '/^\[workspace\]/a\members = [ ".." ]' build/pve-rs/.workspace/Cargo.toml
|
||||||
touch $@
|
# Copy the cargo config
|
||||||
|
mkdir build/pve-rs/.cargo
|
||||||
common-deb: build
|
cp -t build/pve-rs/.cargo .cargo/config
|
||||||
cd ./build/common-pkg && dpkg-buildpackage -b -uc -us
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
pve-upload: pve-deb
|
|
||||||
$(call upload_template,pve,pve)
|
|
||||||
pmg-upload: pmg-deb
|
|
||||||
$(call upload_template,pmg,pmg)
|
|
||||||
|
|
||||||
# need to put into variable to ensure comma isn't interpreted as param separator on call
|
|
||||||
common_target=pve,pmg
|
|
||||||
common-upload: common-deb
|
|
||||||
$(call upload_template,proxmox,$(common_target))
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
cargo clean
|
|
||||||
rm -rf ./build ./PVE ./PMG ./pve-deb ./pmg-deb ./common-deb
|
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
package Proxmox::Lib::{{PRODUCT}};
|
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
Proxmox::Lib::{{PRODUCT}} - base module for {{PRODUCT}} rust bindings
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
package {{PRODUCT}}::RS::SomeBindings;
|
|
||||||
|
|
||||||
use base 'Proxmox::Lib::{{PRODUCT}}';
|
|
||||||
|
|
||||||
BEGIN { __PACKAGE__->bootstrap(); }
|
|
||||||
|
|
||||||
1;
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
|
||||||
|
|
||||||
This is the base module of all {{PRODUCT}} bindings.
|
|
||||||
Its job is to ensure the 'lib{{LIBRARY}}.so' library is loaded and provide a 'bootstrap' class
|
|
||||||
method to load the actual code.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
use DynaLoader;
|
|
||||||
|
|
||||||
sub library {
|
|
||||||
return '{{LIBRARY}}';
|
|
||||||
}
|
|
||||||
|
|
||||||
sub load : prototype($) {
|
|
||||||
my ($pkg) = @_;
|
|
||||||
|
|
||||||
my $mod_name = $pkg->library();
|
|
||||||
|
|
||||||
my @dirs = (map "-L$_/auto", @INC);
|
|
||||||
my $mod_file = DynaLoader::dl_findfile({{DEBUG_LIBPATH}}@dirs, $mod_name);
|
|
||||||
die "failed to locate shared library for $mod_name (lib${mod_name}.so)\n" if !$mod_file;
|
|
||||||
|
|
||||||
my $lib = DynaLoader::dl_load_file($mod_file)
|
|
||||||
or die "failed to load library '$mod_file'\n";
|
|
||||||
|
|
||||||
my $data = ($::{'proxmox-rs-library'} //= {});
|
|
||||||
$data->{$mod_name} = $lib;
|
|
||||||
$data->{-current} //= $lib;
|
|
||||||
$data->{-package} //= $pkg;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub bootstrap {
|
|
||||||
my ($pkg) = @_;
|
|
||||||
|
|
||||||
my $mod_name = $pkg->library();
|
|
||||||
|
|
||||||
my $bootstrap_name = 'boot_' . ($pkg =~ s/::/__/gr);
|
|
||||||
|
|
||||||
my $lib = $::{'proxmox-rs-library'}
|
|
||||||
or die "rust library not available for '{PRODUCT}'\n";
|
|
||||||
$lib = $lib->{$mod_name};
|
|
||||||
|
|
||||||
my $sym = DynaLoader::dl_find_symbol($lib, $bootstrap_name);
|
|
||||||
die "failed to locate '$bootstrap_name'\n" if !defined $sym;
|
|
||||||
my $boot = DynaLoader::dl_install_xsub($bootstrap_name, $sym, "src/FIXME.rs");
|
|
||||||
$boot->();
|
|
||||||
}
|
|
||||||
|
|
||||||
BEGIN { __PACKAGE__->load(); }
|
|
||||||
|
|
||||||
1;
|
|
@ -1,4 +1,4 @@
|
|||||||
include /usr/share/dpkg/default.mk
|
include /usr/share/dpkg/pkg-info.mk
|
||||||
|
|
||||||
PACKAGE=libproxmox-rs-perl
|
PACKAGE=libproxmox-rs-perl
|
||||||
|
|
||||||
@ -8,22 +8,63 @@ export GITVERSION:=$(shell git rev-parse HEAD)
|
|||||||
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
|
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
|
||||||
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
|
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
|
||||||
|
|
||||||
DEB=${PACKAGE}_${DEB_VERSION}_${ARCH}.deb
|
DEB=$(PACKAGE)_$(DEB_VERSION)_$(ARCH).deb
|
||||||
|
DSC=$(PACKAGE)_$(DEB_VERSION_UPSTREAM_REVISION).dsc
|
||||||
|
BUILDDIR ?= $(PACKAGE)-$(DEB_VERSION_UPSTREAM)
|
||||||
|
|
||||||
DESTDIR=
|
DESTDIR=
|
||||||
|
|
||||||
all:
|
PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \
|
||||||
|
--lib=- \
|
||||||
|
--lib-tag=proxmox \
|
||||||
|
--lib-package=Proxmox::Lib::Common \
|
||||||
|
--lib-prefix=Proxmox
|
||||||
|
|
||||||
|
# Point to any generated pm file (Proxmox/ dir is already present in this package)
|
||||||
|
Proxmox/RS/CalendarEvent.pm:
|
||||||
|
$(PERLMOD_GENPACKAGE) \
|
||||||
|
Proxmox::RS::APT::Repositories \
|
||||||
|
Proxmox::RS::CalendarEvent \
|
||||||
|
Proxmox::RS::Notify \
|
||||||
|
Proxmox::RS::Subscription
|
||||||
|
|
||||||
|
all: Proxmox/RS/CalendarEvent.pm
|
||||||
true
|
true
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install:
|
install: Proxmox/RS/CalendarEvent.pm
|
||||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
||||||
find PVE \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
find PVE \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
||||||
find Proxmox \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
find Proxmox \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
||||||
rm $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/Proxmox/Lib/template.pm'
|
|
||||||
|
$(BUILDDIR): debian PVE Proxmox Makefile
|
||||||
|
rm -rf $(BUILDDIR) $(BUILDDIR).tmp
|
||||||
|
mkdir $(BUILDDIR).tmp
|
||||||
|
cp -t $(BUILDDIR).tmp -a debian PVE Proxmox Makefile
|
||||||
|
mv $(BUILDDIR).tmp $(BUILDDIR)
|
||||||
|
|
||||||
.PHONY: deb
|
.PHONY: deb
|
||||||
deb: $(DEB)
|
deb: $(DEB)
|
||||||
$(DEB): build
|
$(DEB): $(BUILDDIR)
|
||||||
cd build; dpkg-buildpackage -b -us -uc --no-pre-clean
|
cd $(BUILDDIR); dpkg-buildpackage -b -us -uc --no-pre-clean
|
||||||
lintian $(DEBS)
|
lintian $(DEB)
|
||||||
|
|
||||||
|
.PHONY: dsc
|
||||||
|
dsc: $(DSC)
|
||||||
|
$(DSC): $(BUILDDIR)
|
||||||
|
cd $(BUILDDIR); dpkg-buildpackage -S -us -uc -d
|
||||||
|
lintian $(DSC)
|
||||||
|
|
||||||
|
sbuild: $(DSC)
|
||||||
|
sbuild $(DSC)
|
||||||
|
|
||||||
|
.PHONY: upload
|
||||||
|
upload: UPLOAD_DIST ?= $(DEB_DISTRIBUTION)
|
||||||
|
upload: $(DEB)
|
||||||
|
# check if working directory is clean
|
||||||
|
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
||||||
|
tar cf - $(DEB) | ssh -X repoman@repo.proxmox.com upload --product pve,pmg --dist $(DEB_DISTRIBUTION)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.deb *.dsc *.tar.* *.build *.buildinfo *.changes
|
||||||
|
rm -rf $(PACKAGE)-[0-9]*/
|
||||||
|
100
common/pkg/Proxmox/Lib/SslProbe.pm
Normal file
100
common/pkg/Proxmox/Lib/SslProbe.pm
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package Proxmox::Lib::SslProbe;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
=head1 Environment Variable Safety
|
||||||
|
|
||||||
|
Perl's handling of environment variables was completely messed up until v5.38.
|
||||||
|
Using `setenv` such as use din the `openssl-probe` crate would cause it to
|
||||||
|
crash later on, therefore we provide a perl-version of env var probing instead,
|
||||||
|
and override the crate with one that doesn't replace the variables if they are
|
||||||
|
already set correctly.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
# Copied from openssl-probe
|
||||||
|
my @cert_dirs = (
|
||||||
|
"/var/ssl",
|
||||||
|
"/usr/share/ssl",
|
||||||
|
"/usr/local/ssl",
|
||||||
|
"/usr/local/openssl",
|
||||||
|
"/usr/local/etc/openssl",
|
||||||
|
"/usr/local/share",
|
||||||
|
"/usr/lib/ssl",
|
||||||
|
"/usr/ssl",
|
||||||
|
"/etc/openssl",
|
||||||
|
"/etc/pki/ca-trust/extracted/pem",
|
||||||
|
"/etc/pki/tls",
|
||||||
|
"/etc/ssl",
|
||||||
|
"/etc/certs",
|
||||||
|
"/opt/etc/ssl",
|
||||||
|
"/data/data/com.termux/files/usr/etc/tls",
|
||||||
|
"/boot/system/data/ssl",
|
||||||
|
);
|
||||||
|
|
||||||
|
# Copied from openssl-probe
|
||||||
|
my @cert_file_names = (
|
||||||
|
"cert.pem",
|
||||||
|
"certs.pem",
|
||||||
|
"ca-bundle.pem",
|
||||||
|
"cacert.pem",
|
||||||
|
"ca-certificates.crt",
|
||||||
|
"certs/ca-certificates.crt",
|
||||||
|
"certs/ca-root-nss.crt",
|
||||||
|
"certs/ca-bundle.crt",
|
||||||
|
"CARootCertificates.pem",
|
||||||
|
"tls-ca-bundle.pem",
|
||||||
|
);
|
||||||
|
|
||||||
|
my $probed_ssl_vars = 0;
|
||||||
|
|
||||||
|
# The algorithm here is taken from the `openssl-probe` crate and should
|
||||||
|
# produce the exact same result in order to ensure the rust code does not
|
||||||
|
# call `setenv()`.
|
||||||
|
my sub probe_ssl_vars : prototype() {
|
||||||
|
return if $probed_ssl_vars;
|
||||||
|
$probed_ssl_vars = 1;
|
||||||
|
|
||||||
|
my $result_file = $ENV{SSL_CERT_FILE};
|
||||||
|
my $result_file_changed = 0;
|
||||||
|
my $result_dir = $ENV{SSL_CERT_DIR};
|
||||||
|
my $result_dir_changed = 0;
|
||||||
|
|
||||||
|
for my $certs_dir (@cert_dirs) {
|
||||||
|
if (!defined($result_file)) {
|
||||||
|
for my $file (@cert_file_names) {
|
||||||
|
my $path = "$certs_dir/$file";
|
||||||
|
if (-e $path) {
|
||||||
|
$result_file = $path;
|
||||||
|
$result_file_changed = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!defined($result_dir)) {
|
||||||
|
for my $file (@cert_file_names) {
|
||||||
|
my $path = "$certs_dir/certs";
|
||||||
|
if (-d $path) {
|
||||||
|
$result_dir = $path;
|
||||||
|
$result_dir_changed = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last if defined($result_file) && defined($result_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result_file_changed && defined($result_file)) {
|
||||||
|
$ENV{SSL_CERT_FILE} = $result_file;
|
||||||
|
}
|
||||||
|
if ($result_dir_changed && defined($result_dir)) {
|
||||||
|
$ENV{SSL_CERT_DIR} = $result_dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
probe_ssl_vars();
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -1,3 +1,27 @@
|
|||||||
|
libproxmox-rs-perl (0.3.3) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* move ssl var probing to Proxmox::Lib::SslProbe
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Thu, 07 Dec 2023 09:57:33 +0100
|
||||||
|
|
||||||
|
libproxmox-rs-perl (0.3.2) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* add Proxmox::Lib::Common::probe_ssl_vars() helper
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Tue, 05 Dec 2023 10:46:39 +0100
|
||||||
|
|
||||||
|
libproxmox-rs-perl (0.3.1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* add Proxmox::RS::Notify module
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Mon, 24 Jul 2023 14:02:17 +0200
|
||||||
|
|
||||||
|
libproxmox-rs-perl (0.3.0) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* rebuild for Debian 12 Bookworm based release series
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 17 May 2023 15:48:41 +0200
|
||||||
|
|
||||||
libproxmox-rs-perl (0.2.1) bullseye; urgency=medium
|
libproxmox-rs-perl (0.2.1) bullseye; urgency=medium
|
||||||
|
|
||||||
* update to proxmox-subscription 0.3 / proxmox-http 0.7
|
* update to proxmox-subscription 0.3 / proxmox-http 0.7
|
||||||
|
@ -1 +0,0 @@
|
|||||||
12
|
|
@ -1,10 +1,9 @@
|
|||||||
Source: libproxmox-rs-perl
|
Source: libproxmox-rs-perl
|
||||||
Section: perl
|
Section: perl
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends:
|
Build-Depends: debhelper-compat (= 13), perlmod-bin,
|
||||||
debhelper (>= 12),
|
|
||||||
Maintainer: Proxmox Support Team <support@proxmox.com>
|
Maintainer: Proxmox Support Team <support@proxmox.com>
|
||||||
Standards-Version: 4.5.1
|
Standards-Version: 4.6.2
|
||||||
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
||||||
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
||||||
Homepage: https://www.proxmox.com
|
Homepage: https://www.proxmox.com
|
||||||
@ -14,15 +13,12 @@ Package: libproxmox-rs-perl
|
|||||||
Architecture: any
|
Architecture: any
|
||||||
# always bump both versioned Depends and Breaks, otherwise systems with both
|
# always bump both versioned Depends and Breaks, otherwise systems with both
|
||||||
# libpmg-rs-perl and libpve-rs-perl might load an outdated lib and break
|
# libpmg-rs-perl and libpve-rs-perl might load an outdated lib and break
|
||||||
Depends:
|
Depends: libpve-rs-perl (>= 0.8.5) | libpmg-rs-perl (>= 0.6.2),
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
${perl:Depends},
|
${perl:Depends},
|
||||||
${shlibs:Depends},
|
${shlibs:Depends},
|
||||||
libpve-rs-perl (>= 0.7.2) | libpmg-rs-perl (>= 0.6.2),
|
Breaks: libpmg-rs-perl (<< 0.6.2), libpve-rs-perl (<< 0.7.2),
|
||||||
Breaks:
|
Replaces: libpve-rs-perl (<< 0.6.0),
|
||||||
libpve-rs-perl (<< 0.7.2),
|
Description: PVE/PMG common perl parts for Rust perlmod bindings
|
||||||
libpmg-rs-perl (<< 0.6.2),
|
Contains the perl side of modules provided by the libraries of both
|
||||||
Replaces: libpve-rs-perl (<< 0.6.0)
|
libpve-rs-perl and libpmg-rs-perl, loading whichever is available.
|
||||||
Description: PVE/PMG common parts which have been ported to Rust - Perl packages
|
|
||||||
Contains the perl side of modules provided by the libraries of both libpve-rs-perl and
|
|
||||||
libpmg-rs-perl, loading whichever is available.
|
|
||||||
|
14
common/src/logger.rs
Normal file
14
common/src/logger.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use env_logger::{Builder, Env};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
/// Initialize logging. Should only be called once
|
||||||
|
pub fn init(env_var_name: &str, default_log_level: &str) {
|
||||||
|
if let Err(e) = Builder::from_env(Env::new().filter_or(env_var_name, default_log_level))
|
||||||
|
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
|
||||||
|
.write_style(env_logger::WriteStyle::Never)
|
||||||
|
.format_timestamp(None)
|
||||||
|
.try_init()
|
||||||
|
{
|
||||||
|
eprintln!("could not set up env_logger: {e}");
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
pub mod apt;
|
pub mod apt;
|
||||||
mod calendar_event;
|
mod calendar_event;
|
||||||
|
pub mod logger;
|
||||||
|
pub mod notify;
|
||||||
mod subscription;
|
mod subscription;
|
||||||
|
508
common/src/notify.rs
Normal file
508
common/src/notify.rs
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
#[perlmod::package(name = "Proxmox::RS::Notify")]
|
||||||
|
mod export {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use anyhow::{bail, Error};
|
||||||
|
use serde_json::Value as JSONValue;
|
||||||
|
|
||||||
|
use perlmod::Value;
|
||||||
|
use proxmox_http_error::HttpError;
|
||||||
|
use proxmox_notify::endpoints::gotify::{
|
||||||
|
DeleteableGotifyProperty, GotifyConfig, GotifyConfigUpdater, GotifyPrivateConfig,
|
||||||
|
GotifyPrivateConfigUpdater,
|
||||||
|
};
|
||||||
|
use proxmox_notify::endpoints::sendmail::{
|
||||||
|
DeleteableSendmailProperty, SendmailConfig, SendmailConfigUpdater,
|
||||||
|
};
|
||||||
|
use proxmox_notify::endpoints::smtp::{
|
||||||
|
DeleteableSmtpProperty, SmtpConfig, SmtpConfigUpdater, SmtpMode, SmtpPrivateConfig,
|
||||||
|
SmtpPrivateConfigUpdater,
|
||||||
|
};
|
||||||
|
use proxmox_notify::matcher::{
|
||||||
|
CalendarMatcher, DeleteableMatcherProperty, FieldMatcher, MatchModeOperator, MatcherConfig,
|
||||||
|
MatcherConfigUpdater, SeverityMatcher,
|
||||||
|
};
|
||||||
|
use proxmox_notify::{api, Config, Notification, Severity};
|
||||||
|
|
||||||
|
pub struct NotificationConfig {
|
||||||
|
config: Mutex<Config>,
|
||||||
|
}
|
||||||
|
|
||||||
|
perlmod::declare_magic!(Box<NotificationConfig> : &NotificationConfig as "Proxmox::RS::Notify");
|
||||||
|
|
||||||
|
/// Support `dclone` so this can be put into the `ccache` of `PVE::Cluster`.
|
||||||
|
#[export(name = "STORABLE_freeze", raw_return)]
|
||||||
|
fn storable_freeze(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
cloning: bool,
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
if !cloning {
|
||||||
|
bail!("freezing Notification config not supported!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cloned = Box::new(NotificationConfig {
|
||||||
|
config: Mutex::new(this.config.lock().unwrap().clone()),
|
||||||
|
});
|
||||||
|
let value = Value::new_pointer::<NotificationConfig>(&mut *cloned);
|
||||||
|
let _perl = Box::leak(cloned);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instead of `thaw` we implement `attach` for `dclone`.
|
||||||
|
#[export(name = "STORABLE_attach", raw_return)]
|
||||||
|
fn storable_attach(
|
||||||
|
#[raw] class: Value,
|
||||||
|
cloning: bool,
|
||||||
|
#[raw] serialized: Value,
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
if !cloning {
|
||||||
|
bail!("STORABLE_attach called with cloning=false");
|
||||||
|
}
|
||||||
|
let data = unsafe { Box::from_raw(serialized.pv_raw::<NotificationConfig>()?) };
|
||||||
|
Ok(perlmod::instantiate_magic!(&class, MAGIC => data))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(raw_return)]
|
||||||
|
fn parse_config(
|
||||||
|
#[raw] class: Value,
|
||||||
|
raw_config: &[u8],
|
||||||
|
raw_private_config: &[u8],
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
let raw_config = std::str::from_utf8(raw_config)?;
|
||||||
|
let raw_private_config = std::str::from_utf8(raw_private_config)?;
|
||||||
|
|
||||||
|
Ok(perlmod::instantiate_magic!(&class, MAGIC => Box::new(
|
||||||
|
NotificationConfig {
|
||||||
|
config: Mutex::new(Config::new(raw_config, raw_private_config)?)
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn write_config(#[try_from_ref] this: &NotificationConfig) -> Result<(String, String), Error> {
|
||||||
|
Ok(this.config.lock().unwrap().write()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn digest(#[try_from_ref] this: &NotificationConfig) -> String {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
hex::encode(config.digest())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn send(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
severity: Severity,
|
||||||
|
title: String,
|
||||||
|
body: String,
|
||||||
|
template_data: Option<JSONValue>,
|
||||||
|
fields: Option<HashMap<String, String>>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
let notification = Notification::new_templated(
|
||||||
|
severity,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
template_data.unwrap_or_default(),
|
||||||
|
fields.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
api::common::send(&config, ¬ification)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn test_target(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
target: &str,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::common::test_target(&config, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_sendmail_endpoints(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
) -> Result<Vec<SendmailConfig>, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::sendmail::get_endpoints(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_sendmail_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
id: &str,
|
||||||
|
) -> Result<SendmailConfig, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::sendmail::get_endpoint(&config, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn add_sendmail_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: String,
|
||||||
|
mailto: Option<Vec<String>>,
|
||||||
|
mailto_user: Option<Vec<String>>,
|
||||||
|
from_address: Option<String>,
|
||||||
|
author: Option<String>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
|
||||||
|
api::sendmail::add_endpoint(
|
||||||
|
&mut config,
|
||||||
|
&SendmailConfig {
|
||||||
|
name,
|
||||||
|
mailto,
|
||||||
|
mailto_user,
|
||||||
|
from_address,
|
||||||
|
author,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
filter: None,
|
||||||
|
origin: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn update_sendmail_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
mailto: Option<Vec<String>>,
|
||||||
|
mailto_user: Option<Vec<String>>,
|
||||||
|
from_address: Option<String>,
|
||||||
|
author: Option<String>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
delete: Option<Vec<DeleteableSendmailProperty>>,
|
||||||
|
digest: Option<&str>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
let digest = decode_digest(digest)?;
|
||||||
|
|
||||||
|
api::sendmail::update_endpoint(
|
||||||
|
&mut config,
|
||||||
|
name,
|
||||||
|
&SendmailConfigUpdater {
|
||||||
|
mailto,
|
||||||
|
mailto_user,
|
||||||
|
from_address,
|
||||||
|
author,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
},
|
||||||
|
delete.as_deref(),
|
||||||
|
digest.as_deref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn delete_sendmail_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::sendmail::delete_endpoint(&mut config, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_gotify_endpoints(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
) -> Result<Vec<GotifyConfig>, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::gotify::get_endpoints(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_gotify_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
id: &str,
|
||||||
|
) -> Result<GotifyConfig, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::gotify::get_endpoint(&config, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn add_gotify_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: String,
|
||||||
|
server: String,
|
||||||
|
token: String,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::gotify::add_endpoint(
|
||||||
|
&mut config,
|
||||||
|
&GotifyConfig {
|
||||||
|
name: name.clone(),
|
||||||
|
server,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
filter: None,
|
||||||
|
origin: None,
|
||||||
|
},
|
||||||
|
&GotifyPrivateConfig { name, token },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn update_gotify_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
server: Option<String>,
|
||||||
|
token: Option<String>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
delete: Option<Vec<DeleteableGotifyProperty>>,
|
||||||
|
digest: Option<&str>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
let digest = decode_digest(digest)?;
|
||||||
|
|
||||||
|
api::gotify::update_endpoint(
|
||||||
|
&mut config,
|
||||||
|
name,
|
||||||
|
&GotifyConfigUpdater {
|
||||||
|
server,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
},
|
||||||
|
&GotifyPrivateConfigUpdater { token },
|
||||||
|
delete.as_deref(),
|
||||||
|
digest.as_deref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn delete_gotify_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::gotify::delete_gotify_endpoint(&mut config, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_smtp_endpoints(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
) -> Result<Vec<SmtpConfig>, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::smtp::get_endpoints(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_smtp_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
id: &str,
|
||||||
|
) -> Result<SmtpConfig, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::smtp::get_endpoint(&config, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn add_smtp_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: String,
|
||||||
|
server: String,
|
||||||
|
port: Option<u16>,
|
||||||
|
mode: Option<SmtpMode>,
|
||||||
|
username: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
mailto: Option<Vec<String>>,
|
||||||
|
mailto_user: Option<Vec<String>>,
|
||||||
|
from_address: String,
|
||||||
|
author: Option<String>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::smtp::add_endpoint(
|
||||||
|
&mut config,
|
||||||
|
&SmtpConfig {
|
||||||
|
name: name.clone(),
|
||||||
|
server,
|
||||||
|
port,
|
||||||
|
mode,
|
||||||
|
username,
|
||||||
|
mailto,
|
||||||
|
mailto_user,
|
||||||
|
from_address,
|
||||||
|
author,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
origin: None,
|
||||||
|
},
|
||||||
|
&SmtpPrivateConfig { name, password },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn update_smtp_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
server: Option<String>,
|
||||||
|
port: Option<u16>,
|
||||||
|
mode: Option<SmtpMode>,
|
||||||
|
username: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
mailto: Option<Vec<String>>,
|
||||||
|
mailto_user: Option<Vec<String>>,
|
||||||
|
from_address: Option<String>,
|
||||||
|
author: Option<String>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
delete: Option<Vec<DeleteableSmtpProperty>>,
|
||||||
|
digest: Option<&str>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
let digest = decode_digest(digest)?;
|
||||||
|
|
||||||
|
api::smtp::update_endpoint(
|
||||||
|
&mut config,
|
||||||
|
name,
|
||||||
|
&SmtpConfigUpdater {
|
||||||
|
server,
|
||||||
|
port,
|
||||||
|
mode,
|
||||||
|
username,
|
||||||
|
mailto,
|
||||||
|
mailto_user,
|
||||||
|
from_address,
|
||||||
|
author,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
},
|
||||||
|
&SmtpPrivateConfigUpdater { password },
|
||||||
|
delete.as_deref(),
|
||||||
|
digest.as_deref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn delete_smtp_endpoint(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::smtp::delete_endpoint(&mut config, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_matchers(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
) -> Result<Vec<MatcherConfig>, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::matcher::get_matchers(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn get_matcher(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
id: &str,
|
||||||
|
) -> Result<MatcherConfig, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::matcher::get_matcher(&config, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn add_matcher(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: String,
|
||||||
|
target: Option<Vec<String>>,
|
||||||
|
match_severity: Option<Vec<SeverityMatcher>>,
|
||||||
|
match_field: Option<Vec<FieldMatcher>>,
|
||||||
|
match_calendar: Option<Vec<CalendarMatcher>>,
|
||||||
|
mode: Option<MatchModeOperator>,
|
||||||
|
invert_match: Option<bool>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::matcher::add_matcher(
|
||||||
|
&mut config,
|
||||||
|
&MatcherConfig {
|
||||||
|
name,
|
||||||
|
match_severity,
|
||||||
|
match_field,
|
||||||
|
match_calendar,
|
||||||
|
target,
|
||||||
|
mode,
|
||||||
|
invert_match,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
origin: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn update_matcher(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
target: Option<Vec<String>>,
|
||||||
|
match_severity: Option<Vec<SeverityMatcher>>,
|
||||||
|
match_field: Option<Vec<FieldMatcher>>,
|
||||||
|
match_calendar: Option<Vec<CalendarMatcher>>,
|
||||||
|
mode: Option<MatchModeOperator>,
|
||||||
|
invert_match: Option<bool>,
|
||||||
|
comment: Option<String>,
|
||||||
|
disable: Option<bool>,
|
||||||
|
delete: Option<Vec<DeleteableMatcherProperty>>,
|
||||||
|
digest: Option<&str>,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
let digest = decode_digest(digest)?;
|
||||||
|
|
||||||
|
api::matcher::update_matcher(
|
||||||
|
&mut config,
|
||||||
|
name,
|
||||||
|
&MatcherConfigUpdater {
|
||||||
|
match_severity,
|
||||||
|
match_field,
|
||||||
|
match_calendar,
|
||||||
|
target,
|
||||||
|
mode,
|
||||||
|
invert_match,
|
||||||
|
comment,
|
||||||
|
disable,
|
||||||
|
},
|
||||||
|
delete.as_deref(),
|
||||||
|
digest.as_deref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export(serialize_error)]
|
||||||
|
fn delete_matcher(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<(), HttpError> {
|
||||||
|
let mut config = this.config.lock().unwrap();
|
||||||
|
api::matcher::delete_matcher(&mut config, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn get_referenced_entities(
|
||||||
|
#[try_from_ref] this: &NotificationConfig,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<Vec<String>, HttpError> {
|
||||||
|
let config = this.config.lock().unwrap();
|
||||||
|
api::common::get_referenced_entities(&config, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_digest(digest: Option<&str>) -> Result<Option<Vec<u8>>, HttpError> {
|
||||||
|
digest
|
||||||
|
.map(hex::decode)
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| api::http_err!(BAD_REQUEST, "invalid digest: {e}"))
|
||||||
|
}
|
||||||
|
}
|
5
pmg-rs/.cargo/config
Normal file
5
pmg-rs/.cargo/config
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[source]
|
||||||
|
[source.debian-packages]
|
||||||
|
directory = "/usr/share/cargo/registry"
|
||||||
|
[source.crates-io]
|
||||||
|
replace-with = "debian-packages"
|
@ -1,14 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pmg-rs"
|
name = "pmg-rs"
|
||||||
version = "0.6.2"
|
version = "0.7.5"
|
||||||
authors = [
|
|
||||||
"Proxmox Support Team <support@proxmox.com>",
|
|
||||||
"Wolfgang Bumiller <w.bumiller@proxmox.com>",
|
|
||||||
"Fabian Ebner <f.ebner@proxmox.com>",
|
|
||||||
]
|
|
||||||
edition = "2018"
|
|
||||||
license = "AGPL-3"
|
|
||||||
description = "PMG parts which have been ported to rust"
|
description = "PMG parts which have been ported to rust"
|
||||||
|
homepage = "https://www.proxmox.com"
|
||||||
|
authors = ["Proxmox Support Team <support@proxmox.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
license = "AGPL-3"
|
||||||
|
repository = "https://git.proxmox.com/?p=proxmox.git"
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"build",
|
"build",
|
||||||
"debian",
|
"debian",
|
||||||
@ -20,22 +19,25 @@ crate-type = [ "cdylib" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
env_logger = "0.10"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
http = "0.2.7"
|
http = "0.2.7"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
nix = "0.24"
|
nix = "0.26"
|
||||||
openssl = "0.10.40"
|
openssl = "0.10.40"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_bytes = "0.11.3"
|
serde_bytes = "0.11"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|
||||||
perlmod = { version = "0.13", features = [ "exporter" ] }
|
perlmod = { version = "0.13.4", features = [ "exporter" ] }
|
||||||
|
|
||||||
proxmox-acme-rs = { version = "0.4", features = ["client"] }
|
proxmox-acme = { version = "0.5", features = ["client"] }
|
||||||
proxmox-apt = "0.9"
|
proxmox-apt = "0.10"
|
||||||
proxmox-http = { version = "0.7", features = ["client-sync", "client-trait"] }
|
proxmox-http = { version = "0.9", features = ["client-sync", "client-trait"] }
|
||||||
proxmox-subscription = "0.3"
|
proxmox-http-error = "0.1.0"
|
||||||
proxmox-sys = "0.4"
|
proxmox-notify = "0.3.1"
|
||||||
proxmox-tfa = { version = "2.1", features = ["api"] }
|
proxmox-subscription = "0.4"
|
||||||
|
proxmox-sys = "0.5"
|
||||||
|
proxmox-tfa = { version = "4.0.4", features = ["api"] }
|
||||||
proxmox-time = "1.1.3"
|
proxmox-time = "1.1.3"
|
||||||
|
4
pmg-rs/Fixup.pm
Normal file
4
pmg-rs/Fixup.pm
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# BEGIN Fixup.pm
|
||||||
|
# This is prepended to the current PMG.pm to force-include the temporary `openssl-probe` fixup.
|
||||||
|
use Proxmox::Lib::SslProbe;
|
||||||
|
# END Fixup.pm
|
@ -1,4 +1,4 @@
|
|||||||
#include /usr/share/dpkg/default.mk
|
#include /usr/share/dpkg/pkg-info.mk
|
||||||
|
|
||||||
PACKAGE=libpmg-rs-perl
|
PACKAGE=libpmg-rs-perl
|
||||||
|
|
||||||
@ -8,33 +8,46 @@ export GITVERSION:=$(shell git rev-parse HEAD)
|
|||||||
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
|
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
|
||||||
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
|
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
|
||||||
|
|
||||||
MAIN_DEB=${PACKAGE}_${DEB_VERSION}_${ARCH}.deb
|
MAIN_DEB=$(PACKAGE)_$(DEB_VERSION)_$(ARCH).deb
|
||||||
DBGSYM_DEB=${PACKAGE}-dbgsym_${DEB_VERSION}_${ARCH}.deb
|
DBGSYM_DEB=$(PACKAGE)-dbgsym_$(DEB_VERSION)_$(ARCH).deb
|
||||||
DEBS=$(MAIN_DEB) $(DBGSYM_DEB)
|
DEBS=$(MAIN_DEB) $(DBGSYM_DEB)
|
||||||
|
DSC=$(PACKAGE)_$(DEB_VERSION_UPSTREAM_REVISION).dsc
|
||||||
|
BUILDDIR ?= $(PACKAGE)-$(DEB_VERSION_UPSTREAM)
|
||||||
|
|
||||||
DESTDIR=
|
DESTDIR=
|
||||||
|
|
||||||
PM_DIR := PMG
|
PM_DIR := PMG
|
||||||
|
|
||||||
|
PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \
|
||||||
|
--lib=pmg_rs \
|
||||||
|
--lib-tag=proxmox \
|
||||||
|
--lib-package=Proxmox::Lib::PMG \
|
||||||
|
--lib-prefix=PMG \
|
||||||
|
--include-file=Fixup.pm
|
||||||
|
|
||||||
|
PERLMOD_PACKAGES := \
|
||||||
|
PMG::RS::APT::Repositories \
|
||||||
|
PMG::RS::Acme \
|
||||||
|
PMG::RS::CSR \
|
||||||
|
PMG::RS::OpenId \
|
||||||
|
PMG::RS::TFA
|
||||||
|
|
||||||
ifeq ($(BUILD_MODE), release)
|
ifeq ($(BUILD_MODE), release)
|
||||||
CARGO_BUILD_ARGS += --release --offline
|
CARGO_BUILD_ARGS += --release --offline
|
||||||
|
TARGET_DIR=release
|
||||||
|
else
|
||||||
|
TARGET_DIR=debug
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all:
|
all: PMG
|
||||||
ifneq ($(BUILD_MODE), skip)
|
|
||||||
cargo build $(CARGO_BUILD_ARGS)
|
cargo build $(CARGO_BUILD_ARGS)
|
||||||
endif
|
|
||||||
|
|
||||||
# always re-create this dir
|
Proxmox PMG: Proxmox/Lib/PMG.pm
|
||||||
# but also copy the local target/ and PMG/ dirs as a build-cache
|
Proxmox/Lib/PMG.pm: Fixup.pm
|
||||||
.PHONY: build
|
$(PERLMOD_GENPACKAGE) $(PERLMOD_PACKAGES)
|
||||||
build:
|
|
||||||
rm -rf build
|
|
||||||
cargo build --release --offline
|
|
||||||
rsync -a debian Makefile Cargo.toml Cargo.lock src target PMG build/
|
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install: target/release/libpmg_rs.so
|
install: target/release/libpmg_rs.so Proxmox/Lib/PMG.pm PMG
|
||||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto
|
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto
|
||||||
install -m644 target/release/libpmg_rs.so $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto/libpmg_rs.so
|
install -m644 target/release/libpmg_rs.so $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto/libpmg_rs.so
|
||||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
||||||
@ -42,25 +55,43 @@ install: target/release/libpmg_rs.so
|
|||||||
install -m644 Proxmox/Lib/PMG.pm $(DESTDIR)$(PERL_INSTALLVENDORLIB)/Proxmox/Lib/PMG.pm
|
install -m644 Proxmox/Lib/PMG.pm $(DESTDIR)$(PERL_INSTALLVENDORLIB)/Proxmox/Lib/PMG.pm
|
||||||
find $(PM_DIR) \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
find $(PM_DIR) \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
||||||
|
|
||||||
.PHONY: deb
|
|
||||||
deb: $(MAIN_DEB)
|
|
||||||
$(MAIN_DEB): build
|
|
||||||
cd build; dpkg-buildpackage -b -us -uc --no-pre-clean
|
|
||||||
lintian $(DEBS)
|
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
rm -rf PMG Proxmox
|
||||||
cargo clean
|
cargo clean
|
||||||
rm -rf *.deb *.dsc *.tar.gz *.buildinfo *.changes Cargo.lock build
|
rm -f *.deb *.dsc *.tar.* *.build *.buildinfo *.changes Cargo.lock
|
||||||
find . -name '*~' -exec rm {} ';'
|
rm -rf $(PACKAGE)-[0-9]*/
|
||||||
|
|
||||||
.PHONY: dinstall
|
.PHONY: dinstall
|
||||||
dinstall: ${DEBS}
|
dinstall: $(DEBS)
|
||||||
dpkg -i ${DEBS}
|
dpkg -i $(DEBS)
|
||||||
|
|
||||||
.PHONY: upload
|
.PHONY: upload
|
||||||
upload: ${DEBS}
|
upload: UPLOAD_DIST ?= $(DEB_DISTRIBUTION)
|
||||||
|
upload: $(DEBS)
|
||||||
# check if working directory is clean
|
# check if working directory is clean
|
||||||
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
||||||
tar cf - ${DEBS} | ssh -X repoman@repo.proxmox.com upload --product pmg --dist bullseye
|
tar cf - $(DEBS) | ssh -X repoman@repo.proxmox.com upload --product pmg --dist $(DEB_DISTRIBUTION)
|
||||||
|
|
||||||
|
$(BUILDDIR): src debian common/src Cargo.toml Makefile .cargo/config
|
||||||
|
rm -rf $(BUILDDIR) $(BUILDDIR).tmp
|
||||||
|
mkdir $(BUILDDIR).tmp
|
||||||
|
mkdir $(BUILDDIR).tmp/common
|
||||||
|
cp -a -t $(BUILDDIR).tmp src debian Cargo.toml Makefile .cargo Fixup.pm
|
||||||
|
cp -a -t $(BUILDDIR).tmp/common common/src
|
||||||
|
mv $(BUILDDIR).tmp $(BUILDDIR)
|
||||||
|
|
||||||
|
.PHONY: deb
|
||||||
|
deb: $(DEBS)
|
||||||
|
$(DEBS): $(BUILDDIR)
|
||||||
|
cd $(BUILDDIR); PATH="/usr/local/bin:/usr/bin" dpkg-buildpackage -b -us -uc
|
||||||
|
lintian $(DEBS)
|
||||||
|
|
||||||
|
.PHONY: dsc
|
||||||
|
dsc: $(DSC)
|
||||||
|
$(DSC): $(BUILDDIR)
|
||||||
|
cd $(BUILDDIR); PATH="/usr/local/bin:/usr/bin" dpkg-buildpackage -S -us -uc -d
|
||||||
|
lintian $(DSC)
|
||||||
|
|
||||||
|
sbuild: $(DSC)
|
||||||
|
sbuild $(DSC)
|
||||||
|
1
pmg-rs/common
Symbolic link
1
pmg-rs/common
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../common
|
@ -1,3 +1,49 @@
|
|||||||
|
libpmg-rs-perl (0.7.5) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* add EAB binding support to ACME
|
||||||
|
|
||||||
|
* make Proxmox::Lib::PMG pull in Proxmox::Lib::SslProbe to work around
|
||||||
|
an issue where the openssl-probe crate calls setenv() and messes up perl's
|
||||||
|
view of the environment
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Thu, 07 Dec 2023 09:57:43 +0100
|
||||||
|
|
||||||
|
libpmg-rs-perl (0.7.4) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* update to env logger 0.10
|
||||||
|
|
||||||
|
* use declare_magic for ACME
|
||||||
|
|
||||||
|
* add Promox::Lib::PMG::use_safe_putenv
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 06 Dec 2023 11:22:56 +0100
|
||||||
|
|
||||||
|
libpmg-rs-perl (0.7.3) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* reset failure counts when unlocking second factors
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 05 Jul 2023 13:35:23 +0200
|
||||||
|
|
||||||
|
libpmg-rs-perl (0.7.2) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* set default log level to 'info'
|
||||||
|
|
||||||
|
* introduce PMG_LOG environment variable to override log level
|
||||||
|
|
||||||
|
* add tfa_lock_status query sub
|
||||||
|
|
||||||
|
* add api_unlock_tfa sub
|
||||||
|
|
||||||
|
* bump proxmox-tfa to 4.0.2
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Tue, 27 Jun 2023 16:01:23 +0200
|
||||||
|
|
||||||
|
libpmg-rs-perl (0.7.1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* rebuild for Debian 12 Bookworm based release series
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Thu, 18 May 2023 12:01:08 +0200
|
||||||
|
|
||||||
libpmg-rs-perl (0.6.2) bullseye; urgency=medium
|
libpmg-rs-perl (0.6.2) bullseye; urgency=medium
|
||||||
|
|
||||||
* update to proxmox-subscription 0.3 / proxmox-http 0.7
|
* update to proxmox-subscription 0.3 / proxmox-http 0.7
|
||||||
|
@ -1 +0,0 @@
|
|||||||
12
|
|
@ -1,43 +1,51 @@
|
|||||||
Source: libpmg-rs-perl
|
Source: libpmg-rs-perl
|
||||||
Section: perl
|
Section: perl
|
||||||
Priority: optional
|
Priority: optional
|
||||||
|
Build-Depends: cargo:native <!nocheck>,
|
||||||
|
debhelper-compat (= 13),
|
||||||
|
librust-openssl-probe-dev (= 0.1.5-1~bpo12+pve1),
|
||||||
|
dh-cargo (>= 25),
|
||||||
|
librust-anyhow-1+default-dev,
|
||||||
|
librust-env-logger-0.10+default-dev,
|
||||||
|
librust-hex-0.4+default-dev,
|
||||||
|
librust-http-0.2+default-dev (>= 0.2.7-~~),
|
||||||
|
librust-libc-0.2+default-dev,
|
||||||
|
librust-nix-0.26+default-dev,
|
||||||
|
librust-openssl-0.10+default-dev (>= 0.10.40-~~),
|
||||||
|
librust-perlmod-0.13+default-dev (>= 0.13.4-~~),
|
||||||
|
librust-perlmod-0.13+exporter-dev (>= 0.13.4-~~),
|
||||||
|
librust-proxmox-acme-0.5+client-dev,
|
||||||
|
librust-proxmox-acme-0.5+default-dev,
|
||||||
|
librust-proxmox-apt-0.10+default-dev,
|
||||||
|
librust-proxmox-http-0.9+client-sync-dev,
|
||||||
|
librust-proxmox-http-0.9+client-trait-dev,
|
||||||
|
librust-proxmox-http-0.9+default-dev,
|
||||||
|
librust-proxmox-http-error-0.1+default-dev,
|
||||||
|
librust-proxmox-notify-0.3+default-dev (>= 0.3.1-~~),
|
||||||
|
librust-proxmox-subscription-0.4+default-dev,
|
||||||
|
librust-proxmox-sys-0.5+default-dev,
|
||||||
|
librust-proxmox-tfa-4+api-dev (>= 4.0.4-~~),
|
||||||
|
librust-proxmox-tfa-4+default-dev (>= 4.0.4-~~),
|
||||||
|
librust-proxmox-time-1+default-dev (>= 1.1.3-~~),
|
||||||
|
librust-serde-1+default-dev,
|
||||||
|
librust-serde-bytes-0.11+default-dev,
|
||||||
|
librust-serde-json-1+default-dev,
|
||||||
|
librust-url-2+default-dev,
|
||||||
|
libstd-rust-dev <!nocheck>,
|
||||||
|
perlmod-bin (>= 0.2.0-3),
|
||||||
|
rustc:native <!nocheck>,
|
||||||
Maintainer: Proxmox Support Team <support@proxmox.com>
|
Maintainer: Proxmox Support Team <support@proxmox.com>
|
||||||
Build-Depends:
|
Standards-Version: 4.6.1
|
||||||
debhelper (>= 12),
|
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
||||||
dh-cargo (>= 24),
|
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
||||||
cargo:native <!nocheck>,
|
|
||||||
rustc:native <!nocheck>,
|
|
||||||
libstd-rust-dev <!nocheck>,
|
|
||||||
librust-anyhow-1+default-dev,
|
|
||||||
librust-hex-0.4+default-dev,
|
|
||||||
librust-http-0.2+default-dev (>= 0.2.7-~~),
|
|
||||||
librust-libc-0.2+default-dev,
|
|
||||||
librust-nix-0.24+default-dev,
|
|
||||||
librust-openssl-0.10+default-dev (>= 0.10.40-~~),
|
|
||||||
librust-perlmod-0.13+default-dev,
|
|
||||||
librust-perlmod-0.13+exporter-dev,
|
|
||||||
librust-proxmox-acme-rs-0.4+client-dev,
|
|
||||||
librust-proxmox-acme-rs-0.4+default-dev,
|
|
||||||
librust-proxmox-apt-0.9+default-dev,
|
|
||||||
librust-proxmox-http-0.7+client-sync-dev,
|
|
||||||
librust-proxmox-http-0.7+client-trait-dev,
|
|
||||||
librust-proxmox-http-0.7+default-dev,
|
|
||||||
librust-proxmox-subscription-0.3+default-dev,
|
|
||||||
librust-proxmox-sys-0.4+default-dev,
|
|
||||||
librust-proxmox-tfa-2+api-dev (>= 2.1-~~),
|
|
||||||
librust-proxmox-tfa-2+default-dev (>= 2.1-~~),
|
|
||||||
librust-proxmox-time-1+default-dev (>= 1.1.3-~~),
|
|
||||||
librust-serde-1+default-dev,
|
|
||||||
librust-serde-bytes-0.11+default-dev (>= 0.11.3-~~),
|
|
||||||
librust-serde-json-1+default-dev,
|
|
||||||
librust-url-2+default-dev,
|
|
||||||
Standards-Version: 4.3.0
|
|
||||||
Homepage: https://www.proxmox.com
|
Homepage: https://www.proxmox.com
|
||||||
|
|
||||||
Package: libpmg-rs-perl
|
Package: libpmg-rs-perl
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: ${perl:Depends},
|
Depends: ${misc:Depends},
|
||||||
|
${perl:Depends},
|
||||||
${shlibs:Depends},
|
${shlibs:Depends},
|
||||||
|
libproxmox-rs-perl (>= 0.3.3),
|
||||||
Description: Components of Proxmox Mail Gateway which have been ported to Rust.
|
Description: Components of Proxmox Mail Gateway which have been ported to Rust.
|
||||||
Contains parts of Proxmox Mail Gateway which have been ported to, or newly
|
Contains parts of Proxmox Mail Gateway which have been ported to, or newly
|
||||||
implemented in the Rust programming language.
|
implemented in the Rust programming language.
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
|
# WARNING: this is *NOT* use as canonical source for d/control, but rather occasionally used via
|
||||||
|
# an invocation like:
|
||||||
|
# make clean
|
||||||
|
# rm debian/control
|
||||||
|
# debcargo package --config debian/debcargo.toml --changelog-ready --no-overlay-write-back --directory libpmg-rs-perl-0.7.1 pmg-rs 0.7.1
|
||||||
|
# mv libpmg-rs-perl-0.7.1/debian/control debian/control
|
||||||
|
# to semi.manually refresh the control file
|
||||||
|
#
|
||||||
|
# NOTE: debcargo thinks this is a source package, but it isn't! Drop provides, the dependencies of
|
||||||
|
# the binary package on rust source packages, Multi-Arch same, and other things that do not make
|
||||||
|
# sense for a combined perl + arch-dependent library package.
|
||||||
|
|
||||||
overlay = "."
|
overlay = "."
|
||||||
crate_src_path = ".."
|
crate_src_path = ".."
|
||||||
maintainer = "Proxmox Support Team <support@proxmox.com>"
|
maintainer = "Proxmox Support Team <support@proxmox.com>"
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
section = "perl"
|
section = "perl"
|
||||||
vcs_git = "git://git.proxmox.com/git/proxmox.git"
|
vcs_git = "git://git.proxmox.com/git/proxmox-perl-rs.git"
|
||||||
vcs_browser = "https://git.proxmox.com/?p=proxmox.git"
|
vcs_browser = "https://git.proxmox.com/?p=proxmox-perl-rs.git"
|
||||||
|
build_depends = [
|
||||||
|
"perlmod-bin",
|
||||||
|
]
|
||||||
|
|
||||||
[packages.libpmg-rs-perl]
|
[packages.bin]
|
||||||
|
name = "libpmg-rs-perl"
|
||||||
|
summary = "Components of Proxmox Mail Gateway which have been ported to Rust."
|
||||||
|
description = """
|
||||||
|
Contains parts of Proxmox Mail Gateway which have been ported to, or newly
|
||||||
|
implemented in the Rust programming language.
|
||||||
|
"""
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
include /usr/share/dpkg/pkg-info.mk
|
||||||
|
include /usr/share/rustc/architecture.mk
|
||||||
|
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
export BUILD_MODE=release
|
export BUILD_MODE=release
|
||||||
|
|
||||||
|
CARGO=/usr/share/cargo/bin/cargo
|
||||||
|
|
||||||
|
export CFLAGS CXXFLAGS CPPFLAGS LDFLAGS
|
||||||
|
export DEB_HOST_RUST_TYPE DEB_HOST_GNU_TYPE
|
||||||
|
export CARGO_HOME = $(CURDIR)/debian/cargo_home
|
||||||
|
|
||||||
|
export DEB_CARGO_CRATE=pmg-rs_$(DEB_VERSION_UPSTREAM)
|
||||||
|
export DEB_CARGO_PACKAGE=pmg-rs
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_configure:
|
||||||
|
@perl -ne 'if (/^version\s*=\s*"(\d+(?:\.\d+)+)"/) { my $$v_cargo = $$1; my $$v_deb = "$(DEB_VERSION_UPSTREAM)"; \
|
||||||
|
die "ERROR: d/changelog <-> Cargo.toml version mismatch: $$v_cargo != $$v_deb\n" if $$v_cargo ne $$v_deb; exit(0); }' Cargo.toml
|
||||||
|
$(CARGO) prepare-debian $(CURDIR)/debian/cargo_registry --link-from-system
|
||||||
|
dh_auto_configure
|
||||||
|
@ -9,8 +9,8 @@ use std::os::unix::fs::OpenOptionsExt;
|
|||||||
use anyhow::{format_err, Error};
|
use anyhow::{format_err, Error};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use proxmox_acme_rs::account::AccountData as AcmeAccountData;
|
use proxmox_acme::account::AccountData as AcmeAccountData;
|
||||||
use proxmox_acme_rs::{Account, Client};
|
use proxmox_acme::{Account, Client};
|
||||||
|
|
||||||
/// Our on-disk format inherited from PVE's proxmox-acme code.
|
/// Our on-disk format inherited from PVE's proxmox-acme code.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
@ -79,6 +79,7 @@ impl Inner {
|
|||||||
tos_agreed: bool,
|
tos_agreed: bool,
|
||||||
contact: Vec<String>,
|
contact: Vec<String>,
|
||||||
rsa_bits: Option<u32>,
|
rsa_bits: Option<u32>,
|
||||||
|
eab_creds: Option<(String, String)>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.tos = if tos_agreed {
|
self.tos = if tos_agreed {
|
||||||
self.client.terms_of_service_url()?.map(str::to_owned)
|
self.client.terms_of_service_url()?.map(str::to_owned)
|
||||||
@ -86,7 +87,9 @@ impl Inner {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let _account = self.client.new_account(contact, tos_agreed, rsa_bits)?;
|
let _account = self
|
||||||
|
.client
|
||||||
|
.new_account(contact, tos_agreed, rsa_bits, eab_creds)?;
|
||||||
let file = OpenOptions::new()
|
let file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
@ -182,67 +185,45 @@ impl Inner {
|
|||||||
#[perlmod::package(name = "PMG::RS::Acme")]
|
#[perlmod::package(name = "PMG::RS::Acme")]
|
||||||
pub mod export {
|
pub mod export {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use serde_bytes::{ByteBuf, Bytes};
|
use serde_bytes::{ByteBuf, Bytes};
|
||||||
|
|
||||||
use perlmod::Value;
|
use perlmod::Value;
|
||||||
use proxmox_acme_rs::directory::Meta;
|
use proxmox_acme::directory::Meta;
|
||||||
use proxmox_acme_rs::order::OrderData;
|
use proxmox_acme::order::OrderData;
|
||||||
use proxmox_acme_rs::{Authorization, Challenge, Order};
|
use proxmox_acme::{Authorization, Challenge, Order};
|
||||||
|
|
||||||
use super::{AccountData, Inner};
|
use super::{AccountData, Inner};
|
||||||
|
|
||||||
const CLASSNAME: &str = "PMG::RS::Acme";
|
perlmod::declare_magic!(Box<Acme> : &Acme as "PMG::RS::Acme");
|
||||||
|
|
||||||
/// An Acme client instance.
|
/// An Acme client instance.
|
||||||
pub struct Acme {
|
pub struct Acme {
|
||||||
inner: Mutex<Inner>,
|
inner: Mutex<Inner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Value> for &'a Acme {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &'a Value) -> Result<&'a Acme, Error> {
|
|
||||||
Ok(unsafe { value.from_blessed_box(CLASSNAME)? })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bless(class: Value, mut ptr: Box<Acme>) -> Result<Value, Error> {
|
|
||||||
let value = Value::new_pointer::<Acme>(&mut *ptr);
|
|
||||||
let value = Value::new_ref(&value);
|
|
||||||
let this = value.bless_sv(&class)?;
|
|
||||||
let _perl = Box::leak(ptr);
|
|
||||||
Ok(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new ACME client instance given an account path and an API directory URL.
|
/// Create a new ACME client instance given an account path and an API directory URL.
|
||||||
#[export(raw_return)]
|
#[export(raw_return)]
|
||||||
pub fn new(#[raw] class: Value, api_directory: String) -> Result<Value, Error> {
|
pub fn new(#[raw] class: Value, api_directory: String) -> Result<Value, Error> {
|
||||||
bless(
|
Ok(perlmod::instantiate_magic!(
|
||||||
class,
|
&class,
|
||||||
Box::new(Acme {
|
MAGIC => Box::new(Acme {
|
||||||
inner: Mutex::new(Inner::new(api_directory)?),
|
inner: Mutex::new(Inner::new(api_directory)?),
|
||||||
}),
|
})
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load an existing account.
|
/// Load an existing account.
|
||||||
#[export(raw_return)]
|
#[export(raw_return)]
|
||||||
pub fn load(#[raw] class: Value, account_path: String) -> Result<Value, Error> {
|
pub fn load(#[raw] class: Value, account_path: String) -> Result<Value, Error> {
|
||||||
bless(
|
Ok(perlmod::instantiate_magic!(
|
||||||
class,
|
&class,
|
||||||
Box::new(Acme {
|
MAGIC => Box::new(Acme {
|
||||||
inner: Mutex::new(Inner::load(account_path)?),
|
inner: Mutex::new(Inner::load(account_path)?),
|
||||||
}),
|
})
|
||||||
)
|
))
|
||||||
}
|
|
||||||
|
|
||||||
#[export(name = "DESTROY")]
|
|
||||||
fn destroy(#[raw] this: Value) {
|
|
||||||
perlmod::destructor!(this, Acme: CLASSNAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new account.
|
/// Create a new account.
|
||||||
@ -260,11 +241,16 @@ pub mod export {
|
|||||||
tos_agreed: bool,
|
tos_agreed: bool,
|
||||||
contact: Vec<String>,
|
contact: Vec<String>,
|
||||||
rsa_bits: Option<u32>,
|
rsa_bits: Option<u32>,
|
||||||
|
eab_kid: Option<String>,
|
||||||
|
eab_hmac_key: Option<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
this.inner
|
this.inner.lock().unwrap().new_account(
|
||||||
.lock()
|
account_path,
|
||||||
.unwrap()
|
tos_agreed,
|
||||||
.new_account(account_path, tos_agreed, contact, rsa_bits)
|
contact,
|
||||||
|
rsa_bits,
|
||||||
|
eab_kid.zip(eab_hmac_key),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the directory's meta information.
|
/// Get the directory's meta information.
|
||||||
|
@ -5,7 +5,7 @@ pub mod export {
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use serde_bytes::ByteBuf;
|
use serde_bytes::ByteBuf;
|
||||||
|
|
||||||
use proxmox_acme_rs::util::Csr;
|
use proxmox_acme::util::Csr;
|
||||||
|
|
||||||
/// Generates a CSR and its accompanying private key.
|
/// Generates a CSR and its accompanying private key.
|
||||||
///
|
///
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
#[path = "../../common/src/mod.rs"]
|
#[path = "../common/src/mod.rs"]
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
pub mod acme;
|
pub mod acme;
|
||||||
pub mod apt;
|
pub mod apt;
|
||||||
pub mod csr;
|
pub mod csr;
|
||||||
pub mod tfa;
|
pub mod tfa;
|
||||||
|
|
||||||
|
#[perlmod::package(name = "Proxmox::Lib::PMG", lib = "pmg_rs")]
|
||||||
|
mod export {
|
||||||
|
use crate::common;
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
pub fn init() {
|
||||||
|
common::logger::init("PMG_LOG", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CLI tools should call this very early. This is a workaround causing environment variable
|
||||||
|
/// manipulation to leak instead of crash. Required when calling into rust code that causes
|
||||||
|
/// `setenv` calls, particularly code using the openssl crate.
|
||||||
|
#[export]
|
||||||
|
pub fn use_safe_putenv() {
|
||||||
|
perlmod::ffi::use_safe_putenv(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,11 +18,13 @@ use nix::errno::Errno;
|
|||||||
use nix::sys::stat::Mode;
|
use nix::sys::stat::Mode;
|
||||||
|
|
||||||
pub(self) use proxmox_tfa::api::{
|
pub(self) use proxmox_tfa::api::{
|
||||||
RecoveryState, TfaChallenge, TfaConfig, TfaResponse, U2fConfig, WebauthnConfig,
|
RecoveryState, TfaChallenge, TfaConfig, TfaResponse, U2fConfig, UserChallengeAccess,
|
||||||
|
WebauthnConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[perlmod::package(name = "PMG::RS::TFA")]
|
#[perlmod::package(name = "PMG::RS::TFA")]
|
||||||
mod export {
|
mod export {
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ mod export {
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use perlmod::Value;
|
use perlmod::Value;
|
||||||
use proxmox_tfa::api::methods;
|
use proxmox_tfa::api::{methods, TfaResult};
|
||||||
|
|
||||||
use super::{TfaConfig, UserAccess};
|
use super::{TfaConfig, UserAccess};
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ mod export {
|
|||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
inner.u2f_registration_challenge(UserAccess::new(&raw_this)?, userid, description)
|
inner.u2f_registration_challenge(&UserAccess::new(&raw_this)?, userid, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish a u2f registration. This updates temporary data in `/run` and therefore the config
|
/// Finish a u2f registration. This updates temporary data in `/run` and therefore the config
|
||||||
@ -120,7 +122,7 @@ mod export {
|
|||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
inner.u2f_registration_finish(UserAccess::new(&raw_this)?, userid, challenge, response)
|
inner.u2f_registration_finish(&UserAccess::new(&raw_this)?, userid, challenge, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a user has any TFA entries of a given type.
|
/// Check if a user has any TFA entries of a given type.
|
||||||
@ -203,7 +205,7 @@ mod export {
|
|||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
match inner.authentication_challenge(
|
match inner.authentication_challenge(
|
||||||
UserAccess::new(&raw_this)?,
|
&UserAccess::new(&raw_this)?,
|
||||||
userid,
|
userid,
|
||||||
origin.as_ref(),
|
origin.as_ref(),
|
||||||
)? {
|
)? {
|
||||||
@ -220,10 +222,7 @@ mod export {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.users
|
.users
|
||||||
.get(userid)
|
.get(userid)
|
||||||
.and_then(|user| {
|
.and_then(|user| user.recovery_state())
|
||||||
let state = user.recovery_state();
|
|
||||||
state.is_available().then(move || state)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes the TFA challenge string (which is a json object) and verifies ther esponse against
|
/// Takes the TFA challenge string (which is a json object) and verifies ther esponse against
|
||||||
@ -244,15 +243,78 @@ mod export {
|
|||||||
let challenge: super::TfaChallenge = serde_json::from_str(challenge)?;
|
let challenge: super::TfaChallenge = serde_json::from_str(challenge)?;
|
||||||
let response: super::TfaResponse = response.parse()?;
|
let response: super::TfaResponse = response.parse()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
inner
|
let result = inner.verify(
|
||||||
.verify(
|
&UserAccess::new(&raw_this)?,
|
||||||
UserAccess::new(&raw_this)?,
|
userid,
|
||||||
userid,
|
&challenge,
|
||||||
&challenge,
|
response,
|
||||||
response,
|
origin.as_ref(),
|
||||||
origin.as_ref(),
|
);
|
||||||
)
|
match result {
|
||||||
.map(|save| save.needs_saving())
|
TfaResult::Success { needs_saving } => Ok(needs_saving),
|
||||||
|
_ => bail!("TFA authentication failed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the TFA challenge string (which is a json object) and verifies ther esponse against
|
||||||
|
/// it.
|
||||||
|
///
|
||||||
|
/// Returns a result hash of the form:
|
||||||
|
/// ```text
|
||||||
|
/// {
|
||||||
|
/// "result": bool, // whether TFA was successful
|
||||||
|
/// "needs-saving": bool, // whether the user config needs saving
|
||||||
|
/// "tfa-limit-reached": bool, // whether the TFA limit was reached (config needs saving)
|
||||||
|
/// "totp-limit-reached": bool, // whether the TOTP limit was reached (config needs saving)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[export]
|
||||||
|
fn authentication_verify2(
|
||||||
|
#[raw] raw_this: Value,
|
||||||
|
//#[try_from_ref] this: &Tfa,
|
||||||
|
userid: &str,
|
||||||
|
challenge: &str, //super::TfaChallenge,
|
||||||
|
response: &str,
|
||||||
|
origin: Option<Url>,
|
||||||
|
) -> Result<TfaReturnValue, Error> {
|
||||||
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
|
let challenge: super::TfaChallenge = serde_json::from_str(challenge)?;
|
||||||
|
let response: super::TfaResponse = response.parse()?;
|
||||||
|
let mut inner = this.inner.lock().unwrap();
|
||||||
|
let result = inner.verify(
|
||||||
|
&UserAccess::new(&raw_this)?,
|
||||||
|
userid,
|
||||||
|
&challenge,
|
||||||
|
response,
|
||||||
|
origin.as_ref(),
|
||||||
|
);
|
||||||
|
Ok(match result {
|
||||||
|
TfaResult::Success { needs_saving } => TfaReturnValue {
|
||||||
|
result: true,
|
||||||
|
needs_saving,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
TfaResult::Locked => TfaReturnValue::default(),
|
||||||
|
TfaResult::Failure {
|
||||||
|
needs_saving,
|
||||||
|
totp_limit_reached,
|
||||||
|
tfa_limit_reached,
|
||||||
|
} => TfaReturnValue {
|
||||||
|
result: false,
|
||||||
|
needs_saving,
|
||||||
|
totp_limit_reached,
|
||||||
|
tfa_limit_reached,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct TfaReturnValue {
|
||||||
|
result: bool,
|
||||||
|
needs_saving: bool,
|
||||||
|
totp_limit_reached: bool,
|
||||||
|
tfa_limit_reached: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DEBUG HELPER: Get the current TOTP value for a given TOTP URI.
|
/// DEBUG HELPER: Get the current TOTP value for a given TOTP URI.
|
||||||
@ -314,7 +376,7 @@ mod export {
|
|||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
methods::add_tfa_entry(
|
methods::add_tfa_entry(
|
||||||
&mut this.inner.lock().unwrap(),
|
&mut this.inner.lock().unwrap(),
|
||||||
UserAccess::new(&raw_this)?,
|
&UserAccess::new(&raw_this)?,
|
||||||
userid,
|
userid,
|
||||||
description,
|
description,
|
||||||
totp,
|
totp,
|
||||||
@ -375,6 +437,66 @@ mod export {
|
|||||||
Err(methods::EntryNotFound) => bail!("no such entry"),
|
Err(methods::EntryNotFound) => bail!("no such entry"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn api_unlock_tfa(#[raw] raw_this: Value, userid: &str) -> Result<bool, Error> {
|
||||||
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
|
Ok(methods::unlock_and_reset_tfa(
|
||||||
|
&mut this.inner.lock().unwrap(),
|
||||||
|
&UserAccess::new(&raw_this)?,
|
||||||
|
userid,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct TfaLockStatus {
|
||||||
|
/// Once a user runs into a TOTP limit they get locked out of TOTP until they successfully use
|
||||||
|
/// a recovery key.
|
||||||
|
#[serde(skip_serializing_if = "bool_is_false", default)]
|
||||||
|
totp_locked: bool,
|
||||||
|
|
||||||
|
/// If a user hits too many 2nd factor failures, they get completely blocked for a while.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
|
#[serde(deserialize_with = "filter_expired_timestamp")]
|
||||||
|
tfa_locked_until: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&proxmox_tfa::api::TfaUserData> for TfaLockStatus {
|
||||||
|
fn from(data: &proxmox_tfa::api::TfaUserData) -> Self {
|
||||||
|
Self {
|
||||||
|
totp_locked: data.totp_locked,
|
||||||
|
tfa_locked_until: data.tfa_locked_until,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_is_false(b: &bool) -> bool {
|
||||||
|
!*b
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn tfa_lock_status(
|
||||||
|
#[try_from_ref] this: &Tfa,
|
||||||
|
userid: Option<&str>,
|
||||||
|
) -> Result<Option<perlmod::Value>, Error> {
|
||||||
|
let this = this.inner.lock().unwrap();
|
||||||
|
if let Some(userid) = userid {
|
||||||
|
if let Some(user) = this.users.get(userid) {
|
||||||
|
Ok(Some(perlmod::to_value(&TfaLockStatus::from(user))?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Some(perlmod::to_value(
|
||||||
|
&HashMap::<String, TfaLockStatus>::from_iter(
|
||||||
|
this.users
|
||||||
|
.iter()
|
||||||
|
.map(|(uid, data)| (uid.clone(), TfaLockStatus::from(data))),
|
||||||
|
),
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attach the path to errors from [`nix::mkir()`].
|
/// Attach the path to errors from [`nix::mkir()`].
|
||||||
@ -440,9 +562,7 @@ fn challenge_data_path(userid: &str, debug: bool) -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
||||||
type Data = UserChallengeData;
|
fn open(&self, userid: &str) -> Result<Box<dyn UserChallengeAccess>, Error> {
|
||||||
|
|
||||||
fn open(&self, userid: &str) -> Result<UserChallengeData, Error> {
|
|
||||||
if self.is_debug() {
|
if self.is_debug() {
|
||||||
mkdir("./local-tfa-challenges", 0o700)?;
|
mkdir("./local-tfa-challenges", 0o700)?;
|
||||||
} else {
|
} else {
|
||||||
@ -485,15 +605,15 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(UserChallengeData {
|
Ok(Box::new(UserChallengeData {
|
||||||
inner,
|
inner,
|
||||||
path,
|
path,
|
||||||
lock: file,
|
lock: file,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `open` without creating the file if it doesn't exist, to finish WA authentications.
|
/// `open` without creating the file if it doesn't exist, to finish WA authentications.
|
||||||
fn open_no_create(&self, userid: &str) -> Result<Option<UserChallengeData>, Error> {
|
fn open_no_create(&self, userid: &str) -> Result<Option<Box<dyn UserChallengeAccess>>, Error> {
|
||||||
let path = challenge_data_path(userid, self.is_debug());
|
let path = challenge_data_path(userid, self.is_debug());
|
||||||
|
|
||||||
let mut file = match std::fs::OpenOptions::new()
|
let mut file = match std::fs::OpenOptions::new()
|
||||||
@ -514,11 +634,11 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
|||||||
format_err!("failed to read challenge data for user {}: {}", userid, err)
|
format_err!("failed to read challenge data for user {}: {}", userid, err)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Some(UserChallengeData {
|
Ok(Some(Box::new(UserChallengeData {
|
||||||
inner,
|
inner,
|
||||||
path,
|
path,
|
||||||
lock: file,
|
lock: file,
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, userid: &str) -> Result<bool, Error> {
|
fn remove(&self, userid: &str) -> Result<bool, Error> {
|
||||||
@ -529,6 +649,10 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
|||||||
Err(err) => Err(err.into()),
|
Err(err) => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enable_lockout(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container of `TfaUserChallenges` with the corresponding file lock guard.
|
/// Container of `TfaUserChallenges` with the corresponding file lock guard.
|
||||||
@ -546,7 +670,7 @@ impl proxmox_tfa::api::UserChallengeAccess for UserChallengeData {
|
|||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(self) -> Result<(), Error> {
|
fn save(&mut self) -> Result<(), Error> {
|
||||||
UserChallengeData::save(self)
|
UserChallengeData::save(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,7 +715,7 @@ impl UserChallengeData {
|
|||||||
///
|
///
|
||||||
/// This currently consumes selfe as we never perform more than 1 insertion/removal, and this
|
/// This currently consumes selfe as we never perform more than 1 insertion/removal, and this
|
||||||
/// way also unlocks early.
|
/// way also unlocks early.
|
||||||
fn save(mut self) -> Result<(), Error> {
|
fn save(&mut self) -> Result<(), Error> {
|
||||||
self.rewind()?;
|
self.rewind()?;
|
||||||
|
|
||||||
serde_json::to_writer(&mut &self.lock, &self.inner).map_err(|err| {
|
serde_json::to_writer(&mut &self.lock, &self.inner).map_err(|err| {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pve-rs"
|
name = "pve-rs"
|
||||||
version = "0.7.3"
|
version = "0.8.8"
|
||||||
authors = ["Proxmox Support Team <support@proxmox.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
license = "AGPL-3"
|
|
||||||
description = "PVE parts which have been ported to Rust"
|
description = "PVE parts which have been ported to Rust"
|
||||||
homepage = "https://www.proxmox.com"
|
homepage = "https://www.proxmox.com"
|
||||||
|
authors = ["Proxmox Support Team <support@proxmox.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
license = "AGPL-3"
|
||||||
|
repository = "https://git.proxmox.com/?p=proxmox.git"
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"debian",
|
"debian",
|
||||||
@ -18,10 +19,12 @@ crate-type = [ "cdylib" ]
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
base32 = "0.4"
|
base32 = "0.4"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
env_logger = "0.10"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
http = "0.2.7"
|
http = "0.2.7"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
nix = "0.24"
|
log = "0.4.17"
|
||||||
|
nix = "0.26"
|
||||||
openssl = "0.10.40"
|
openssl = "0.10.40"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_bytes = "0.11"
|
serde_bytes = "0.11"
|
||||||
@ -30,11 +33,13 @@ url = "2"
|
|||||||
|
|
||||||
perlmod = { version = "0.13", features = [ "exporter" ] }
|
perlmod = { version = "0.13", features = [ "exporter" ] }
|
||||||
|
|
||||||
proxmox-apt = "0.9"
|
proxmox-apt = "0.10.6"
|
||||||
proxmox-http = { version = "0.7", features = ["client-sync", "client-trait"] }
|
proxmox-http = { version = "0.9", features = ["client-sync", "client-trait"] }
|
||||||
proxmox-openid = "0.9.5"
|
proxmox-http-error = "0.1.0"
|
||||||
proxmox-resource-scheduling = "0.1"
|
proxmox-notify = { version = "0.3.1", features = ["pve-context"] }
|
||||||
proxmox-subscription = "0.3"
|
proxmox-openid = "0.10"
|
||||||
proxmox-sys = "0.4"
|
proxmox-resource-scheduling = "0.3.0"
|
||||||
proxmox-tfa = { version = "2.1", features = ["api"] }
|
proxmox-subscription = "0.4"
|
||||||
|
proxmox-sys = "0.5"
|
||||||
|
proxmox-tfa = { version = "4.0.4", features = ["api"] }
|
||||||
proxmox-time = "1.1.3"
|
proxmox-time = "1.1.3"
|
||||||
|
4
pve-rs/Fixup.pm
Normal file
4
pve-rs/Fixup.pm
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# BEGIN Fixup.pm
|
||||||
|
# This is prepended to the current PMG.pm to force-include the temporary `openssl-probe` fixup.
|
||||||
|
use Proxmox::Lib::SslProbe;
|
||||||
|
# END Fixup.pm
|
@ -1,4 +1,4 @@
|
|||||||
#include /usr/share/dpkg/default.mk
|
#include /usr/share/dpkg/pkg-info.mk
|
||||||
|
|
||||||
PACKAGE=libpve-rs-perl
|
PACKAGE=libpve-rs-perl
|
||||||
export PERLMOD_PRODUCT=PVE
|
export PERLMOD_PRODUCT=PVE
|
||||||
@ -9,33 +9,52 @@ export GITVERSION:=$(shell git rev-parse HEAD)
|
|||||||
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
|
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
|
||||||
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
|
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
|
||||||
|
|
||||||
MAIN_DEB=${PACKAGE}_${DEB_VERSION}_${ARCH}.deb
|
MAIN_DEB=$(PACKAGE)_$(DEB_VERSION)_$(ARCH).deb
|
||||||
DBGSYM_DEB=${PACKAGE}-dbgsym_${DEB_VERSION}_${ARCH}.deb
|
DBGSYM_DEB=$(PACKAGE)-dbgsym_$(DEB_VERSION)_$(ARCH).deb
|
||||||
DEBS=$(MAIN_DEB) $(DBGSYM_DEB)
|
DEBS=$(MAIN_DEB) $(DBGSYM_DEB)
|
||||||
|
DSC=$(PACKAGE)_$(DEB_VERSION_UPSTREAM_REVISION).dsc
|
||||||
|
BUILDDIR ?= $(PACKAGE)-$(DEB_VERSION_UPSTREAM)
|
||||||
|
|
||||||
DESTDIR=
|
DESTDIR=
|
||||||
|
|
||||||
PM_DIR := PVE
|
PM_DIR := PVE
|
||||||
|
|
||||||
|
PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \
|
||||||
|
--lib=pve_rs \
|
||||||
|
--lib-tag=proxmox \
|
||||||
|
--lib-package=Proxmox::Lib::PVE \
|
||||||
|
--lib-prefix=PVE \
|
||||||
|
--include-file=Fixup.pm
|
||||||
|
|
||||||
|
PERLMOD_PACKAGES := \
|
||||||
|
PVE::RS::APT::Repositories \
|
||||||
|
PVE::RS::OpenId \
|
||||||
|
PVE::RS::ResourceScheduling::Static \
|
||||||
|
PVE::RS::TFA
|
||||||
|
|
||||||
ifeq ($(BUILD_MODE), release)
|
ifeq ($(BUILD_MODE), release)
|
||||||
CARGO_BUILD_ARGS += --release --offline
|
CARGO_BUILD_ARGS += --release --offline
|
||||||
|
TARGET_DIR=release
|
||||||
|
else
|
||||||
|
TARGET_DIR=debug
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all:
|
all: PVE
|
||||||
ifneq ($(BUILD_MODE), skip)
|
|
||||||
cargo build $(CARGO_BUILD_ARGS)
|
cargo build $(CARGO_BUILD_ARGS)
|
||||||
endif
|
mkdir -p test/Proxmox/Lib
|
||||||
|
sed -r -e \
|
||||||
|
's@^sub libfile.*$$@sub libfile { "$(shell pwd)/target/$(TARGET_DIR)/libpve_rs.so"; }@' \
|
||||||
|
Proxmox/Lib/PVE.pm >test/Proxmox/Lib/PVE.pm
|
||||||
|
|
||||||
# always re-create this dir
|
Proxmox PVE: Proxmox/Lib/PVE.pm
|
||||||
# but also copy the local target/ and PVE/ dirs as a build-cache
|
Proxmox/Lib/PVE.pm: Fixup.pm
|
||||||
.PHONY: build
|
$(PERLMOD_GENPACKAGE) $(PERLMOD_PACKAGES)
|
||||||
build:
|
|
||||||
rm -rf build
|
check: all
|
||||||
cargo build --release --offline
|
$(MAKE) -C test test
|
||||||
rsync -a debian Makefile Cargo.toml Cargo.lock src target PVE build/
|
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install: target/release/libpve_rs.so
|
install: target/release/libpve_rs.so Proxmox/Lib/PVE.pm PVE
|
||||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto
|
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto
|
||||||
install -m644 target/release/libpve_rs.so $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto/libpve_rs.so
|
install -m644 target/release/libpve_rs.so $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto/libpve_rs.so
|
||||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
||||||
@ -43,25 +62,42 @@ install: target/release/libpve_rs.so
|
|||||||
install -m644 Proxmox/Lib/PVE.pm $(DESTDIR)$(PERL_INSTALLVENDORLIB)/Proxmox/Lib/PVE.pm
|
install -m644 Proxmox/Lib/PVE.pm $(DESTDIR)$(PERL_INSTALLVENDORLIB)/Proxmox/Lib/PVE.pm
|
||||||
find $(PM_DIR) \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
find $(PM_DIR) \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
||||||
|
|
||||||
.PHONY: deb
|
|
||||||
deb: $(MAIN_DEB)
|
|
||||||
$(MAIN_DEB): build
|
|
||||||
cd build; dpkg-buildpackage -b -us -uc --no-pre-clean
|
|
||||||
lintian $(DEBS)
|
|
||||||
|
|
||||||
distclean: clean
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
rm -rf PVE Proxmox
|
||||||
cargo clean
|
cargo clean
|
||||||
rm -rf *.deb *.dsc *.tar.gz *.buildinfo *.changes Cargo.lock build
|
rm -f *.deb *.dsc *.tar.* *.build *.buildinfo *.changes Cargo.lock
|
||||||
find . -name '*~' -exec rm {} ';'
|
rm -rf $(PACKAGE)-[0-9]*/
|
||||||
|
|
||||||
.PHONY: dinstall
|
.PHONY: dinstall
|
||||||
dinstall: ${DEBS}
|
dinstall: $(DEBS)
|
||||||
dpkg -i ${DEBS}
|
dpkg -i $(DEBS)
|
||||||
|
|
||||||
.PHONY: upload
|
.PHONY: upload
|
||||||
upload: ${DEBS}
|
upload: UPLOAD_DIST ?= $(DEB_DISTRIBUTION)
|
||||||
|
upload: $(DEBS)
|
||||||
# check if working directory is clean
|
# check if working directory is clean
|
||||||
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
||||||
tar cf - ${DEBS} | ssh -X repoman@repo.proxmox.com upload --product pve --dist bullseye
|
tar cf - $(DEBS) | ssh -X repoman@repo.proxmox.com upload --product pve --dist $(DEB_DISTRIBUTION)
|
||||||
|
|
||||||
|
$(BUILDDIR): src debian test common/src Cargo.toml Makefile .cargo/config
|
||||||
|
rm -rf $(BUILDDIR) $(BUILDDIR).tmp
|
||||||
|
mkdir $(BUILDDIR).tmp
|
||||||
|
mkdir $(BUILDDIR).tmp/common
|
||||||
|
cp -a -t $(BUILDDIR).tmp src debian test Cargo.toml Makefile .cargo Fixup.pm
|
||||||
|
cp -a -t $(BUILDDIR).tmp/common common/src
|
||||||
|
mv $(BUILDDIR).tmp $(BUILDDIR)
|
||||||
|
|
||||||
|
.PHONY: deb
|
||||||
|
deb: $(DEBS)
|
||||||
|
$(DEBS): $(BUILDDIR)
|
||||||
|
cd $(BUILDDIR); PATH="/usr/local/bin:/usr/bin" dpkg-buildpackage -b -us -uc
|
||||||
|
lintian $(DEBS)
|
||||||
|
|
||||||
|
.PHONY: dsc
|
||||||
|
dsc: $(DSC)
|
||||||
|
$(DSC): $(BUILDDIR)
|
||||||
|
cd $(BUILDDIR); PATH="/usr/local/bin:/usr/bin" dpkg-buildpackage -S -us -uc -d
|
||||||
|
lintian $(DSC)
|
||||||
|
|
||||||
|
sbuild: $(DSC)
|
||||||
|
sbuild $(DSC)
|
||||||
|
1
pve-rs/common
Symbolic link
1
pve-rs/common
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../common
|
@ -1,3 +1,114 @@
|
|||||||
|
libpve-rs-perl (0.8.8) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* notify: include 'hostname' and 'type' metadata fields for forwarded mails
|
||||||
|
|
||||||
|
* notify: smtp: forward original message instead of nesting
|
||||||
|
|
||||||
|
* notify: smtp: add 'Auto-Submitted' header to email body
|
||||||
|
|
||||||
|
* notify: api: allow resetting built-in targets if used by a matcher
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 10 Jan 2024 14:19:47 +0100
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.7) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* notify: adapt to new matcher-based notification routing
|
||||||
|
|
||||||
|
* notify: add bindings for smtp API calls
|
||||||
|
|
||||||
|
* pve-rs: notify: remove notify_context for PVE
|
||||||
|
|
||||||
|
* notify: add 'disable' parameter
|
||||||
|
|
||||||
|
* notify: support 'origin' paramter
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Fri, 17 Nov 2023 13:41:17 +0100
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.6) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* re-build with newer proxmox-apt depenceny to make Ceph Reef repo available
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Tue, 05 Sep 2023 15:37:44 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.5) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* add PVE::RS::Notify module
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Mon, 24 Jul 2023 11:18:56 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.4) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* reset failure counts when unlocking second factors
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 05 Jul 2023 13:30:17 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.3) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* set default log level to 'info'
|
||||||
|
|
||||||
|
* introduce PVE_LOG environment variable to override log level
|
||||||
|
|
||||||
|
* add tfa_lock_status query sub
|
||||||
|
|
||||||
|
* bump proxmox-tfa to 4.0.2
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Mon, 05 Jun 2023 12:55:03 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.2) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* update proxmox-apt which updated repositories info for bookworm
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Sun, 04 Jun 2023 18:33:42 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* bump proxmox-apt,http,openid,subscription,sys crates to their bookworm
|
||||||
|
versions
|
||||||
|
|
||||||
|
* bump proxmox-tfa to 4.0.1 to include the unlock API
|
||||||
|
|
||||||
|
* enable TFA lockout and provide the `api_unlock_tfa` call
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 31 May 2023 14:17:31 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.8.0) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* rebuild for Debian 12 Bookworm based release series
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Tue, 16 May 2023 14:26:52 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.7.6) bullseye; urgency=medium
|
||||||
|
|
||||||
|
* update to new tfa crate
|
||||||
|
|
||||||
|
* introduce new authentication_verify2 call to utilize the totp/tfa locking
|
||||||
|
capabilities of the TFA API
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 10 May 2023 10:54:10 +0200
|
||||||
|
|
||||||
|
libpve-rs-perl (0.7.5) bullseye; urgency=medium
|
||||||
|
|
||||||
|
* update proxmox-resource-scheduling dependency to 0.2.1 to pull in an
|
||||||
|
improvement for with services where CPU should matter more if there is no
|
||||||
|
memory load at all
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Tue, 21 Mar 2023 17:58:22 +0100
|
||||||
|
|
||||||
|
libpve-rs-perl (0.7.4) bullseye; urgency=medium
|
||||||
|
|
||||||
|
* initialize logging when shared library is loaded
|
||||||
|
|
||||||
|
* update to new TFA crate
|
||||||
|
|
||||||
|
* bump proxmox-resource-scheduling dependency to 0.2 to pull in a fix for
|
||||||
|
usage calculation for homogeneous nodes
|
||||||
|
|
||||||
|
* pve: test: resource scheduling: add test with overcommitted node
|
||||||
|
|
||||||
|
* update nix to 0.26
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Tue, 21 Mar 2023 15:28:08 +0100
|
||||||
|
|
||||||
libpve-rs-perl (0.7.3) bullseye; urgency=medium
|
libpve-rs-perl (0.7.3) bullseye; urgency=medium
|
||||||
|
|
||||||
* add PVE::RS::ResourceScheduling::Static and tests
|
* add PVE::RS::ResourceScheduling::Static and tests
|
||||||
|
@ -1 +0,0 @@
|
|||||||
10
|
|
@ -1,39 +1,44 @@
|
|||||||
Source: libpve-rs-perl
|
Source: libpve-rs-perl
|
||||||
Section: perl
|
Section: perl
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends:
|
Build-Depends: cargo:native <!nocheck>,
|
||||||
debhelper (>= 12),
|
debhelper-compat (= 13),
|
||||||
dh-cargo (>= 24),
|
dh-cargo (>= 25),
|
||||||
cargo:native <!nocheck>,
|
librust-anyhow-1+default-dev,
|
||||||
rustc:native <!nocheck>,
|
librust-base32-0.4+default-dev,
|
||||||
libstd-rust-dev <!nocheck>,
|
librust-base64-0.13+default-dev,
|
||||||
librust-anyhow-1+default-dev,
|
librust-env-logger-0.10+default-dev,
|
||||||
librust-base32-0.4+default-dev,
|
librust-hex-0.4+default-dev,
|
||||||
librust-base64-0.13+default-dev,
|
librust-http-0.2+default-dev (>= 0.2.7-~~),
|
||||||
librust-hex-0.4+default-dev,
|
librust-libc-0.2+default-dev,
|
||||||
librust-http-0.2+default-dev (>= 0.2.7-~~),
|
librust-log-0.4+default-dev (>= 0.4.17-~~),
|
||||||
librust-libc-0.2+default-dev,
|
librust-nix-0.26+default-dev,
|
||||||
librust-nix-0.24+default-dev,
|
librust-openssl-0.10+default-dev (>= 0.10.40-~~),
|
||||||
librust-openssl-0.10+default-dev (>= 0.10.40-~~),
|
librust-perlmod-0.13+default-dev,
|
||||||
librust-perlmod-0.13+default-dev,
|
librust-perlmod-0.13+exporter-dev,
|
||||||
librust-perlmod-0.13+exporter-dev,
|
librust-proxmox-apt-0.10+default-dev (>= 0.10.6-~~),
|
||||||
librust-proxmox-apt-0.9+default-dev,
|
librust-proxmox-http-0.9+client-sync-dev,
|
||||||
librust-proxmox-http-0.7+client-sync-dev,
|
librust-proxmox-http-0.9+client-trait-dev,
|
||||||
librust-proxmox-http-0.7+client-trait-dev,
|
librust-proxmox-http-0.9+default-dev,
|
||||||
librust-proxmox-http-0.7+default-dev,
|
librust-proxmox-http-error-0.1+default-dev,
|
||||||
librust-proxmox-openid-0.9+default-dev (>= 0.9.5-~~),
|
librust-proxmox-notify-0.3+default-dev (>= 0.3.1-~~),
|
||||||
librust-proxmox-resource-scheduling-0.1+default-dev,
|
librust-proxmox-notify-0.3+pve-context-dev (>= 0.3.1-~~),
|
||||||
librust-proxmox-subscription-0.3+default-dev,
|
librust-proxmox-openid-0.10+default-dev,
|
||||||
librust-proxmox-sys-0.4+default-dev,
|
librust-proxmox-resource-scheduling-0.3+default-dev,
|
||||||
librust-proxmox-tfa-2+api-dev (>= 2.1-~~),
|
librust-proxmox-subscription-0.4+default-dev,
|
||||||
librust-proxmox-tfa-2+default-dev (>= 2.1-~~),
|
librust-proxmox-sys-0.5+default-dev,
|
||||||
librust-proxmox-time-1+default-dev (>= 1.1.3-~~),
|
librust-proxmox-tfa-4+api-dev (>= 4.0.4-~~),
|
||||||
librust-serde-1+default-dev,
|
librust-proxmox-tfa-4+default-dev (>= 4.0.4-~~),
|
||||||
librust-serde-bytes-0.11+default-dev,
|
librust-proxmox-time-1+default-dev (>= 1.1.3-~~),
|
||||||
librust-serde-json-1+default-dev,
|
librust-serde-1+default-dev,
|
||||||
librust-url-2+default-dev,
|
librust-serde-bytes-0.11+default-dev,
|
||||||
|
librust-serde-json-1+default-dev,
|
||||||
|
librust-url-2+default-dev,
|
||||||
|
libstd-rust-dev <!nocheck>,
|
||||||
|
perlmod-bin (>= 0.2.0-3),
|
||||||
|
rustc:native <!nocheck>,
|
||||||
Maintainer: Proxmox Support Team <support@proxmox.com>
|
Maintainer: Proxmox Support Team <support@proxmox.com>
|
||||||
Standards-Version: 4.5.1
|
Standards-Version: 4.6.1
|
||||||
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
||||||
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
||||||
Homepage: https://www.proxmox.com
|
Homepage: https://www.proxmox.com
|
||||||
@ -41,14 +46,13 @@ Rules-Requires-Root: no
|
|||||||
|
|
||||||
Package: libpve-rs-perl
|
Package: libpve-rs-perl
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends:
|
Depends: ${misc:Depends},
|
||||||
${misc:Depends},
|
${perl:Depends},
|
||||||
${perl:Depends},
|
${shlibs:Depends},
|
||||||
${shlibs:Depends},
|
libproxmox-rs-perl (>= 0.3.3),
|
||||||
Breaks:
|
Breaks: libpve-access-control (<< 7.1-3),
|
||||||
libpve-access-control (<< 7.1-3),
|
libpve-common-perl (<< 7.1-4),
|
||||||
libpve-common-perl (<< 7.1-4),
|
pve-manager (<< 7.1-11),
|
||||||
pve-manager (<< 7.1-11),
|
|
||||||
Description: PVE parts which have been ported to Rust - Rust source code
|
Description: PVE parts which have been ported to Rust - Rust source code
|
||||||
This package contains the source for the Rust pve-rs crate, packaged by
|
This package contains the source for the Rust pve-rs crate, packaged by
|
||||||
debcargo for use with cargo and dh-cargo.
|
debcargo for use with cargo and dh-cargo.
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
include /usr/share/dpkg/pkg-info.mk
|
||||||
|
include /usr/share/rustc/architecture.mk
|
||||||
|
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
export BUILD_MODE=release
|
export BUILD_MODE=release
|
||||||
|
|
||||||
|
CARGO=/usr/share/cargo/bin/cargo
|
||||||
|
|
||||||
|
export CFLAGS CXXFLAGS CPPFLAGS LDFLAGS
|
||||||
|
export DEB_HOST_RUST_TYPE DEB_HOST_GNU_TYPE
|
||||||
|
export CARGO_HOME = $(CURDIR)/debian/cargo_home
|
||||||
|
|
||||||
|
export DEB_CARGO_CRATE=pve-rs_$(DEB_VERSION_UPSTREAM)
|
||||||
|
export DEB_CARGO_PACKAGE=pve-rs
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_configure:
|
||||||
|
@perl -ne 'if (/^version\s*=\s*"(\d+(?:\.\d+)+)"/) { my $$v_cargo = $$1; my $$v_deb = "$(DEB_VERSION_UPSTREAM)"; \
|
||||||
|
die "ERROR: d/changelog <-> Cargo.toml version mismatch: $$v_cargo != $$v_deb\n" if $$v_cargo ne $$v_deb; exit(0); }' Cargo.toml
|
||||||
|
$(CARGO) prepare-debian $(CURDIR)/debian/cargo_registry --link-from-system
|
||||||
|
dh_auto_configure
|
||||||
|
@ -1,9 +1,22 @@
|
|||||||
//! Rust library for the Proxmox VE code base.
|
//! Rust library for the Proxmox VE code base.
|
||||||
|
|
||||||
#[path = "../../common/src/mod.rs"]
|
#[path = "../common/src/mod.rs"]
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
//pub mod apt;
|
//pub mod apt;
|
||||||
pub mod openid;
|
pub mod openid;
|
||||||
pub mod resource_scheduling;
|
pub mod resource_scheduling;
|
||||||
pub mod tfa;
|
pub mod tfa;
|
||||||
|
|
||||||
|
#[perlmod::package(name = "Proxmox::Lib::PVE", lib = "pve_rs")]
|
||||||
|
mod export {
|
||||||
|
use proxmox_notify::context::pve::PVE_CONTEXT;
|
||||||
|
|
||||||
|
use crate::common;
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
pub fn init() {
|
||||||
|
common::logger::init("PVE_LOG", "info");
|
||||||
|
proxmox_notify::context::set_context(&PVE_CONTEXT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[perlmod::package(name = "PVE::RS::OpenId", lib = "pve_rs")]
|
#[perlmod::package(name = "PVE::RS::OpenId", lib = "pve_rs")]
|
||||||
mod export {
|
mod export {
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
@ -9,34 +8,13 @@ mod export {
|
|||||||
|
|
||||||
use proxmox_openid::{OpenIdAuthenticator, OpenIdConfig, PrivateAuthState};
|
use proxmox_openid::{OpenIdAuthenticator, OpenIdConfig, PrivateAuthState};
|
||||||
|
|
||||||
const CLASSNAME: &str = "PVE::RS::OpenId";
|
perlmod::declare_magic!(Box<OpenId> : &OpenId as "PVE::RS::OpenId");
|
||||||
|
|
||||||
/// An OpenIdAuthenticator client instance.
|
/// An OpenIdAuthenticator client instance.
|
||||||
pub struct OpenId {
|
pub struct OpenId {
|
||||||
inner: Mutex<OpenIdAuthenticator>,
|
inner: Mutex<OpenIdAuthenticator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Value> for &'a OpenId {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &'a Value) -> Result<&'a OpenId, Error> {
|
|
||||||
Ok(unsafe { value.from_blessed_box(CLASSNAME)? })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bless(class: Value, mut ptr: Box<OpenId>) -> Result<Value, Error> {
|
|
||||||
let value = Value::new_pointer::<OpenId>(&mut *ptr);
|
|
||||||
let value = Value::new_ref(&value);
|
|
||||||
let this = value.bless_sv(&class)?;
|
|
||||||
let _perl = Box::leak(ptr);
|
|
||||||
Ok(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export(name = "DESTROY")]
|
|
||||||
fn destroy(#[raw] this: Value) {
|
|
||||||
perlmod::destructor!(this, OpenId: CLASSNAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new OpenId client instance
|
/// Create a new OpenId client instance
|
||||||
#[export(raw_return)]
|
#[export(raw_return)]
|
||||||
pub fn discover(
|
pub fn discover(
|
||||||
@ -45,12 +23,12 @@ mod export {
|
|||||||
redirect_url: &str,
|
redirect_url: &str,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
let open_id = OpenIdAuthenticator::discover(&config, redirect_url)?;
|
let open_id = OpenIdAuthenticator::discover(&config, redirect_url)?;
|
||||||
bless(
|
Ok(perlmod::instantiate_magic!(
|
||||||
class,
|
&class,
|
||||||
Box::new(OpenId {
|
MAGIC => Box::new(OpenId {
|
||||||
inner: Mutex::new(open_id),
|
inner: Mutex::new(open_id),
|
||||||
}),
|
})
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export]
|
#[export]
|
||||||
|
@ -21,11 +21,13 @@ use nix::sys::stat::Mode;
|
|||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
pub(self) use proxmox_tfa::api::{
|
pub(self) use proxmox_tfa::api::{
|
||||||
RecoveryState, TfaChallenge, TfaConfig, TfaResponse, TfaUserData, U2fConfig, WebauthnConfig,
|
RecoveryState, TfaChallenge, TfaConfig, TfaResponse, TfaUserData, U2fConfig,
|
||||||
|
UserChallengeAccess, WebauthnConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[perlmod::package(name = "PVE::RS::TFA")]
|
#[perlmod::package(name = "PVE::RS::TFA")]
|
||||||
mod export {
|
mod export {
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ mod export {
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use perlmod::Value;
|
use perlmod::Value;
|
||||||
use proxmox_tfa::api::methods;
|
use proxmox_tfa::api::{methods, TfaResult};
|
||||||
|
|
||||||
use super::{TfaConfig, UserAccess};
|
use super::{TfaConfig, UserAccess};
|
||||||
|
|
||||||
@ -173,7 +175,7 @@ mod export {
|
|||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
inner.u2f_registration_challenge(UserAccess::new(&raw_this)?, userid, description)
|
inner.u2f_registration_challenge(&UserAccess::new(&raw_this)?, userid, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish a u2f registration. This updates temporary data in `/run` and therefore the config
|
/// Finish a u2f registration. This updates temporary data in `/run` and therefore the config
|
||||||
@ -188,7 +190,7 @@ mod export {
|
|||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
inner.u2f_registration_finish(UserAccess::new(&raw_this)?, userid, challenge, response)
|
inner.u2f_registration_finish(&UserAccess::new(&raw_this)?, userid, challenge, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a user has any TFA entries of a given type.
|
/// Check if a user has any TFA entries of a given type.
|
||||||
@ -249,7 +251,7 @@ mod export {
|
|||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
match inner.authentication_challenge(
|
match inner.authentication_challenge(
|
||||||
UserAccess::new(&raw_this)?,
|
&UserAccess::new(&raw_this)?,
|
||||||
userid,
|
userid,
|
||||||
origin.as_ref(),
|
origin.as_ref(),
|
||||||
)? {
|
)? {
|
||||||
@ -266,10 +268,7 @@ mod export {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.users
|
.users
|
||||||
.get(userid)
|
.get(userid)
|
||||||
.and_then(|user| {
|
.and_then(|user| user.recovery_state())
|
||||||
let state = user.recovery_state();
|
|
||||||
state.is_available().then(move || state)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes the TFA challenge string (which is a json object) and verifies ther esponse against
|
/// Takes the TFA challenge string (which is a json object) and verifies ther esponse against
|
||||||
@ -277,6 +276,8 @@ mod export {
|
|||||||
///
|
///
|
||||||
/// NOTE: This returns a boolean whether the config data needs to be *saved* after this call
|
/// NOTE: This returns a boolean whether the config data needs to be *saved* after this call
|
||||||
/// (to use up recovery keys!).
|
/// (to use up recovery keys!).
|
||||||
|
///
|
||||||
|
/// WARNING: This method is now deprecated, as it failures were communicated via croaking.
|
||||||
#[export]
|
#[export]
|
||||||
fn authentication_verify(
|
fn authentication_verify(
|
||||||
#[raw] raw_this: Value,
|
#[raw] raw_this: Value,
|
||||||
@ -290,15 +291,78 @@ mod export {
|
|||||||
let challenge: super::TfaChallenge = serde_json::from_str(challenge)?;
|
let challenge: super::TfaChallenge = serde_json::from_str(challenge)?;
|
||||||
let response: super::TfaResponse = response.parse()?;
|
let response: super::TfaResponse = response.parse()?;
|
||||||
let mut inner = this.inner.lock().unwrap();
|
let mut inner = this.inner.lock().unwrap();
|
||||||
inner
|
let result = inner.verify(
|
||||||
.verify(
|
&UserAccess::new(&raw_this)?,
|
||||||
UserAccess::new(&raw_this)?,
|
userid,
|
||||||
userid,
|
&challenge,
|
||||||
&challenge,
|
response,
|
||||||
response,
|
origin.as_ref(),
|
||||||
origin.as_ref(),
|
);
|
||||||
)
|
match result {
|
||||||
.map(|save| save.needs_saving())
|
TfaResult::Success { needs_saving } => Ok(needs_saving),
|
||||||
|
_ => bail!("TFA authentication failed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the TFA challenge string (which is a json object) and verifies ther esponse against
|
||||||
|
/// it.
|
||||||
|
///
|
||||||
|
/// Returns a result hash of the form:
|
||||||
|
/// ```text
|
||||||
|
/// {
|
||||||
|
/// "result": bool, // whether TFA was successful
|
||||||
|
/// "needs-saving": bool, // whether the user config needs saving
|
||||||
|
/// "tfa-limit-reached": bool, // whether the TFA limit was reached (config needs saving)
|
||||||
|
/// "totp-limit-reached": bool, // whether the TOTP limit was reached (config needs saving)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[export]
|
||||||
|
fn authentication_verify2(
|
||||||
|
#[raw] raw_this: Value,
|
||||||
|
//#[try_from_ref] this: &Tfa,
|
||||||
|
userid: &str,
|
||||||
|
challenge: &str, //super::TfaChallenge,
|
||||||
|
response: &str,
|
||||||
|
origin: Option<Url>,
|
||||||
|
) -> Result<TfaReturnValue, Error> {
|
||||||
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
|
let challenge: super::TfaChallenge = serde_json::from_str(challenge)?;
|
||||||
|
let response: super::TfaResponse = response.parse()?;
|
||||||
|
let mut inner = this.inner.lock().unwrap();
|
||||||
|
let result = inner.verify(
|
||||||
|
&UserAccess::new(&raw_this)?,
|
||||||
|
userid,
|
||||||
|
&challenge,
|
||||||
|
response,
|
||||||
|
origin.as_ref(),
|
||||||
|
);
|
||||||
|
Ok(match result {
|
||||||
|
TfaResult::Success { needs_saving } => TfaReturnValue {
|
||||||
|
result: true,
|
||||||
|
needs_saving,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
TfaResult::Locked => TfaReturnValue::default(),
|
||||||
|
TfaResult::Failure {
|
||||||
|
needs_saving,
|
||||||
|
totp_limit_reached,
|
||||||
|
tfa_limit_reached,
|
||||||
|
} => TfaReturnValue {
|
||||||
|
result: false,
|
||||||
|
needs_saving,
|
||||||
|
totp_limit_reached,
|
||||||
|
tfa_limit_reached,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct TfaReturnValue {
|
||||||
|
result: bool,
|
||||||
|
needs_saving: bool,
|
||||||
|
totp_limit_reached: bool,
|
||||||
|
tfa_limit_reached: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DEBUG HELPER: Get the current TOTP value for a given TOTP URI.
|
/// DEBUG HELPER: Get the current TOTP value for a given TOTP URI.
|
||||||
@ -360,7 +424,7 @@ mod export {
|
|||||||
let this: &Tfa = (&raw_this).try_into()?;
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
methods::add_tfa_entry(
|
methods::add_tfa_entry(
|
||||||
&mut this.inner.lock().unwrap(),
|
&mut this.inner.lock().unwrap(),
|
||||||
UserAccess::new(&raw_this)?,
|
&UserAccess::new(&raw_this)?,
|
||||||
userid,
|
userid,
|
||||||
description,
|
description,
|
||||||
totp,
|
totp,
|
||||||
@ -421,6 +485,66 @@ mod export {
|
|||||||
Err(methods::EntryNotFound) => bail!("no such entry"),
|
Err(methods::EntryNotFound) => bail!("no such entry"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn api_unlock_tfa(#[raw] raw_this: Value, userid: &str) -> Result<bool, Error> {
|
||||||
|
let this: &Tfa = (&raw_this).try_into()?;
|
||||||
|
Ok(methods::unlock_and_reset_tfa(
|
||||||
|
&mut this.inner.lock().unwrap(),
|
||||||
|
&UserAccess::new(&raw_this)?,
|
||||||
|
userid,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct TfaLockStatus {
|
||||||
|
/// Once a user runs into a TOTP limit they get locked out of TOTP until they successfully use
|
||||||
|
/// a recovery key.
|
||||||
|
#[serde(skip_serializing_if = "bool_is_false", default)]
|
||||||
|
totp_locked: bool,
|
||||||
|
|
||||||
|
/// If a user hits too many 2nd factor failures, they get completely blocked for a while.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
|
#[serde(deserialize_with = "filter_expired_timestamp")]
|
||||||
|
tfa_locked_until: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&proxmox_tfa::api::TfaUserData> for TfaLockStatus {
|
||||||
|
fn from(data: &proxmox_tfa::api::TfaUserData) -> Self {
|
||||||
|
Self {
|
||||||
|
totp_locked: data.totp_locked,
|
||||||
|
tfa_locked_until: data.tfa_locked_until,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_is_false(b: &bool) -> bool {
|
||||||
|
!*b
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export]
|
||||||
|
fn tfa_lock_status(
|
||||||
|
#[try_from_ref] this: &Tfa,
|
||||||
|
userid: Option<&str>,
|
||||||
|
) -> Result<Option<perlmod::Value>, Error> {
|
||||||
|
let this = this.inner.lock().unwrap();
|
||||||
|
if let Some(userid) = userid {
|
||||||
|
if let Some(user) = this.users.get(userid) {
|
||||||
|
Ok(Some(perlmod::to_value(&TfaLockStatus::from(user))?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Some(perlmod::to_value(
|
||||||
|
&HashMap::<String, TfaLockStatus>::from_iter(
|
||||||
|
this.users
|
||||||
|
.iter()
|
||||||
|
.map(|(uid, data)| (uid.clone(), TfaLockStatus::from(data))),
|
||||||
|
),
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version 1 format of `/etc/pve/priv/tfa.cfg`
|
/// Version 1 format of `/etc/pve/priv/tfa.cfg`
|
||||||
@ -514,6 +638,7 @@ fn decode_old_entry(ty: &[u8], data: &[u8], user: &str) -> Result<TfaUserData, E
|
|||||||
b"oath" => user_data.totp.extend(
|
b"oath" => user_data.totp.extend(
|
||||||
decode_old_oath_entry(value, user)?
|
decode_old_oath_entry(value, user)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.map(proxmox_tfa::api::TotpEntry::new)
|
||||||
.map(move |entry| proxmox_tfa::api::TfaEntry::from_parts(info.clone(), entry)),
|
.map(move |entry| proxmox_tfa::api::TfaEntry::from_parts(info.clone(), entry)),
|
||||||
),
|
),
|
||||||
b"yubico" => user_data.yubico.extend(
|
b"yubico" => user_data.yubico.extend(
|
||||||
@ -841,9 +966,7 @@ fn challenge_data_path(userid: &str, debug: bool) -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
||||||
type Data = UserChallengeData;
|
fn open(&self, userid: &str) -> Result<Box<dyn UserChallengeAccess>, Error> {
|
||||||
|
|
||||||
fn open(&self, userid: &str) -> Result<UserChallengeData, Error> {
|
|
||||||
if self.is_debug() {
|
if self.is_debug() {
|
||||||
mkdir("./local-tfa-challenges", 0o700)?;
|
mkdir("./local-tfa-challenges", 0o700)?;
|
||||||
} else {
|
} else {
|
||||||
@ -886,15 +1009,15 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(UserChallengeData {
|
Ok(Box::new(UserChallengeData {
|
||||||
inner,
|
inner,
|
||||||
path,
|
path,
|
||||||
lock: file,
|
lock: file,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `open` without creating the file if it doesn't exist, to finish WA authentications.
|
/// `open` without creating the file if it doesn't exist, to finish WA authentications.
|
||||||
fn open_no_create(&self, userid: &str) -> Result<Option<UserChallengeData>, Error> {
|
fn open_no_create(&self, userid: &str) -> Result<Option<Box<dyn UserChallengeAccess>>, Error> {
|
||||||
let path = challenge_data_path(userid, self.is_debug());
|
let path = challenge_data_path(userid, self.is_debug());
|
||||||
|
|
||||||
let mut file = match std::fs::OpenOptions::new()
|
let mut file = match std::fs::OpenOptions::new()
|
||||||
@ -915,11 +1038,11 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
|||||||
format_err!("failed to read challenge data for user {}: {}", userid, err)
|
format_err!("failed to read challenge data for user {}: {}", userid, err)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Some(UserChallengeData {
|
Ok(Some(Box::new(UserChallengeData {
|
||||||
inner,
|
inner,
|
||||||
path,
|
path,
|
||||||
lock: file,
|
lock: file,
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, userid: &str) -> Result<bool, Error> {
|
fn remove(&self, userid: &str) -> Result<bool, Error> {
|
||||||
@ -930,6 +1053,10 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
|||||||
Err(err) => Err(err.into()),
|
Err(err) => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enable_lockout(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container of `TfaUserChallenges` with the corresponding file lock guard.
|
/// Container of `TfaUserChallenges` with the corresponding file lock guard.
|
||||||
@ -947,7 +1074,7 @@ impl proxmox_tfa::api::UserChallengeAccess for UserChallengeData {
|
|||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(self) -> Result<(), Error> {
|
fn save(&mut self) -> Result<(), Error> {
|
||||||
UserChallengeData::save(self)
|
UserChallengeData::save(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -992,7 +1119,7 @@ impl UserChallengeData {
|
|||||||
///
|
///
|
||||||
/// This currently consumes selfe as we never perform more than 1 insertion/removal, and this
|
/// This currently consumes selfe as we never perform more than 1 insertion/removal, and this
|
||||||
/// way also unlocks early.
|
/// way also unlocks early.
|
||||||
fn save(mut self) -> Result<(), Error> {
|
fn save(&mut self) -> Result<(), Error> {
|
||||||
self.rewind()?;
|
self.rewind()?;
|
||||||
|
|
||||||
serde_json::to_writer(&mut &self.lock, &self.inner).map_err(|err| {
|
serde_json::to_writer(&mut &self.lock, &self.inner).map_err(|err| {
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test: Proxmox/Lib/PVE.pm
|
||||||
@echo "-- running pve-rs tests --"
|
@echo "-- running pve-rs tests --"
|
||||||
./resource_scheduling.pl
|
perl -I. -I.. -I../.. ./resource_scheduling.pl
|
||||||
|
|
||||||
|
# The test stub, we don't know where to look for the library from in here!
|
||||||
|
Proxmox/Lib/PVE.pm:
|
||||||
|
@echo "run 'make' in the pve-rs/ dir first"
|
||||||
|
@exit 1
|
||||||
|
@ -5,35 +5,73 @@ use warnings;
|
|||||||
|
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# FIXME ensure that the just built library is loaded rather than the installed one and add a test
|
|
||||||
# target to pve-rs/Makefile afterwards. Issue is that the loader looks into an $PATH/auto directory,
|
|
||||||
# so it's not enough to use lib qw(../target/release)
|
|
||||||
# Also might be a good idea to test for existence of the files to avoid surprises if the directory
|
|
||||||
# structure changes in the future.
|
|
||||||
#use lib qw(..);
|
|
||||||
#use lib qw(../target/release);
|
|
||||||
use PVE::RS::ResourceScheduling::Static;
|
use PVE::RS::ResourceScheduling::Static;
|
||||||
|
|
||||||
my $static = PVE::RS::ResourceScheduling::Static->new();
|
sub test_basic {
|
||||||
is(scalar($static->list_nodes()->@*), 0, 'node list empty');
|
my $static = PVE::RS::ResourceScheduling::Static->new();
|
||||||
$static->add_node("A", 10, 100_000_000_000);
|
is(scalar($static->list_nodes()->@*), 0, 'node list empty');
|
||||||
is(scalar($static->list_nodes()->@*), 1, '1 node added');
|
$static->add_node("A", 10, 100_000_000_000);
|
||||||
$static->add_node("B", 20, 200_000_000_000);
|
is(scalar($static->list_nodes()->@*), 1, '1 node added');
|
||||||
is(scalar($static->list_nodes()->@*), 2, '2nd node');
|
$static->add_node("B", 20, 200_000_000_000);
|
||||||
$static->add_node("C", 30, 300_000_000_000);
|
is(scalar($static->list_nodes()->@*), 2, '2nd node');
|
||||||
is(scalar($static->list_nodes()->@*), 3, '3rd node');
|
$static->add_node("C", 30, 300_000_000_000);
|
||||||
$static->remove_node("C");
|
is(scalar($static->list_nodes()->@*), 3, '3rd node');
|
||||||
is(scalar($static->list_nodes()->@*), 2, '3rd removed should be 2');
|
$static->remove_node("C");
|
||||||
ok($static->contains_node("A"), 'should contain a node A');
|
is(scalar($static->list_nodes()->@*), 2, '3rd removed should be 2');
|
||||||
ok($static->contains_node("B"), 'should contain a node B');
|
ok($static->contains_node("A"), 'should contain a node A');
|
||||||
ok(!$static->contains_node("C"), 'should not contain a node C');
|
ok($static->contains_node("B"), 'should contain a node B');
|
||||||
|
ok(!$static->contains_node("C"), 'should not contain a node C');
|
||||||
|
}
|
||||||
|
|
||||||
my $service = {
|
sub test_balance {
|
||||||
maxcpu => 4,
|
my $static = PVE::RS::ResourceScheduling::Static->new();
|
||||||
maxmem => 20_000_000_000,
|
$static->add_node("A", 10, 100_000_000_000);
|
||||||
};
|
$static->add_node("B", 20, 200_000_000_000);
|
||||||
|
|
||||||
|
my $service = {
|
||||||
|
maxcpu => 4,
|
||||||
|
maxmem => 20_000_000_000,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (my $i = 0; $i < 15; $i++) {
|
||||||
|
my $score_list = $static->score_nodes_to_start_service($service);
|
||||||
|
|
||||||
|
# imitate HA manager
|
||||||
|
my $scores = { map { $_->[0] => -$_->[1] } $score_list->@* };
|
||||||
|
my @nodes = sort {
|
||||||
|
$scores->{$a} <=> $scores->{$b} || $a cmp $b
|
||||||
|
} keys $scores->%*;
|
||||||
|
|
||||||
|
if ($i % 3 == 2) {
|
||||||
|
is($nodes[0], "A", 'first should be A');
|
||||||
|
is($nodes[1], "B", 'second should be A');
|
||||||
|
} else {
|
||||||
|
is($nodes[0], "B", 'first should be B');
|
||||||
|
is($nodes[1], "A", 'second should be A');
|
||||||
|
}
|
||||||
|
|
||||||
|
$static->add_service_usage_to_node($nodes[0], $service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub test_overcommitted {
|
||||||
|
my $static = PVE::RS::ResourceScheduling::Static->new();
|
||||||
|
$static->add_node("A", 4, 4_102_062_080);
|
||||||
|
$static->add_node("B", 4, 4_102_062_080);
|
||||||
|
$static->add_node("C", 4, 4_102_053_888);
|
||||||
|
$static->add_node("D", 4, 4_102_053_888);
|
||||||
|
|
||||||
|
my $service = {
|
||||||
|
maxcpu => 1,
|
||||||
|
maxmem => 536_870_912,
|
||||||
|
};
|
||||||
|
|
||||||
|
$static->add_service_usage_to_node("A", $service);
|
||||||
|
$static->add_service_usage_to_node("A", $service);
|
||||||
|
$static->add_service_usage_to_node("A", $service);
|
||||||
|
$static->add_service_usage_to_node("B", $service);
|
||||||
|
$static->add_service_usage_to_node("A", $service);
|
||||||
|
|
||||||
for (my $i = 0; $i < 15; $i++) {
|
|
||||||
my $score_list = $static->score_nodes_to_start_service($service);
|
my $score_list = $static->score_nodes_to_start_service($service);
|
||||||
|
|
||||||
# imitate HA manager
|
# imitate HA manager
|
||||||
@ -42,15 +80,65 @@ for (my $i = 0; $i < 15; $i++) {
|
|||||||
$scores->{$a} <=> $scores->{$b} || $a cmp $b
|
$scores->{$a} <=> $scores->{$b} || $a cmp $b
|
||||||
} keys $scores->%*;
|
} keys $scores->%*;
|
||||||
|
|
||||||
if ($i % 3 == 2) {
|
is($nodes[0], "C", 'first should be C');
|
||||||
is($nodes[0], "A", 'first should be A');
|
is($nodes[1], "D", 'second should be D');
|
||||||
is($nodes[1], "B", 'second should be A');
|
is($nodes[2], "B", 'third should be B');
|
||||||
} else {
|
is($nodes[3], "A", 'fourth should be A');
|
||||||
is($nodes[0], "B", 'first should be B');
|
|
||||||
is($nodes[1], "A", 'second should be A');
|
|
||||||
}
|
|
||||||
|
|
||||||
$static->add_service_usage_to_node($nodes[0], $service);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub test_balance_small_memory_difference {
|
||||||
|
my ($with_start_load) = @_;
|
||||||
|
|
||||||
|
my $static = PVE::RS::ResourceScheduling::Static->new();
|
||||||
|
# Memory is different to avoid flaky results with what would otherwise be ties.
|
||||||
|
$static->add_node("A", 8, 10_000_000_000);
|
||||||
|
$static->add_node("B", 4, 9_000_000_000);
|
||||||
|
$static->add_node("C", 4, 8_000_000_000);
|
||||||
|
|
||||||
|
if ($with_start_load) {
|
||||||
|
$static->add_service_usage_to_node("A", { maxcpu => 4, maxmem => 1_000_000_000 });
|
||||||
|
$static->add_service_usage_to_node("B", { maxcpu => 2, maxmem => 1_000_000_000 });
|
||||||
|
$static->add_service_usage_to_node("C", { maxcpu => 2, maxmem => 1_000_000_000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
my $service = {
|
||||||
|
maxcpu => 3,
|
||||||
|
maxmem => 16_000_000,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (my $i = 0; $i < 20; $i++) {
|
||||||
|
my $score_list = $static->score_nodes_to_start_service($service);
|
||||||
|
|
||||||
|
# imitate HA manager
|
||||||
|
my $scores = { map { $_->[0] => -$_->[1] } $score_list->@* };
|
||||||
|
my @nodes = sort {
|
||||||
|
$scores->{$a} <=> $scores->{$b} || $a cmp $b
|
||||||
|
} keys $scores->%*;
|
||||||
|
|
||||||
|
if ($i % 4 <= 1) {
|
||||||
|
is($nodes[0], "A", 'first should be A');
|
||||||
|
is($nodes[1], "B", 'second should be B');
|
||||||
|
is($nodes[2], "C", 'third should be C');
|
||||||
|
} elsif ($i % 4 == 2) {
|
||||||
|
is($nodes[0], "B", 'first should be B');
|
||||||
|
is($nodes[1], "C", 'second should be C');
|
||||||
|
is($nodes[2], "A", 'third should be A');
|
||||||
|
} elsif ($i % 4 == 3) {
|
||||||
|
is($nodes[0], "C", 'first should be C');
|
||||||
|
is($nodes[1], "A", 'second should be A');
|
||||||
|
is($nodes[2], "B", 'third should be B');
|
||||||
|
} else {
|
||||||
|
die "internal error, got $i % 4 == " . ($i % 4) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$static->add_service_usage_to_node($nodes[0], $service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_basic();
|
||||||
|
test_balance();
|
||||||
|
test_overcommitted();
|
||||||
|
test_balance_small_memory_difference(1);
|
||||||
|
test_balance_small_memory_difference(0);
|
||||||
|
|
||||||
done_testing();
|
done_testing();
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env perl
|
|
||||||
|
|
||||||
# Create a perl package given a product and package name.
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use File::Path qw(make_path);
|
|
||||||
|
|
||||||
my $product = shift @ARGV or die "missing product name (PVE, PMG or Common)\n";
|
|
||||||
|
|
||||||
die "missing package name\n" if !@ARGV;
|
|
||||||
|
|
||||||
for my $package (@ARGV) {
|
|
||||||
my $path = ($package =~ s@::@/@gr) . ".pm";
|
|
||||||
|
|
||||||
print "Generating $path\n";
|
|
||||||
|
|
||||||
$path =~ m@^(.*)/[^/]+@;
|
|
||||||
make_path($1, { mode => 0755 });
|
|
||||||
|
|
||||||
open(my $fh, '>', $path) or die "failed to open '$path' for writing: $!\n";
|
|
||||||
|
|
||||||
print {$fh} <<"EOF";
|
|
||||||
package $package;
|
|
||||||
use base 'Proxmox::Lib::$product';
|
|
||||||
BEGIN { __PACKAGE__->bootstrap(); }
|
|
||||||
1;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
close($fh);
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user