forked from Proxmox/proxmox-perl-rs
Compare commits
116 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ae27b307b8 | ||
|
dfd8a2e321 | ||
|
a3e466af88 | ||
|
cdc792005e | ||
|
ea4d87816b | ||
|
9a91594ee6 | ||
|
885830935c | ||
|
b3b8b375c2 | ||
|
6789b14986 | ||
|
89d9debadb | ||
|
5c994bf942 | ||
|
9eda29d688 | ||
|
83427e9204 | ||
|
61b2f69a45 | ||
|
c873ac57d5 | ||
|
7e3ea35595 | ||
|
a34b31054d | ||
|
da068b1a47 | ||
|
cd0e7b8cd2 | ||
|
0b6800b0bd | ||
|
dc02255bdc | ||
|
7ac7fa5b00 | ||
|
627a95bf89 | ||
|
d0633ac98e | ||
|
45a1af8ad2 | ||
|
6bed9c40bc | ||
|
2860777e61 | ||
|
4e6598ef85 | ||
|
27a7f2e252 | ||
|
199be72401 | ||
|
d6df8340c5 | ||
|
427fdb13c0 | ||
|
a5330e34d2 | ||
|
c57e1868e7 | ||
|
ec95bb1c53 | ||
|
fb5f1be6dc | ||
|
237b276028 | ||
|
16c41f1a91 | ||
|
86706cc049 | ||
|
62fc43fea9 | ||
|
6a31f73fa3 | ||
|
9525623c19 | ||
|
089e555d51 | ||
|
b9185327f4 | ||
|
ce550d06e2 | ||
|
5ac44c9fbb | ||
|
4c54abcea8 | ||
|
61ab181b01 | ||
|
036236c278 | ||
|
36fbb76145 | ||
|
b905cfd03d | ||
|
7f8cb0c5c3 | ||
|
29602a4b01 | ||
|
bfc7f2c518 | ||
|
14a3de9826 | ||
|
a04d26b0d2 | ||
|
c8d4db7836 | ||
|
1c2ff27e75 | ||
|
e3bc763de4 | ||
|
e9c2ba606d | ||
|
4c6cc7e241 | ||
|
50f372fe7e | ||
|
8d031134e1 | ||
|
e52b4ea877 | ||
|
8ff4471ee6 | ||
|
76b63ed6a8 | ||
|
2be21ff9fa | ||
|
47b7ebbc96 | ||
|
af7ff77ac7 | ||
|
d5ff7165a2 | ||
|
703cfbd212 | ||
|
69d2eb953d | ||
|
de59ffe4ec | ||
|
178196e1ae | ||
|
a5ee03ed0f | ||
|
79f339d136 | ||
|
6b5dbc3238 | ||
|
a73ba69716 | ||
|
4b64b63ff7 | ||
|
350cdd6b59 | ||
|
b9c4756445 | ||
|
cd8984a954 | ||
|
225b640f1f | ||
|
8759447585 | ||
|
e2c950bf4c | ||
|
0be7076578 | ||
|
470849f974 | ||
|
5c6a27da1d | ||
|
06f325fd9d | ||
|
3df4aecac0 | ||
|
fdcdd326c3 | ||
|
aed1657598 | ||
|
39a7399c2c | ||
|
7bd8036ff0 | ||
|
e1f6379b02 | ||
|
0d530835cb | ||
|
d0cab6371a | ||
|
15e7531f3c | ||
|
3037864e4d | ||
|
590af894ef | ||
|
10472bc265 | ||
|
a4610c6a0f | ||
|
f7bb45a38b | ||
|
181b19e2ef | ||
|
e3d4bb03c9 | ||
|
a53d4737d3 | ||
|
6b92c01349 | ||
|
0d049201e9 | ||
|
f7a9ddfdfd | ||
|
c0bc3436ee | ||
|
6beb0ffa6b | ||
|
4917bd4ead | ||
|
3255c3b59c | ||
|
1b499b7611 | ||
|
f863004159 | ||
|
34a0068618 |
45
Makefile
45
Makefile
@ -1,45 +0,0 @@
|
||||
CARGO ?= cargo
|
||||
|
||||
ifeq ($(BUILD_MODE), release)
|
||||
CARGO_BUILD_ARGS += --release
|
||||
else
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
ifeq ($(BUILD_TARGET), pve)
|
||||
$(MAKE) pve
|
||||
else ifeq ($(BUILD_TARGET), pmg)
|
||||
$(MAKE) pmg
|
||||
else
|
||||
@echo "Run one of"
|
||||
@echo " - make pve"
|
||||
@echo " - make pmg"
|
||||
endif
|
||||
|
||||
build:
|
||||
rm -rf build
|
||||
mkdir build
|
||||
echo system >build/rust-toolchain
|
||||
cp -a ./Cargo.toml ./build
|
||||
cp -a ./common ./build
|
||||
cp -a ./pve-rs ./build
|
||||
cp -a ./pmg-rs ./build
|
||||
# Replace the symlinks with copies of the common code in pve/pmg:
|
||||
cd build; for i in pve pmg; do \
|
||||
rm ./$$i-rs/common ; \
|
||||
mkdir ./$$i-rs/common ; \
|
||||
cp -R ./common/src ./$$i-rs/common/src ; \
|
||||
done
|
||||
# So the common packages end up in ./build, rather than ./build/common
|
||||
mv ./build/common/pkg ./build/common-pkg
|
||||
# Copy the workspace root into the sources
|
||||
mkdir build/pve-rs/.workspace
|
||||
cp -t build/pve-rs/.workspace Cargo.toml
|
||||
sed -i -e '/\[package\]/a\workspace = ".workspace"' build/pve-rs/Cargo.toml
|
||||
# Clear the member array and replace it with ".."
|
||||
sed -i -e '/^members = \[/,/^]$$/d' build/pve-rs/.workspace/Cargo.toml
|
||||
sed -i -e '/^\[workspace\]/a\members = [ ".." ]' build/pve-rs/.workspace/Cargo.toml
|
||||
# Copy the cargo config
|
||||
mkdir build/pve-rs/.cargo
|
||||
cp -t build/pve-rs/.cargo .cargo/config
|
@ -20,18 +20,24 @@ PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \
|
||||
--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) \
|
||||
PERLMOD_PACKAGES := \
|
||||
Proxmox::RS::APT::Repositories \
|
||||
Proxmox::RS::CalendarEvent \
|
||||
Proxmox::RS::Notify \
|
||||
Proxmox::RS::SharedCache \
|
||||
Proxmox::RS::Subscription
|
||||
|
||||
all: Proxmox/RS/CalendarEvent.pm
|
||||
PERLMOD_PACKAGE_FILES := $(addsuffix .pm,$(subst ::,/,$(PERLMOD_PACKAGES)))
|
||||
|
||||
Proxmox/RS: $(PERLMOD_PACKAGE_FILES)
|
||||
$(PERLMOD_PACKAGE_FILES) &:
|
||||
$(PERLMOD_GENPACKAGE) $(PERLMOD_PACKAGES)
|
||||
|
||||
all: Proxmox/RS
|
||||
true
|
||||
|
||||
.PHONY: install
|
||||
install: Proxmox/RS/CalendarEvent.pm
|
||||
install: Proxmox/RS
|
||||
install -d -m755 $(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)'/{}' ';'
|
||||
@ -67,3 +73,4 @@ upload: $(DEB)
|
||||
clean:
|
||||
rm -f *.deb *.dsc *.tar.* *.build *.buildinfo *.changes
|
||||
rm -rf $(PACKAGE)-[0-9]*/
|
||||
rm -rf Proxmox/RS
|
||||
|
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,33 @@
|
||||
libproxmox-rs-perl (0.3.4) bookworm; urgency=medium
|
||||
|
||||
* add bindings for proxmox-shared-cache crate
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Fri, 09 Aug 2024 14:21:41 +0200
|
||||
|
||||
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
|
||||
|
||||
* update to proxmox-subscription 0.3 / proxmox-http 0.7
|
||||
|
@ -1 +0,0 @@
|
||||
12
|
@ -1,11 +1,9 @@
|
||||
Source: libproxmox-rs-perl
|
||||
Section: perl
|
||||
Priority: optional
|
||||
Build-Depends:
|
||||
debhelper (>= 12),
|
||||
perlmod-bin,
|
||||
Build-Depends: debhelper-compat (= 13), perlmod-bin,
|
||||
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-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
||||
Homepage: https://www.proxmox.com
|
||||
@ -15,15 +13,12 @@ Package: libproxmox-rs-perl
|
||||
Architecture: any
|
||||
# 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
|
||||
Depends:
|
||||
${misc:Depends},
|
||||
${perl:Depends},
|
||||
${shlibs:Depends},
|
||||
libpve-rs-perl (>= 0.7.2) | libpmg-rs-perl (>= 0.6.2),
|
||||
Breaks:
|
||||
libpve-rs-perl (<< 0.7.2),
|
||||
libpmg-rs-perl (<< 0.6.2),
|
||||
Replaces: libpve-rs-perl (<< 0.6.0)
|
||||
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.
|
||||
Depends: libpve-rs-perl (>= 0.8.10) | libpmg-rs-perl (>= 0.7.6),
|
||||
${misc:Depends},
|
||||
${perl:Depends},
|
||||
${shlibs:Depends},
|
||||
Breaks: libpmg-rs-perl (<< 0.6.2), libpve-rs-perl (<< 0.7.2),
|
||||
Replaces: libpve-rs-perl (<< 0.6.0),
|
||||
Description: PVE/PMG common perl parts for Rust perlmod bindings
|
||||
Contains the perl side of modules provided by the libraries of both
|
||||
libpve-rs-perl and libpmg-rs-perl, loading whichever is available.
|
||||
|
@ -1,62 +1,18 @@
|
||||
#[perlmod::package(name = "Proxmox::RS::APT::Repositories")]
|
||||
pub mod export {
|
||||
use std::convert::TryInto;
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use anyhow::Error;
|
||||
|
||||
use proxmox_apt::repositories::{
|
||||
APTRepositoryFile, APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo,
|
||||
APTStandardRepository,
|
||||
use proxmox_apt_api_types::{
|
||||
APTChangeRepositoryOptions, APTGetChangelogOptions, APTRepositoriesResult,
|
||||
APTRepositoryHandle, APTUpdateInfo, APTUpdateOptions,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Result for the repositories() function
|
||||
pub struct RepositoriesResult {
|
||||
/// Successfully parsed files.
|
||||
pub files: Vec<APTRepositoryFile>,
|
||||
|
||||
/// Errors for files that could not be parsed or read.
|
||||
pub errors: Vec<APTRepositoryFileError>,
|
||||
|
||||
/// Common digest for successfully parsed files.
|
||||
pub digest: String,
|
||||
|
||||
/// Additional information/warnings about repositories.
|
||||
pub infos: Vec<APTRepositoryInfo>,
|
||||
|
||||
/// Standard repositories and their configuration status.
|
||||
pub standard_repos: Vec<APTStandardRepository>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// For changing an existing repository.
|
||||
pub struct ChangeProperties {
|
||||
/// Whether the repository should be enabled or not.
|
||||
pub enabled: Option<bool>,
|
||||
}
|
||||
use proxmox_config_digest::ConfigDigest;
|
||||
|
||||
/// Get information about configured repositories and standard repositories for `product`.
|
||||
#[export]
|
||||
pub fn repositories(product: &str) -> Result<RepositoriesResult, Error> {
|
||||
let (files, errors, digest) = proxmox_apt::repositories::repositories()?;
|
||||
let digest = hex::encode(&digest);
|
||||
|
||||
let suite = proxmox_apt::repositories::get_current_release_codename()?;
|
||||
|
||||
let infos = proxmox_apt::repositories::check_repositories(&files, suite);
|
||||
let standard_repos =
|
||||
proxmox_apt::repositories::standard_repositories(&files, product, suite);
|
||||
|
||||
Ok(RepositoriesResult {
|
||||
files,
|
||||
errors,
|
||||
digest,
|
||||
infos,
|
||||
standard_repos,
|
||||
})
|
||||
pub fn repositories(product: &str) -> Result<APTRepositoriesResult, Error> {
|
||||
proxmox_apt::list_repositories(product)
|
||||
}
|
||||
|
||||
/// Add the repository identified by the `handle` and `product`.
|
||||
@ -64,65 +20,12 @@ pub mod export {
|
||||
///
|
||||
/// The `digest` parameter asserts that the configuration has not been modified.
|
||||
#[export]
|
||||
pub fn add_repository(handle: &str, product: &str, digest: Option<&str>) -> Result<(), Error> {
|
||||
let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
|
||||
|
||||
let handle: APTRepositoryHandle = handle.try_into()?;
|
||||
let suite = proxmox_apt::repositories::get_current_release_codename()?;
|
||||
|
||||
if let Some(digest) = digest {
|
||||
let expected_digest = hex::decode(digest)?;
|
||||
if expected_digest != current_digest {
|
||||
bail!("detected modified configuration - file changed by other user? Try again.");
|
||||
}
|
||||
}
|
||||
|
||||
// check if it's already configured first
|
||||
for file in files.iter_mut() {
|
||||
for repo in file.repositories.iter_mut() {
|
||||
if repo.is_referenced_repository(handle, product, &suite.to_string()) {
|
||||
if repo.enabled {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
repo.set_enabled(true);
|
||||
file.write()?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (repo, path) =
|
||||
proxmox_apt::repositories::get_standard_repository(handle, product, suite);
|
||||
|
||||
if let Some(error) = errors.iter().find(|error| error.path == path) {
|
||||
bail!(
|
||||
"unable to parse existing file {} - {}",
|
||||
error.path,
|
||||
error.error,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(file) = files
|
||||
.iter_mut()
|
||||
.find(|file| file.path.as_ref() == Some(&path))
|
||||
{
|
||||
file.repositories.push(repo);
|
||||
|
||||
file.write()?;
|
||||
} else {
|
||||
let mut file = match APTRepositoryFile::new(&path)? {
|
||||
Some(file) => file,
|
||||
None => bail!("invalid path - {}", path),
|
||||
};
|
||||
|
||||
file.repositories.push(repo);
|
||||
|
||||
file.write()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
pub fn add_repository(
|
||||
handle: APTRepositoryHandle,
|
||||
product: &str,
|
||||
digest: Option<ConfigDigest>,
|
||||
) -> Result<(), Error> {
|
||||
proxmox_apt::add_repository_handle(product, handle, digest)
|
||||
}
|
||||
|
||||
/// Change the properties of the specified repository.
|
||||
@ -132,39 +35,55 @@ pub mod export {
|
||||
pub fn change_repository(
|
||||
path: &str,
|
||||
index: usize,
|
||||
options: ChangeProperties,
|
||||
digest: Option<&str>,
|
||||
options: APTChangeRepositoryOptions,
|
||||
digest: Option<ConfigDigest>,
|
||||
) -> Result<(), Error> {
|
||||
let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
|
||||
proxmox_apt::change_repository(path, index, &options, digest)
|
||||
}
|
||||
|
||||
if let Some(digest) = digest {
|
||||
let expected_digest = hex::decode(digest)?;
|
||||
if expected_digest != current_digest {
|
||||
bail!("detected modified configuration - file changed by other user? Try again.");
|
||||
}
|
||||
}
|
||||
/// Retrieve the changelog of the specified package.
|
||||
#[export]
|
||||
pub fn get_changelog(options: APTGetChangelogOptions) -> Result<String, Error> {
|
||||
proxmox_apt::get_changelog(&options)
|
||||
}
|
||||
|
||||
if let Some(error) = errors.iter().find(|error| error.path == path) {
|
||||
bail!("unable to parse file {} - {}", error.path, error.error);
|
||||
}
|
||||
/// List available APT updates
|
||||
///
|
||||
/// Automatically updates an expired package cache.
|
||||
#[export]
|
||||
pub fn list_available_apt_update(apt_state_file: &str) -> Result<Vec<APTUpdateInfo>, Error> {
|
||||
proxmox_apt::list_available_apt_update(apt_state_file)
|
||||
}
|
||||
|
||||
if let Some(file) = files
|
||||
.iter_mut()
|
||||
.find(|file| file.path.as_ref() == Some(&path.to_string()))
|
||||
{
|
||||
if let Some(repo) = file.repositories.get_mut(index) {
|
||||
if let Some(enabled) = options.enabled {
|
||||
repo.set_enabled(enabled);
|
||||
}
|
||||
/// Update the APT database
|
||||
///
|
||||
/// You should update the APT proxy configuration before running this.
|
||||
#[export]
|
||||
pub fn update_database(apt_state_file: &str, options: APTUpdateOptions) -> Result<(), Error> {
|
||||
proxmox_apt::update_database(
|
||||
apt_state_file,
|
||||
&options,
|
||||
|updates: &[&APTUpdateInfo]| -> Result<(), Error> {
|
||||
// fixme: howto send notifgications?
|
||||
crate::send_updates_available(updates)?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
file.write()?;
|
||||
} else {
|
||||
bail!("invalid index - {}", index);
|
||||
}
|
||||
} else {
|
||||
bail!("invalid path - {}", path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
/// Get package information for a list of important product packages.
|
||||
#[export]
|
||||
pub fn get_package_versions(
|
||||
product_virtual_package: &str,
|
||||
api_server_package: &str,
|
||||
running_api_server_version: &str,
|
||||
package_list: Vec<&str>,
|
||||
) -> Result<Vec<APTUpdateInfo>, Error> {
|
||||
proxmox_apt::get_package_versions(
|
||||
product_virtual_package,
|
||||
api_server_package,
|
||||
running_api_server_version,
|
||||
&package_list,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
use anyhow::Error;
|
||||
|
||||
/// Initialize logging. Should only be called once
|
||||
pub fn init() {
|
||||
if let Err(e) = env_logger::try_init() {
|
||||
eprintln!("could not set up env_logger: {e}");
|
||||
pub fn init(env_var_name: &str, default_log_level: &str) {
|
||||
if let Err(e) = default_log_level
|
||||
.parse()
|
||||
.map_err(Error::from)
|
||||
.and_then(|default_log_level| proxmox_log::init_logger(env_var_name, default_log_level))
|
||||
{
|
||||
eprintln!("could not set up env_logger: {e:?}");
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
pub mod apt;
|
||||
mod calendar_event;
|
||||
pub mod logger;
|
||||
pub mod notify;
|
||||
pub mod shared_cache;
|
||||
mod subscription;
|
||||
|
506
common/src/notify.rs
Normal file
506
common/src/notify.rs
Normal file
@ -0,0 +1,506 @@
|
||||
#[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,
|
||||
template_name: String,
|
||||
template_data: Option<JSONValue>,
|
||||
fields: Option<HashMap<String, String>>,
|
||||
) -> Result<(), HttpError> {
|
||||
let config = this.config.lock().unwrap();
|
||||
let notification = Notification::from_template(
|
||||
severity,
|
||||
template_name,
|
||||
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.unwrap_or_default(),
|
||||
mailto_user: mailto_user.unwrap_or_default(),
|
||||
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.unwrap_or_default(),
|
||||
mailto_user: mailto_user.unwrap_or_default(),
|
||||
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_severity.unwrap_or_default(),
|
||||
match_field: match_field.unwrap_or_default(),
|
||||
match_calendar: match_calendar.unwrap_or_default(),
|
||||
target: target.unwrap_or_default(),
|
||||
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}"))
|
||||
}
|
||||
}
|
68
common/src/shared_cache.rs
Normal file
68
common/src/shared_cache.rs
Normal file
@ -0,0 +1,68 @@
|
||||
#[perlmod::package(name = "Proxmox::RS::SharedCache")]
|
||||
mod export {
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Error;
|
||||
use nix::sys::stat::Mode;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value as JSONValue;
|
||||
|
||||
use perlmod::Value;
|
||||
|
||||
use proxmox_shared_cache::SharedCache;
|
||||
use proxmox_sys::fs::CreateOptions;
|
||||
|
||||
pub struct CacheWrapper(SharedCache);
|
||||
|
||||
perlmod::declare_magic!(Box<CacheWrapper> : &CacheWrapper as "Proxmox::RS::SharedCache");
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Params {
|
||||
path: String,
|
||||
owner: u32,
|
||||
group: u32,
|
||||
entry_mode: u32,
|
||||
keep_old: u32,
|
||||
}
|
||||
|
||||
#[export(raw_return)]
|
||||
fn new(#[raw] class: Value, params: Params) -> Result<Value, Error> {
|
||||
let options = CreateOptions::new()
|
||||
.owner(params.owner.into())
|
||||
.group(params.group.into())
|
||||
.perm(Mode::from_bits_truncate(params.entry_mode));
|
||||
|
||||
Ok(perlmod::instantiate_magic!(&class, MAGIC => Box::new(
|
||||
CacheWrapper (
|
||||
SharedCache::new(params.path, options, params.keep_old)?
|
||||
)
|
||||
)))
|
||||
}
|
||||
|
||||
#[export]
|
||||
fn set(
|
||||
#[try_from_ref] this: &CacheWrapper,
|
||||
value: JSONValue,
|
||||
lock_timeout: u64,
|
||||
) -> Result<(), Error> {
|
||||
this.0.set(&value, Duration::from_secs(lock_timeout))
|
||||
}
|
||||
|
||||
#[export]
|
||||
fn get(#[try_from_ref] this: &CacheWrapper) -> Result<Option<JSONValue>, Error> {
|
||||
this.0.get()
|
||||
}
|
||||
|
||||
#[export]
|
||||
fn get_last(
|
||||
#[try_from_ref] this: &CacheWrapper,
|
||||
number_of_old_entries: u32,
|
||||
) -> Result<Vec<JSONValue>, Error> {
|
||||
this.0.get_last(number_of_old_entries)
|
||||
}
|
||||
|
||||
#[export]
|
||||
fn delete(#[try_from_ref] this: &CacheWrapper, lock_timeout: u64) -> Result<(), Error> {
|
||||
this.0.delete(Duration::from_secs(lock_timeout))
|
||||
}
|
||||
}
|
@ -3,3 +3,6 @@
|
||||
directory = "/usr/share/cargo/registry"
|
||||
[source.crates-io]
|
||||
replace-with = "debian-packages"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "pmg-rs"
|
||||
version = "0.6.3"
|
||||
version = "0.7.6"
|
||||
description = "PMG parts which have been ported to rust"
|
||||
homepage = "https://www.proxmox.com"
|
||||
authors = ["Proxmox Support Team <support@proxmox.com>"]
|
||||
@ -8,34 +8,37 @@ edition = "2021"
|
||||
license = "AGPL-3"
|
||||
repository = "https://git.proxmox.com/?p=proxmox.git"
|
||||
|
||||
exclude = [
|
||||
"build",
|
||||
"debian",
|
||||
"PMG",
|
||||
]
|
||||
exclude = ["build", "debian", "PMG"]
|
||||
|
||||
[lib]
|
||||
crate-type = [ "cdylib" ]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
env_logger = "0.9"
|
||||
hex = "0.4"
|
||||
http = "0.2.7"
|
||||
libc = "0.2"
|
||||
log = "0.4.17"
|
||||
nix = "0.26"
|
||||
openssl = "0.10.40"
|
||||
serde = "1.0"
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1.37"
|
||||
url = "2"
|
||||
|
||||
perlmod = { version = "0.13", features = [ "exporter" ] }
|
||||
perlmod = { version = "0.13.4", features = ["exporter"] }
|
||||
|
||||
proxmox-acme-rs = { version = "0.4", features = ["client"] }
|
||||
proxmox-apt = "0.9.4"
|
||||
proxmox-http = { version = "0.8", features = ["client-sync", "client-trait"] }
|
||||
proxmox-subscription = "0.3"
|
||||
proxmox-sys = "0.4.2"
|
||||
proxmox-tfa = { version = "4", features = ["api"] }
|
||||
proxmox-time = "1.1.3"
|
||||
proxmox-acme = { version = "0.5", features = ["client", "api-types"] }
|
||||
proxmox-apt = { version = "0.11", features = ["cache"] }
|
||||
proxmox-apt-api-types = "1.0"
|
||||
proxmox-config-digest = "0.1"
|
||||
proxmox-http = { version = "0.9", features = ["client-sync", "client-trait"] }
|
||||
proxmox-http-error = "0.1.0"
|
||||
proxmox-log = "0.2"
|
||||
proxmox-notify = "0.4"
|
||||
proxmox-shared-cache = "0.1.0"
|
||||
proxmox-subscription = "0.4"
|
||||
proxmox-sys = "0.6"
|
||||
proxmox-tfa = { version = "5", features = ["api"] }
|
||||
proxmox-time = "2"
|
||||
|
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
|
@ -22,7 +22,8 @@ PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \
|
||||
--lib=pmg_rs \
|
||||
--lib-tag=proxmox \
|
||||
--lib-package=Proxmox::Lib::PMG \
|
||||
--lib-prefix=PMG
|
||||
--lib-prefix=PMG \
|
||||
--include-file=Fixup.pm
|
||||
|
||||
PERLMOD_PACKAGES := \
|
||||
PMG::RS::APT::Repositories \
|
||||
@ -31,6 +32,8 @@ PERLMOD_PACKAGES := \
|
||||
PMG::RS::OpenId \
|
||||
PMG::RS::TFA
|
||||
|
||||
PERLMOD_PACKAGE_FILES := $(addsuffix .pm,$(subst ::,/,$(PERLMOD_PACKAGES)))
|
||||
|
||||
ifeq ($(BUILD_MODE), release)
|
||||
CARGO_BUILD_ARGS += --release
|
||||
TARGET_DIR=release
|
||||
@ -41,12 +44,13 @@ endif
|
||||
all: PMG
|
||||
cargo build $(CARGO_BUILD_ARGS)
|
||||
|
||||
PMG: Proxmox/Lib/PMG.pm
|
||||
Proxmox/Lib/PMG.pm:
|
||||
Proxmox: Proxmox/Lib/PMG.pm
|
||||
PMG: $(PERLMOD_PACKAGE_FILES)
|
||||
Proxmox/Lib/PMG.pm $(PERLMOD_PACKAGE_FILES) &: Fixup.pm
|
||||
$(PERLMOD_GENPACKAGE) $(PERLMOD_PACKAGES)
|
||||
|
||||
.PHONY: install
|
||||
install: target/release/libpmg_rs.so Proxmox/Lib/PMG.pm PMG
|
||||
install: target/release/libpmg_rs.so Proxmox/Lib/PMG.pm $(PERLMOD_PACKAGE_FILES)
|
||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto
|
||||
install -m644 target/release/libpmg_rs.so $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto/libpmg_rs.so
|
||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
||||
@ -56,6 +60,7 @@ install: target/release/libpmg_rs.so Proxmox/Lib/PMG.pm PMG
|
||||
|
||||
distclean: clean
|
||||
clean:
|
||||
rm -rf PMG Proxmox
|
||||
cargo clean
|
||||
rm -f *.deb *.dsc *.tar.* *.build *.buildinfo *.changes Cargo.lock
|
||||
rm -rf $(PACKAGE)-[0-9]*/
|
||||
@ -71,11 +76,11 @@ upload: $(DEBS)
|
||||
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
||||
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
|
||||
$(BUILDDIR): src debian common/src Cargo.toml Makefile .cargo/config.toml
|
||||
rm -rf $(BUILDDIR) $(BUILDDIR).tmp
|
||||
mkdir $(BUILDDIR).tmp
|
||||
mkdir $(BUILDDIR).tmp/common
|
||||
cp -a -t $(BUILDDIR).tmp src debian Cargo.toml Makefile .cargo
|
||||
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)
|
||||
|
||||
|
@ -1,9 +1,58 @@
|
||||
libpmg-rs-perl (0.6.3) bullseye; urgency=medium
|
||||
libpmg-rs-perl (0.7.6) bookworm; urgency=medium
|
||||
|
||||
* build with proxmox-apt 0.9.4 to also detect repository with next suite as
|
||||
configured
|
||||
* upgrade to current rust crates or perlmod and proxmox-sys/tfa/apt/notify
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Fri, 09 Jun 2023 11:36:12 +0200
|
||||
* add bindings for proxmox-shared-cache crate
|
||||
|
||||
* use apt api method implementation from proxmox-apt crate
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Fri, 09 Aug 2024 14:19:56 +0200
|
||||
|
||||
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
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
12
|
@ -1,45 +1,58 @@
|
||||
Source: libpmg-rs-perl
|
||||
Section: perl
|
||||
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-hex-0.4+default-dev,
|
||||
librust-http-0.2+default-dev (>= 0.2.7-~~),
|
||||
librust-libc-0.2+default-dev,
|
||||
librust-log-0.4+default-dev (>= 0.4.17-~~),
|
||||
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+api-types-dev,
|
||||
librust-proxmox-acme-0.5+client-dev,
|
||||
librust-proxmox-acme-0.5+default-dev,
|
||||
librust-proxmox-apt-0.11+cache-dev,
|
||||
librust-proxmox-apt-0.11+default-dev,
|
||||
librust-proxmox-apt-api-types-1+default-dev,
|
||||
librust-proxmox-config-digest-0.1+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-log-0.2+default-dev,
|
||||
librust-proxmox-notify-0.4+default-dev,
|
||||
librust-proxmox-shared-cache-0.1+default-dev,
|
||||
librust-proxmox-subscription-0.4+default-dev,
|
||||
librust-proxmox-sys-0.6+default-dev,
|
||||
librust-proxmox-tfa-5+api-dev,
|
||||
librust-proxmox-tfa-5+default-dev,
|
||||
librust-proxmox-time-2+default-dev,
|
||||
librust-serde-1+default-dev,
|
||||
librust-serde-bytes-0.11+default-dev,
|
||||
librust-serde-json-1+default-dev,
|
||||
librust-tracing-0.1+default-dev (>= 0.1.37-~~),
|
||||
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>
|
||||
Build-Depends:
|
||||
debhelper (>= 12),
|
||||
dh-cargo (>= 24),
|
||||
perlmod-bin,
|
||||
cargo:native <!nocheck>,
|
||||
rustc:native <!nocheck>,
|
||||
libstd-rust-dev <!nocheck>,
|
||||
librust-anyhow-1+default-dev,
|
||||
librust-env-logger-0.9+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,
|
||||
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 (>= 0.9.4-~~),
|
||||
librust-proxmox-http-0.8+client-sync-dev,
|
||||
librust-proxmox-http-0.8+client-trait-dev,
|
||||
librust-proxmox-http-0.8+default-dev,
|
||||
librust-proxmox-subscription-0.3+default-dev,
|
||||
librust-proxmox-sys-0.4+default-dev (>= 0.4.2-~~),
|
||||
librust-proxmox-tfa-4+api-dev,
|
||||
librust-proxmox-tfa-4+default-dev,
|
||||
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,
|
||||
Standards-Version: 4.3.0
|
||||
Standards-Version: 4.6.1
|
||||
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
||||
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
||||
Homepage: https://www.proxmox.com
|
||||
|
||||
Package: libpmg-rs-perl
|
||||
Architecture: any
|
||||
Depends: ${perl:Depends},
|
||||
Depends: ${misc:Depends},
|
||||
${perl:Depends},
|
||||
${shlibs:Depends},
|
||||
libproxmox-rs-perl (>= 0.3.3),
|
||||
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
|
||||
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 = "."
|
||||
crate_src_path = ".."
|
||||
maintainer = "Proxmox Support Team <support@proxmox.com>"
|
||||
|
||||
[source]
|
||||
section = "perl"
|
||||
vcs_git = "git://git.proxmox.com/git/proxmox.git"
|
||||
vcs_browser = "https://git.proxmox.com/?p=proxmox.git"
|
||||
vcs_git = "git://git.proxmox.com/git/proxmox-perl-rs.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
|
||||
|
||||
include /usr/share/dpkg/pkg-info.mk
|
||||
include /usr/share/rustc/architecture.mk
|
||||
|
||||
#export DH_VERBOSE=1
|
||||
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 $@
|
||||
|
||||
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 serde::{Deserialize, Serialize};
|
||||
|
||||
use proxmox_acme_rs::account::AccountData as AcmeAccountData;
|
||||
use proxmox_acme_rs::{Account, Client};
|
||||
use proxmox_acme::types::AccountData as AcmeAccountData;
|
||||
use proxmox_acme::{Account, Client};
|
||||
|
||||
/// Our on-disk format inherited from PVE's proxmox-acme code.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
@ -79,6 +79,7 @@ impl Inner {
|
||||
tos_agreed: bool,
|
||||
contact: Vec<String>,
|
||||
rsa_bits: Option<u32>,
|
||||
eab_creds: Option<(String, String)>,
|
||||
) -> Result<(), Error> {
|
||||
self.tos = if tos_agreed {
|
||||
self.client.terms_of_service_url()?.map(str::to_owned)
|
||||
@ -86,7 +87,9 @@ impl Inner {
|
||||
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()
|
||||
.write(true)
|
||||
.create(true)
|
||||
@ -182,67 +185,45 @@ impl Inner {
|
||||
#[perlmod::package(name = "PMG::RS::Acme")]
|
||||
pub mod export {
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::Error;
|
||||
use serde_bytes::{ByteBuf, Bytes};
|
||||
|
||||
use perlmod::Value;
|
||||
use proxmox_acme_rs::directory::Meta;
|
||||
use proxmox_acme_rs::order::OrderData;
|
||||
use proxmox_acme_rs::{Authorization, Challenge, Order};
|
||||
use proxmox_acme::directory::Meta;
|
||||
use proxmox_acme::order::OrderData;
|
||||
use proxmox_acme::{Authorization, Challenge, Order};
|
||||
|
||||
use super::{AccountData, Inner};
|
||||
|
||||
const CLASSNAME: &str = "PMG::RS::Acme";
|
||||
perlmod::declare_magic!(Box<Acme> : &Acme as "PMG::RS::Acme");
|
||||
|
||||
/// An Acme client instance.
|
||||
pub struct Acme {
|
||||
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.
|
||||
#[export(raw_return)]
|
||||
pub fn new(#[raw] class: Value, api_directory: String) -> Result<Value, Error> {
|
||||
bless(
|
||||
class,
|
||||
Box::new(Acme {
|
||||
Ok(perlmod::instantiate_magic!(
|
||||
&class,
|
||||
MAGIC => Box::new(Acme {
|
||||
inner: Mutex::new(Inner::new(api_directory)?),
|
||||
}),
|
||||
)
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
/// Load an existing account.
|
||||
#[export(raw_return)]
|
||||
pub fn load(#[raw] class: Value, account_path: String) -> Result<Value, Error> {
|
||||
bless(
|
||||
class,
|
||||
Box::new(Acme {
|
||||
Ok(perlmod::instantiate_magic!(
|
||||
&class,
|
||||
MAGIC => Box::new(Acme {
|
||||
inner: Mutex::new(Inner::load(account_path)?),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[export(name = "DESTROY")]
|
||||
fn destroy(#[raw] this: Value) {
|
||||
perlmod::destructor!(this, Acme: CLASSNAME);
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a new account.
|
||||
@ -260,11 +241,16 @@ pub mod export {
|
||||
tos_agreed: bool,
|
||||
contact: Vec<String>,
|
||||
rsa_bits: Option<u32>,
|
||||
eab_kid: Option<String>,
|
||||
eab_hmac_key: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
this.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.new_account(account_path, tos_agreed, contact, rsa_bits)
|
||||
this.inner.lock().unwrap().new_account(
|
||||
account_path,
|
||||
tos_agreed,
|
||||
contact,
|
||||
rsa_bits,
|
||||
eab_kid.zip(eab_hmac_key),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the directory's meta information.
|
||||
|
@ -1,12 +1,16 @@
|
||||
#[perlmod::package(name = "PMG::RS::APT::Repositories")]
|
||||
mod export {
|
||||
use anyhow::Error;
|
||||
use proxmox_apt_api_types::{
|
||||
APTChangeRepositoryOptions, APTRepositoriesResult, APTRepositoryHandle,
|
||||
};
|
||||
use proxmox_config_digest::ConfigDigest;
|
||||
|
||||
use crate::common::apt::repositories::export as common;
|
||||
|
||||
/// Get information about configured and standard repositories.
|
||||
#[export]
|
||||
pub fn repositories() -> Result<common::RepositoriesResult, Error> {
|
||||
pub fn repositories() -> Result<APTRepositoriesResult, Error> {
|
||||
common::repositories("pmg")
|
||||
}
|
||||
|
||||
@ -15,7 +19,10 @@ mod export {
|
||||
///
|
||||
/// The `digest` parameter asserts that the configuration has not been modified.
|
||||
#[export]
|
||||
pub fn add_repository(handle: &str, digest: Option<&str>) -> Result<(), Error> {
|
||||
pub fn add_repository(
|
||||
handle: APTRepositoryHandle,
|
||||
digest: Option<ConfigDigest>,
|
||||
) -> Result<(), Error> {
|
||||
common::add_repository(handle, "pmg", digest)
|
||||
}
|
||||
|
||||
@ -26,8 +33,8 @@ mod export {
|
||||
pub fn change_repository(
|
||||
path: &str,
|
||||
index: usize,
|
||||
options: common::ChangeProperties,
|
||||
digest: Option<&str>,
|
||||
options: APTChangeRepositoryOptions,
|
||||
digest: Option<ConfigDigest>,
|
||||
) -> Result<(), Error> {
|
||||
common::change_repository(path, index, options, digest)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ pub mod export {
|
||||
use anyhow::Error;
|
||||
use serde_bytes::ByteBuf;
|
||||
|
||||
use proxmox_acme_rs::util::Csr;
|
||||
use proxmox_acme::util::Csr;
|
||||
|
||||
/// Generates a CSR and its accompanying private key.
|
||||
///
|
||||
|
@ -1,3 +1,7 @@
|
||||
use anyhow::Error;
|
||||
|
||||
use proxmox_apt_api_types::APTUpdateInfo;
|
||||
|
||||
#[path = "../common/src/mod.rs"]
|
||||
pub mod common;
|
||||
|
||||
@ -12,6 +16,20 @@ mod export {
|
||||
|
||||
#[export]
|
||||
pub fn init() {
|
||||
common::logger::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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_updates_available(_updates: &[&APTUpdateInfo]) -> Result<(), Error> {
|
||||
log::warn!("update notifications are not implemented for PMG yet");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub(self) use proxmox_tfa::api::{
|
||||
|
||||
#[perlmod::package(name = "PMG::RS::TFA")]
|
||||
mod export {
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Mutex;
|
||||
|
||||
@ -436,6 +437,66 @@ mod export {
|
||||
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()`].
|
||||
@ -589,9 +650,8 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: enable once we have UI/API admin stuff to unlock locked accounts
|
||||
fn enable_lockout(&self) -> bool {
|
||||
false
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,3 +3,6 @@
|
||||
directory = "/usr/share/cargo/registry"
|
||||
[source.crates-io]
|
||||
replace-with = "debian-packages"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "pve-rs"
|
||||
version = "0.7.7"
|
||||
version = "0.8.10"
|
||||
description = "PVE parts which have been ported to Rust"
|
||||
homepage = "https://www.proxmox.com"
|
||||
authors = ["Proxmox Support Team <support@proxmox.com>"]
|
||||
@ -8,35 +8,40 @@ edition = "2021"
|
||||
license = "AGPL-3"
|
||||
repository = "https://git.proxmox.com/?p=proxmox.git"
|
||||
|
||||
exclude = [
|
||||
"debian",
|
||||
]
|
||||
exclude = ["debian"]
|
||||
|
||||
[lib]
|
||||
crate-type = [ "cdylib" ]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
base32 = "0.4"
|
||||
base64 = "0.13"
|
||||
env_logger = "0.9"
|
||||
hex = "0.4"
|
||||
http = "0.2.7"
|
||||
libc = "0.2"
|
||||
log = "0.4.17"
|
||||
nix = "0.26"
|
||||
openssl = "0.10.40"
|
||||
serde = "1.0"
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1.37"
|
||||
url = "2"
|
||||
|
||||
perlmod = { version = "0.13", features = [ "exporter" ] }
|
||||
perlmod = { version = "0.13", features = ["exporter"] }
|
||||
|
||||
proxmox-apt = "0.9.4"
|
||||
proxmox-http = { version = "0.8", features = ["client-sync", "client-trait"] }
|
||||
proxmox-openid = "0.9.8"
|
||||
proxmox-resource-scheduling = "0.2.1"
|
||||
proxmox-subscription = "0.3"
|
||||
proxmox-sys = "0.4.2"
|
||||
proxmox-tfa = { version = "4", features = ["api"] }
|
||||
proxmox-time = "1.1.3"
|
||||
proxmox-apt = { version = "0.11", features = ["cache"] }
|
||||
proxmox-apt-api-types = "1.0"
|
||||
proxmox-config-digest = "0.1"
|
||||
proxmox-http = { version = "0.9", features = ["client-sync", "client-trait"] }
|
||||
proxmox-http-error = "0.1.0"
|
||||
proxmox-log = "0.2"
|
||||
proxmox-notify = { version = "0.4", features = ["pve-context"] }
|
||||
proxmox-openid = "0.10"
|
||||
proxmox-resource-scheduling = "0.3.0"
|
||||
proxmox-shared-cache = "0.1.0"
|
||||
proxmox-subscription = "0.4"
|
||||
proxmox-sys = "0.6"
|
||||
proxmox-tfa = { version = "5", features = ["api"] }
|
||||
proxmox-time = "2"
|
||||
|
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
|
@ -23,7 +23,8 @@ PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \
|
||||
--lib=pve_rs \
|
||||
--lib-tag=proxmox \
|
||||
--lib-package=Proxmox::Lib::PVE \
|
||||
--lib-prefix=PVE
|
||||
--lib-prefix=PVE \
|
||||
--include-file=Fixup.pm
|
||||
|
||||
PERLMOD_PACKAGES := \
|
||||
PVE::RS::APT::Repositories \
|
||||
@ -31,6 +32,8 @@ PERLMOD_PACKAGES := \
|
||||
PVE::RS::ResourceScheduling::Static \
|
||||
PVE::RS::TFA
|
||||
|
||||
PERLMOD_PACKAGE_FILES := $(addsuffix .pm,$(subst ::,/,$(PERLMOD_PACKAGES)))
|
||||
|
||||
ifeq ($(BUILD_MODE), release)
|
||||
CARGO_BUILD_ARGS += --release
|
||||
TARGET_DIR=release
|
||||
@ -42,18 +45,19 @@ all: PVE
|
||||
cargo build $(CARGO_BUILD_ARGS)
|
||||
mkdir -p test/Proxmox/Lib
|
||||
sed -r -e \
|
||||
's@^sub libdirs.*$$@sub libdirs { return ("./target/$(TARGET_DIR)", "./../target/$(TARGET_DIR)"); }@' \
|
||||
's@^sub libfile.*$$@sub libfile { "$(shell pwd)/target/$(TARGET_DIR)/libpve_rs.so"; }@' \
|
||||
Proxmox/Lib/PVE.pm >test/Proxmox/Lib/PVE.pm
|
||||
|
||||
PVE: Proxmox/Lib/PVE.pm
|
||||
Proxmox/Lib/PVE.pm:
|
||||
Proxmox: Proxmox/Lib/PVE.pm
|
||||
PVE: $(PERLMOD_PACKAGE_FILES)
|
||||
Proxmox/Lib/PVE.pm $(PERLMOD_PACKAGE_FILES) &: Fixup.pm
|
||||
$(PERLMOD_GENPACKAGE) $(PERLMOD_PACKAGES)
|
||||
|
||||
check: all
|
||||
$(MAKE) -C test test
|
||||
|
||||
.PHONY: install
|
||||
install: target/release/libpve_rs.so Proxmox/Lib/PVE.pm PVE
|
||||
install: target/release/libpve_rs.so Proxmox/Lib/PVE.pm $(PERLMOD_PACKAGE_FILES)
|
||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto
|
||||
install -m644 target/release/libpve_rs.so $(DESTDIR)$(PERL_INSTALLVENDORARCH)/auto/libpve_rs.so
|
||||
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)
|
||||
@ -62,6 +66,7 @@ install: target/release/libpve_rs.so Proxmox/Lib/PVE.pm PVE
|
||||
find $(PM_DIR) \! -type d -print -exec install -Dm644 '{}' $(DESTDIR)$(PERL_INSTALLVENDORLIB)'/{}' ';'
|
||||
|
||||
clean:
|
||||
rm -rf PVE Proxmox
|
||||
cargo clean
|
||||
rm -f *.deb *.dsc *.tar.* *.build *.buildinfo *.changes Cargo.lock
|
||||
rm -rf $(PACKAGE)-[0-9]*/
|
||||
@ -77,11 +82,11 @@ upload: $(DEBS)
|
||||
git diff --exit-code --stat && git diff --exit-code --stat --staged
|
||||
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
|
||||
$(BUILDDIR): src debian test common/src Cargo.toml Makefile .cargo/config.toml
|
||||
rm -rf $(BUILDDIR) $(BUILDDIR).tmp
|
||||
mkdir $(BUILDDIR).tmp
|
||||
mkdir $(BUILDDIR).tmp/common
|
||||
cp -a -t $(BUILDDIR).tmp src debian test Cargo.toml Makefile .cargo
|
||||
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)
|
||||
|
||||
|
@ -1,9 +1,99 @@
|
||||
libpve-rs-perl (0.7.7) bullseye; urgency=medium
|
||||
libpve-rs-perl (0.8.10) bookworm; urgency=medium
|
||||
|
||||
* build with proxmox-apt 0.9.4 to also detect repository with next suite as
|
||||
configured
|
||||
* use apt api implementation from the proxmox-apt crate
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Fri, 09 Jun 2023 11:30:59 +0200
|
||||
* send apt update notification via proxmox-notify
|
||||
|
||||
* add bindings for proxmox-shared-cache crate
|
||||
|
||||
* update to current proxmox-time/tfa/sys/log crates
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Fri, 09 Aug 2024 13:42:38 +0200
|
||||
|
||||
libpve-rs-perl (0.8.9) bookworm; urgency=medium
|
||||
|
||||
* update to notify 0.4: use file based notification templates
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Tue, 04 Jun 2024 11:01:03 +0200
|
||||
|
||||
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
|
||||
|
||||
|
@ -1,41 +1,49 @@
|
||||
Source: libpve-rs-perl
|
||||
Section: perl
|
||||
Priority: optional
|
||||
Build-Depends:
|
||||
debhelper-compat (= 13),
|
||||
dh-cargo (>= 24),
|
||||
perlmod-bin,
|
||||
cargo:native <!nocheck>,
|
||||
rustc:native <!nocheck>,
|
||||
libstd-rust-dev <!nocheck>,
|
||||
librust-anyhow-1+default-dev,
|
||||
librust-base32-0.4+default-dev,
|
||||
librust-base64-0.13+default-dev,
|
||||
librust-env-logger-0.9+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,
|
||||
librust-perlmod-0.13+exporter-dev,
|
||||
librust-proxmox-apt-0.9+default-dev (>= 0.9.4-~~),
|
||||
librust-proxmox-http-0.8+client-sync-dev,
|
||||
librust-proxmox-http-0.8+client-trait-dev,
|
||||
librust-proxmox-http-0.8+default-dev,
|
||||
librust-proxmox-openid-0.9+default-dev (>= 0.9.8-~~),
|
||||
librust-proxmox-resource-scheduling-0.2+default-dev (>= 0.2.1-~~),
|
||||
librust-proxmox-subscription-0.3+default-dev,
|
||||
librust-proxmox-sys-0.4+default-dev (>= 0.4.2-~~),
|
||||
librust-proxmox-tfa-4+api-dev,
|
||||
librust-proxmox-tfa-4+default-dev,
|
||||
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,
|
||||
Build-Depends: cargo:native <!nocheck>,
|
||||
debhelper-compat (= 13),
|
||||
dh-cargo (>= 25),
|
||||
librust-anyhow-1+default-dev,
|
||||
librust-base32-0.4+default-dev,
|
||||
librust-base64-0.13+default-dev,
|
||||
librust-hex-0.4+default-dev,
|
||||
librust-http-0.2+default-dev (>= 0.2.7-~~),
|
||||
librust-libc-0.2+default-dev,
|
||||
librust-log-0.4+default-dev (>= 0.4.17-~~),
|
||||
librust-nix-0.26+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-apt-0.11+cache-dev,
|
||||
librust-proxmox-apt-0.11+default-dev,
|
||||
librust-proxmox-apt-api-types-1+default-dev,
|
||||
librust-proxmox-config-digest-0.1+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-log-0.2+default-dev,
|
||||
librust-proxmox-notify-0.4+default-dev,
|
||||
librust-proxmox-notify-0.4+pve-context-dev,
|
||||
librust-proxmox-openid-0.10+default-dev,
|
||||
librust-proxmox-resource-scheduling-0.3+default-dev,
|
||||
librust-proxmox-shared-cache-0.1+default-dev,
|
||||
librust-proxmox-subscription-0.4+default-dev,
|
||||
librust-proxmox-sys-0.6+default-dev,
|
||||
librust-proxmox-tfa-5+api-dev,
|
||||
librust-proxmox-tfa-5+default-dev,
|
||||
librust-proxmox-time-2+default-dev,
|
||||
librust-serde-1+default-dev,
|
||||
librust-serde-bytes-0.11+default-dev,
|
||||
librust-serde-json-1+default-dev,
|
||||
librust-tracing-0.1+default-dev (>= 0.1.37-~~),
|
||||
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>
|
||||
Standards-Version: 4.5.1
|
||||
Standards-Version: 4.6.1
|
||||
Vcs-Git: git://git.proxmox.com/git/proxmox-perl-rs.git
|
||||
Vcs-Browser: https://git.proxmox.com/?p=proxmox-perl-rs.git
|
||||
Homepage: https://www.proxmox.com
|
||||
@ -43,14 +51,14 @@ Rules-Requires-Root: no
|
||||
|
||||
Package: libpve-rs-perl
|
||||
Architecture: any
|
||||
Depends:
|
||||
${misc:Depends},
|
||||
${perl:Depends},
|
||||
${shlibs:Depends},
|
||||
Breaks:
|
||||
libpve-access-control (<< 7.1-3),
|
||||
libpve-common-perl (<< 7.1-4),
|
||||
pve-manager (<< 7.1-11),
|
||||
Depends: ${misc:Depends},
|
||||
${perl:Depends},
|
||||
${shlibs:Depends},
|
||||
libproxmox-rs-perl (>= 0.3.3),
|
||||
Breaks: libpve-access-control (<< 7.1-3),
|
||||
libpve-common-perl (<< 7.1-4),
|
||||
libpve-notify-perl (<< 8.0.7),
|
||||
pve-manager (<< 7.1-11),
|
||||
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
|
||||
debcargo for use with cargo and dh-cargo.
|
||||
|
@ -1,7 +1,25 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
include /usr/share/dpkg/pkg-info.mk
|
||||
include /usr/share/rustc/architecture.mk
|
||||
|
||||
#export DH_VERBOSE=1
|
||||
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 $@
|
||||
|
||||
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
|
||||
|
@ -2,12 +2,17 @@
|
||||
mod export {
|
||||
use anyhow::Error;
|
||||
|
||||
use proxmox_apt_api_types::{
|
||||
APTChangeRepositoryOptions, APTRepositoriesResult, APTRepositoryHandle,
|
||||
};
|
||||
use proxmox_config_digest::ConfigDigest;
|
||||
|
||||
use crate::common::apt::repositories::export as common;
|
||||
|
||||
/// Get information about configured and standard repositories.
|
||||
#[export]
|
||||
pub fn repositories() -> Result<common::RepositoriesResult, Error> {
|
||||
common::repositories("pve")
|
||||
pub fn repositories() -> Result<APTRepositoriesResult, Error> {
|
||||
proxmox_apt::list_repositories("pve")
|
||||
}
|
||||
|
||||
/// Add the repository identified by the `handle`.
|
||||
@ -15,7 +20,10 @@ mod export {
|
||||
///
|
||||
/// The `digest` parameter asserts that the configuration has not been modified.
|
||||
#[export]
|
||||
pub fn add_repository(handle: &str, digest: Option<&str>) -> Result<(), Error> {
|
||||
pub fn add_repository(
|
||||
handle: APTRepositoryHandle,
|
||||
digest: Option<ConfigDigest>,
|
||||
) -> Result<(), Error> {
|
||||
common::add_repository(handle, "pve", digest)
|
||||
}
|
||||
|
||||
@ -26,8 +34,8 @@ mod export {
|
||||
pub fn change_repository(
|
||||
path: &str,
|
||||
index: usize,
|
||||
options: common::ChangeProperties,
|
||||
digest: Option<&str>,
|
||||
options: APTChangeRepositoryOptions,
|
||||
digest: Option<ConfigDigest>,
|
||||
) -> Result<(), Error> {
|
||||
common::change_repository(path, index, options, digest)
|
||||
}
|
||||
|
@ -1,5 +1,13 @@
|
||||
//! Rust library for the Proxmox VE code base.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Error;
|
||||
use serde_json::json;
|
||||
|
||||
use proxmox_apt_api_types::APTUpdateInfo;
|
||||
use proxmox_notify::{Config, Notification, Severity};
|
||||
|
||||
#[path = "../common/src/mod.rs"]
|
||||
pub mod common;
|
||||
|
||||
@ -10,10 +18,69 @@ 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();
|
||||
common::logger::init("PVE_LOG", "info");
|
||||
proxmox_notify::context::set_context(&PVE_CONTEXT);
|
||||
}
|
||||
}
|
||||
|
||||
fn send_notification(notification: &Notification) -> Result<(), Error> {
|
||||
let config = proxmox_sys::fs::file_read_optional_string("/etc/pve/notifications.cfg")?
|
||||
.unwrap_or_default();
|
||||
let private_config =
|
||||
proxmox_sys::fs::file_read_optional_string("/etc/pve/priv/notifications.cfg")?
|
||||
.unwrap_or_default();
|
||||
|
||||
let config = Config::new(&config, &private_config)?;
|
||||
|
||||
proxmox_notify::api::common::send(&config, notification)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_updates_available(updates: &[&APTUpdateInfo]) -> Result<(), Error> {
|
||||
let hostname = proxmox_sys::nodename().to_string();
|
||||
|
||||
let metadata = HashMap::from([
|
||||
("hostname".into(), hostname.clone()),
|
||||
("type".into(), "package-updates".into()),
|
||||
]);
|
||||
|
||||
// The template uses the `table` handlebars helper, so
|
||||
// we need to form the approriate data structure first.
|
||||
let update_table = json!({
|
||||
"schema": {
|
||||
"columns": [
|
||||
{
|
||||
"label": "Package",
|
||||
"id": "Package",
|
||||
},
|
||||
{
|
||||
"label": "Old Version",
|
||||
"id": "OldVersion",
|
||||
},
|
||||
{
|
||||
"label": "New Version",
|
||||
"id": "Version",
|
||||
}
|
||||
],
|
||||
},
|
||||
"data": updates,
|
||||
});
|
||||
|
||||
let template_data = json!({
|
||||
"hostname": hostname,
|
||||
"updates": update_table,
|
||||
});
|
||||
|
||||
let notification =
|
||||
Notification::from_template(Severity::Info, "package-updates", template_data, metadata);
|
||||
|
||||
send_notification(¬ification)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#[perlmod::package(name = "PVE::RS::OpenId", lib = "pve_rs")]
|
||||
mod export {
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::Error;
|
||||
@ -9,34 +8,13 @@ mod export {
|
||||
|
||||
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.
|
||||
pub struct OpenId {
|
||||
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
|
||||
#[export(raw_return)]
|
||||
pub fn discover(
|
||||
@ -45,12 +23,12 @@ mod export {
|
||||
redirect_url: &str,
|
||||
) -> Result<Value, Error> {
|
||||
let open_id = OpenIdAuthenticator::discover(&config, redirect_url)?;
|
||||
bless(
|
||||
class,
|
||||
Box::new(OpenId {
|
||||
Ok(perlmod::instantiate_magic!(
|
||||
&class,
|
||||
MAGIC => Box::new(OpenId {
|
||||
inner: Mutex::new(open_id),
|
||||
}),
|
||||
)
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
#[export]
|
||||
|
@ -27,6 +27,7 @@ pub(self) use proxmox_tfa::api::{
|
||||
|
||||
#[perlmod::package(name = "PVE::RS::TFA")]
|
||||
mod export {
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Mutex;
|
||||
|
||||
@ -484,6 +485,66 @@ mod export {
|
||||
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`
|
||||
@ -993,9 +1054,8 @@ impl proxmox_tfa::api::OpenUserChallengeData for UserAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: Enable this once we can consider most clusters to support the new format.
|
||||
fn enable_lockout(&self) -> bool {
|
||||
false
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,8 +86,59 @@ sub test_overcommitted {
|
||||
is($nodes[3], "A", 'fourth should be A');
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -1 +0,0 @@
|
||||
edition = "2018"
|
Loading…
x
Reference in New Issue
Block a user