import pve-rs

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2021-11-09 08:56:38 +01:00
parent 50754a46aa
commit 1ae868ad78
15 changed files with 503 additions and 0 deletions

View File

@ -1,2 +1,5 @@
[workspace]
exclude = [ "build", "perl-*" ]
members = [
"pve-rs",
]

40
pve-rs/Cargo.toml Normal file
View File

@ -0,0 +1,40 @@
[package]
name = "pve-rs"
version = "0.3.0"
authors = ["Proxmox Support Team <support@proxmox.com>"]
edition = "2018"
license = "AGPL-3"
description = "PVE parts which have been ported to Rust"
homepage = "https://www.proxmox.com"
exclude = [
"debian",
]
[lib]
crate-type = [ "cdylib" ]
[dependencies]
anyhow = "1.0"
base32 = "0.4"
base64 = "0.12"
hex = "0.4"
libc = "0.2"
nix = "0.19"
openssl = "0.10"
serde = "1.0"
serde_bytes = "0.11"
serde_json = "1.0"
perlmod = { version = "0.8.1", features = [ "exporter" ] }
proxmox-apt = "0.8"
proxmox-openid = "0.8"
#proxmox-tfa-api = { path = "../proxmox-tfa-api", version = "0.1" }
# Dependencies purely in proxmox-tfa-api:
webauthn-rs = "0.2.5"
proxmox-time = "1"
proxmox-uuid = "1"
proxmox-tfa = { version = "1.2", features = ["u2f"] }

76
pve-rs/Makefile Normal file
View File

@ -0,0 +1,76 @@
include /usr/share/dpkg/default.mk
PACKAGE=libpve-rs-perl
export PERLMOD_PRODUCT=PVE
ARCH:=$(shell dpkg-architecture -qDEB_BUILD_ARCH)
export GITVERSION:=$(shell git rev-parse HEAD)
PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
PERL_INSTALLVENDORLIB != perl -MConfig -e 'print $$Config{installvendorlib};'
MAIN_DEB=${PACKAGE}_${DEB_VERSION}_${ARCH}.deb
DBGSYM_DEB=${PACKAGE}-dbgsym_${DEB_VERSION}_${ARCH}.deb
DEBS=$(MAIN_DEB) $(DBGSYM_DEB)
DESTDIR=
PM_DIRS := \
PVE/RS/APT
PM_FILES := \
PVE/RS/APT/Repositories.pm \
PVE/RS/OpenId.pm \
PVE/RS/TFA.pm
ifeq ($(BUILD_MODE), release)
CARGO_BUILD_ARGS += --release
endif
all:
ifneq ($(BUILD_MODE), skip)
cargo build $(CARGO_BUILD_ARGS)
endif
# always re-create this dir
# but also copy the local target/ and PVE/ dirs as a build-cache
.PHONY: build
build:
rm -rf build
cargo build --release
rsync -a debian Makefile Cargo.toml Cargo.lock src target PVE build/
.PHONY: install
install: target/release/libpve_rs.so
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)/PVE/RS
for i in $(PM_DIRS); do \
install -d -m755 $(DESTDIR)$(PERL_INSTALLVENDORLIB)/$$i; \
done
for i in $(PM_FILES); do \
install -m644 $$i $(DESTDIR)$(PERL_INSTALLVENDORLIB)/$$i; \
done
.PHONY: deb
deb: $(MAIN_DEB)
$(MAIN_DEB): build
cd build; dpkg-buildpackage -b -us -uc --no-pre-clean
lintian $(DEBS)
distclean: clean
clean:
cargo clean
rm -rf *.deb *.dsc *.tar.gz *.buildinfo *.changes Cargo.lock build
find . -name '*~' -exec rm {} ';'
.PHONY: dinstall
dinstall: ${DEBS}
dpkg -i ${DEBS}
.PHONY: upload
upload: ${DEBS}
# check if working directory is clean
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

55
pve-rs/debian/changelog Normal file
View File

@ -0,0 +1,55 @@
libpve-rs-perl (0.3.0) UNRELEASED; urgency=medium
* add TFA api
-- Proxmox Support Team <support@proxmox.com> Wed, 20 Oct 2021 10:11:47 +0200
libpve-rs-perl (0.2.3) bullseye; urgency=medium
* use newer dependencies for apt to improve repo+suite handling
-- Proxmox Support Team <support@proxmox.com> Thu, 29 Jul 2021 18:13:07 +0200
libpve-rs-perl (0.2.2) bullseye; urgency=medium
* apt: avoid overwriting files that could not be parsed
* apt: check if repository is already configured before adding
-- Proxmox Support Team <support@proxmox.com> Fri, 02 Jul 2021 13:06:42 +0200
libpve-rs-perl (0.2.1) bullseye; urgency=medium
* depend on proxmox-apt 0.4.0
-- Proxmox Support Team <support@proxmox.com> Thu, 01 Jul 2021 18:37:20 +0200
libpve-rs-perl (0.2.0) bullseye; urgency=medium
* add bindings for proxmox-apt
* depend on proxmox-openid 0.6.0
* move to native version format
-- Proxmox Support Team <support@proxmox.com> Wed, 30 Jun 2021 20:56:19 +0200
libpve-rs-perl (0.1.2-1) unstable; urgency=medium
* depend on proxmox-openid 0.5.0
* set proxmox "default-features = false"
-- Proxmox Support Team <support@proxmox.com> Wed, 23 Jun 2021 11:34:34 +0200
libpve-rs-perl (0.1.1-1) unstable; urgency=medium
* depend on perlmod 0.5.1
-- Proxmox Support Team <support@proxmox.com> Wed, 23 Jun 2021 11:09:31 +0200
libpve-rs-perl (0.1.0-1) unstable; urgency=medium
* Initial release.
-- Proxmox Support Team <support@proxmox.com> Thu, 27 May 2021 10:41:30 +0200

1
pve-rs/debian/compat Normal file
View File

@ -0,0 +1 @@
10

41
pve-rs/debian/control Normal file
View File

@ -0,0 +1,41 @@
Source: libpve-rs-perl
Section: perl
Priority: optional
Build-Depends: debhelper (>= 12),
dh-cargo (>= 24),
cargo:native <!nocheck>,
rustc:native <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-anyhow-1+default-dev <!nocheck>,
librust-base32-0.4+default-dev <!nocheck>,
librust-base64-0.12+default-dev <!nocheck>,
librust-hex-0.4+default-dev <!nocheck>,
librust-libc-0.2+default-dev <!nocheck>,
librust-nix-0.19+default-dev <!nocheck>,
librust-openssl-0.10+default-dev <!nocheck>,
librust-perlmod-0.8+default-dev (>= 0.7.1-~~) <!nocheck>,
librust-perlmod-0.8+exporter-dev (>= 0.7.1-~~) <!nocheck>,
librust-proxmox-apt-0.8+default-dev <!nocheck>,
librust-proxmox-openid-0.8+default-dev <!nocheck>,
librust-proxmox-tfa-1+default-dev <!nocheck>,
librust-proxmox-tfa-1+u2f-dev <!nocheck>,
librust-proxmox-time-1+default-dev <!nocheck>,
librust-proxmox-uuid-1+default-dev <!nocheck>,
librust-serde-1+default-dev <!nocheck>,
librust-serde-json-1+default-dev <!nocheck>,
librust-webauthn-rs-0.2+default-dev (>= 0.2.5-~~) <!nocheck>
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.5.1
Vcs-Git: git://git.proxmox.com/git/proxmox.git
Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
Homepage: https://www.proxmox.com
Rules-Requires-Root: no
Package: libpve-rs-perl
Architecture: any
Depends:
${misc:Depends},
${shlibs:Depends},
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.

16
pve-rs/debian/copyright Normal file
View File

@ -0,0 +1,16 @@
Copyright (C) 2021 Proxmox Server Solutions GmbH
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,8 @@
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"

8
pve-rs/debian/rules Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/make -f
#export DH_VERBOSE=1
export BUILD_MODE=release
export RUSTFLAGS=-C prefer-dynamic
%:
dh $@

View File

@ -0,0 +1 @@
3.0 (native)

1
pve-rs/debian/triggers Normal file
View File

@ -0,0 +1 @@
activate-noawait pve-api-updates

1
pve-rs/src/apt/mod.rs Normal file
View File

@ -0,0 +1 @@
mod repositories;

View File

@ -0,0 +1,162 @@
#[perlmod::package(name = "PVE::RS::APT::Repositories", lib = "pve_rs")]
mod export {
use std::convert::TryInto;
use anyhow::{bail, Error};
use serde::{Deserialize, Serialize};
use proxmox_apt::repositories::{
APTRepositoryFile, APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo,
APTStandardRepository,
};
#[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>,
}
/// Get information about configured and standard repositories.
#[export]
pub fn repositories() -> 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, "pve", suite);
Ok(RepositoriesResult {
files,
errors,
digest,
infos,
standard_repos,
})
}
/// Add the repository identified by the `handle`.
/// If the repository is already configured, it will be set to enabled.
///
/// The `digest` parameter asserts that the configuration has not been modified.
#[export]
pub fn add_repository(handle: &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, "pve", &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, "pve", 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 == 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(())
}
/// Change the properties of the specified repository.
///
/// The `digest` parameter asserts that the configuration has not been modified.
#[export]
pub fn change_repository(
path: &str,
index: usize,
options: ChangeProperties,
digest: Option<&str>,
) -> Result<(), Error> {
let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
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.");
}
}
if let Some(error) = errors.iter().find(|error| error.path == path) {
bail!("unable to parse file {} - {}", error.path, error.error);
}
if let Some(file) = files.iter_mut().find(|file| file.path == path) {
if let Some(repo) = file.repositories.get_mut(index) {
if let Some(enabled) = options.enabled {
repo.set_enabled(enabled);
}
file.write()?;
} else {
bail!("invalid index - {}", index);
}
} else {
bail!("invalid path - {}", path);
}
Ok(())
}
}

2
pve-rs/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod apt;
pub mod openid;

88
pve-rs/src/openid/mod.rs Normal file
View File

@ -0,0 +1,88 @@
#[perlmod::package(name = "PVE::RS::OpenId", lib = "pve_rs")]
mod export {
use std::sync::Mutex;
use std::convert::TryFrom;
use anyhow::Error;
use perlmod::{to_value, Value};
use proxmox_openid::{OpenIdConfig, OpenIdAuthenticator, PrivateAuthState};
const CLASSNAME: &str = "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(
#[raw] class: Value,
config: OpenIdConfig,
redirect_url: &str,
) -> Result<Value, Error> {
let open_id = OpenIdAuthenticator::discover(&config, redirect_url)?;
bless(
class,
Box::new(OpenId {
inner: Mutex::new(open_id),
}),
)
}
#[export]
pub fn authorize_url(
#[try_from_ref] this: &OpenId,
state_dir: &str,
realm: &str,
) -> Result<String, Error> {
let open_id = this.inner.lock().unwrap();
open_id.authorize_url(state_dir, realm)
}
#[export]
pub fn verify_public_auth_state(
state_dir: &str,
state: &str,
) -> Result<(String, PrivateAuthState), Error> {
OpenIdAuthenticator::verify_public_auth_state(state_dir, state)
}
#[export(raw_return)]
pub fn verify_authorization_code(
#[try_from_ref] this: &OpenId,
code: &str,
private_auth_state: PrivateAuthState,
) -> Result<Value, Error> {
let open_id = this.inner.lock().unwrap();
let claims = open_id.verify_authorization_code(code, &private_auth_state)?;
Ok(to_value(&claims)?)
}
}