From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id D135E7D721 for ; Tue, 9 Nov 2021 12:27:53 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7CC56B08F for ; Tue, 9 Nov 2021 12:27:41 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 66CD8AFDD for ; Tue, 9 Nov 2021 12:27:27 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 361A1427FE for ; Tue, 9 Nov 2021 12:27:27 +0100 (CET) From: Wolfgang Bumiller To: pve-devel@lists.proxmox.com Date: Tue, 9 Nov 2021 12:26:51 +0100 Message-Id: <20211109112721.130935-3-w.bumiller@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20211109112721.130935-1-w.bumiller@proxmox.com> References: <20211109112721.130935-1-w.bumiller@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.502 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_SHORT 0.001 Use of a URL Shortener for very short URL SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [default.mk, openid.pm, repositories.rs, tfa.pm, gnu.org, mod.rs, lib.rs, repositories.pm, proxmox.com] Subject: [pve-devel] [PATCH proxmox-perl-rs 2/6] import pve-rs X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 09 Nov 2021 11:27:54 -0000 Signed-off-by: Wolfgang Bumiller --- Cargo.toml | 3 + pve-rs/Cargo.toml | 40 ++++++++ pve-rs/Makefile | 76 ++++++++++++++++ pve-rs/debian/changelog | 55 +++++++++++ pve-rs/debian/compat | 1 + pve-rs/debian/control | 41 +++++++++ pve-rs/debian/copyright | 16 ++++ pve-rs/debian/debcargo.toml | 8 ++ pve-rs/debian/rules | 8 ++ pve-rs/debian/source/format | 1 + pve-rs/debian/triggers | 1 + pve-rs/src/apt/mod.rs | 1 + pve-rs/src/apt/repositories.rs | 162 +++++++++++++++++++++++++++++++++ pve-rs/src/lib.rs | 2 + pve-rs/src/openid/mod.rs | 88 ++++++++++++++++++ 15 files changed, 503 insertions(+) create mode 100644 pve-rs/Cargo.toml create mode 100644 pve-rs/Makefile create mode 100644 pve-rs/debian/changelog create mode 100644 pve-rs/debian/compat create mode 100644 pve-rs/debian/control create mode 100644 pve-rs/debian/copyright create mode 100644 pve-rs/debian/debcargo.toml create mode 100755 pve-rs/debian/rules create mode 100644 pve-rs/debian/source/format create mode 100644 pve-rs/debian/triggers create mode 100644 pve-rs/src/apt/mod.rs create mode 100644 pve-rs/src/apt/repositories.rs create mode 100644 pve-rs/src/lib.rs create mode 100644 pve-rs/src/openid/mod.rs diff --git a/Cargo.toml b/Cargo.toml index e2c6ef8..c413d77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,5 @@ [workspace] exclude = [ "build", "perl-*" ] +members = [ + "pve-rs", +] diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml new file mode 100644 index 0000000..1c00edd --- /dev/null +++ b/pve-rs/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "pve-rs" +version = "0.3.0" +authors = ["Proxmox Support Team "] +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"] } diff --git a/pve-rs/Makefile b/pve-rs/Makefile new file mode 100644 index 0000000..c912d9d --- /dev/null +++ b/pve-rs/Makefile @@ -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 diff --git a/pve-rs/debian/changelog b/pve-rs/debian/changelog new file mode 100644 index 0000000..62060bf --- /dev/null +++ b/pve-rs/debian/changelog @@ -0,0 +1,55 @@ +libpve-rs-perl (0.3.0) UNRELEASED; urgency=medium + + * add TFA api + + -- Proxmox Support Team 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 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 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 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 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 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 Wed, 23 Jun 2021 11:09:31 +0200 + +libpve-rs-perl (0.1.0-1) unstable; urgency=medium + + * Initial release. + + -- Proxmox Support Team Thu, 27 May 2021 10:41:30 +0200 diff --git a/pve-rs/debian/compat b/pve-rs/debian/compat new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/pve-rs/debian/compat @@ -0,0 +1 @@ +10 diff --git a/pve-rs/debian/control b/pve-rs/debian/control new file mode 100644 index 0000000..18a99b2 --- /dev/null +++ b/pve-rs/debian/control @@ -0,0 +1,41 @@ +Source: libpve-rs-perl +Section: perl +Priority: optional +Build-Depends: debhelper (>= 12), + dh-cargo (>= 24), + cargo:native , + rustc:native , + libstd-rust-dev , + librust-anyhow-1+default-dev , + librust-base32-0.4+default-dev , + librust-base64-0.12+default-dev , + librust-hex-0.4+default-dev , + librust-libc-0.2+default-dev , + librust-nix-0.19+default-dev , + librust-openssl-0.10+default-dev , + librust-perlmod-0.8+default-dev (>= 0.7.1-~~) , + librust-perlmod-0.8+exporter-dev (>= 0.7.1-~~) , + librust-proxmox-apt-0.8+default-dev , + librust-proxmox-openid-0.8+default-dev , + librust-proxmox-tfa-1+default-dev , + librust-proxmox-tfa-1+u2f-dev , + librust-proxmox-time-1+default-dev , + librust-proxmox-uuid-1+default-dev , + librust-serde-1+default-dev , + librust-serde-json-1+default-dev , + librust-webauthn-rs-0.2+default-dev (>= 0.2.5-~~) +Maintainer: Proxmox Support Team +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. diff --git a/pve-rs/debian/copyright b/pve-rs/debian/copyright new file mode 100644 index 0000000..5661ef6 --- /dev/null +++ b/pve-rs/debian/copyright @@ -0,0 +1,16 @@ +Copyright (C) 2021 Proxmox Server Solutions GmbH + +This software is written by Proxmox Server Solutions GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/pve-rs/debian/debcargo.toml b/pve-rs/debian/debcargo.toml new file mode 100644 index 0000000..e642fe2 --- /dev/null +++ b/pve-rs/debian/debcargo.toml @@ -0,0 +1,8 @@ +overlay = "." +crate_src_path = ".." +maintainer = "Proxmox Support Team " + +[source] +section = "perl" +vcs_git = "git://git.proxmox.com/git/proxmox.git" +vcs_browser = "https://git.proxmox.com/?p=proxmox.git" diff --git a/pve-rs/debian/rules b/pve-rs/debian/rules new file mode 100755 index 0000000..2b39115 --- /dev/null +++ b/pve-rs/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 +export BUILD_MODE=release +export RUSTFLAGS=-C prefer-dynamic + +%: + dh $@ diff --git a/pve-rs/debian/source/format b/pve-rs/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/pve-rs/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/pve-rs/debian/triggers b/pve-rs/debian/triggers new file mode 100644 index 0000000..59dd688 --- /dev/null +++ b/pve-rs/debian/triggers @@ -0,0 +1 @@ +activate-noawait pve-api-updates diff --git a/pve-rs/src/apt/mod.rs b/pve-rs/src/apt/mod.rs new file mode 100644 index 0000000..574c1a7 --- /dev/null +++ b/pve-rs/src/apt/mod.rs @@ -0,0 +1 @@ +mod repositories; diff --git a/pve-rs/src/apt/repositories.rs b/pve-rs/src/apt/repositories.rs new file mode 100644 index 0000000..2d5e1da --- /dev/null +++ b/pve-rs/src/apt/repositories.rs @@ -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, + + /// Errors for files that could not be parsed or read. + pub errors: Vec, + + /// Common digest for successfully parsed files. + pub digest: String, + + /// Additional information/warnings about repositories. + pub infos: Vec, + + /// Standard repositories and their configuration status. + pub standard_repos: Vec, + } + + #[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, + } + + /// Get information about configured and standard repositories. + #[export] + pub fn repositories() -> Result { + 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(()) + } +} diff --git a/pve-rs/src/lib.rs b/pve-rs/src/lib.rs new file mode 100644 index 0000000..cad331d --- /dev/null +++ b/pve-rs/src/lib.rs @@ -0,0 +1,2 @@ +pub mod apt; +pub mod openid; diff --git a/pve-rs/src/openid/mod.rs b/pve-rs/src/openid/mod.rs new file mode 100644 index 0000000..febe927 --- /dev/null +++ b/pve-rs/src/openid/mod.rs @@ -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, + } + + 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) -> Result { + let value = Value::new_pointer::(&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 { + + 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 { + + 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 { + + let open_id = this.inner.lock().unwrap(); + let claims = open_id.verify_authorization_code(code, &private_auth_state)?; + + Ok(to_value(&claims)?) + } +} -- 2.30.2