all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [RFC proxmox 3/3] new proxmox-cert-management crate
Date: Wed, 18 Oct 2023 12:39:10 +0200	[thread overview]
Message-ID: <20231018103911.3798182-4-d.csapak@proxmox.com> (raw)
In-Reply-To: <20231018103911.3798182-1-d.csapak@proxmox.com>

refactored from proxmox-backup, but uses the new ServerConfig global
settings for the users and directories.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 Cargo.toml                                   |   2 +
 proxmox-cert-management/Cargo.toml           |  23 +++
 proxmox-cert-management/debian/changelog     |   5 +
 proxmox-cert-management/debian/control       |  53 ++++++
 proxmox-cert-management/debian/copyright     |  18 ++
 proxmox-cert-management/debian/debcargo.toml |   7 +
 proxmox-cert-management/src/lib.rs           | 182 +++++++++++++++++++
 7 files changed, 290 insertions(+)
 create mode 100644 proxmox-cert-management/Cargo.toml
 create mode 100644 proxmox-cert-management/debian/changelog
 create mode 100644 proxmox-cert-management/debian/control
 create mode 100644 proxmox-cert-management/debian/copyright
 create mode 100644 proxmox-cert-management/debian/debcargo.toml
 create mode 100644 proxmox-cert-management/src/lib.rs

diff --git a/Cargo.toml b/Cargo.toml
index 4b4b787..0a4ad06 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ members = [
     "proxmox-async",
     "proxmox-auth-api",
     "proxmox-borrow",
+    "proxmox-cert-management",
     "proxmox-client",
     "proxmox-compression",
     "proxmox-http",
@@ -91,6 +92,7 @@ webauthn-rs = "0.3"
 zstd = { version = "0.12", features = [ "bindgen" ] }
 
 # workspace dependencies
+proxmox-auth-api = { version = "0.3", path = "proxmox-auth-api" }
 proxmox-api-macro = { version = "1.0.6", path = "proxmox-api-macro" }
 proxmox-async = { version = "0.4.1", path = "proxmox-async" }
 proxmox-compression = { version = "0.2.0", path = "proxmox-compression" }
diff --git a/proxmox-cert-management/Cargo.toml b/proxmox-cert-management/Cargo.toml
new file mode 100644
index 0000000..816f4b6
--- /dev/null
+++ b/proxmox-cert-management/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "proxmox-cert-management"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+description = "Certificate management and utilities"
+
+exclude.workspace = true
+
+[dependencies]
+anyhow.workspace = true
+base64.workspace = true
+lazy_static.workspace = true
+nix.workspace = true
+openssl.workspace = true
+
+proxmox-auth-api = { workspace = true, features = ["api-types"] }
+proxmox-lang.workspace = true
+proxmox-sys.workspace = true
+proxmox-server-config.workspace = true
+proxmox-time.workspace = true
diff --git a/proxmox-cert-management/debian/changelog b/proxmox-cert-management/debian/changelog
new file mode 100644
index 0000000..ed5d4d6
--- /dev/null
+++ b/proxmox-cert-management/debian/changelog
@@ -0,0 +1,5 @@
+rust-proxmox-cert-management (0.1.0-1) stable; urgency=medium
+
+  * initial version
+
+ -- Proxmox Support Team <support@proxmox.com>  Tue, 17 Oct 2023 13:56:35 +0200
diff --git a/proxmox-cert-management/debian/control b/proxmox-cert-management/debian/control
new file mode 100644
index 0000000..593ea6c
--- /dev/null
+++ b/proxmox-cert-management/debian/control
@@ -0,0 +1,53 @@
+Source: rust-proxmox-cert-management
+Section: rust
+Priority: optional
+Build-Depends: debhelper (>= 12),
+ dh-cargo (>= 25),
+ cargo:native <!nocheck>,
+ rustc:native <!nocheck>,
+ libstd-rust-dev <!nocheck>,
+ librust-anyhow-1+default-dev <!nocheck>,
+ librust-base64-0.13+default-dev <!nocheck>,
+ librust-lazy-static-1+default-dev (>= 1.4-~~) <!nocheck>,
+ librust-nix-0.26+default-dev (>= 0.26.1-~~) <!nocheck>,
+ librust-openssl-0.10+default-dev <!nocheck>,
+ librust-proxmox-auth-api-0.3+api-types-dev <!nocheck>,
+ librust-proxmox-auth-api-0.3+default-dev <!nocheck>,
+ librust-proxmox-lang-1+default-dev (>= 1.1-~~) <!nocheck>,
+ librust-proxmox-server-config-0.1+default-dev <!nocheck>,
+ librust-proxmox-sys-0.5+default-dev <!nocheck>,
+ librust-proxmox-time-1+default-dev (>= 1.1.4-~~) <!nocheck>
+Maintainer: Proxmox Support Team <support@proxmox.com>
+Standards-Version: 4.6.1
+Vcs-Git: git://git.proxmox.com/git/proxmox.git
+Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
+X-Cargo-Crate: proxmox-cert-management
+Rules-Requires-Root: no
+
+Package: librust-proxmox-cert-management-dev
+Architecture: any
+Multi-Arch: same
+Depends:
+ ${misc:Depends},
+ librust-anyhow-1+default-dev,
+ librust-base64-0.13+default-dev,
+ librust-lazy-static-1+default-dev (>= 1.4-~~),
+ librust-nix-0.26+default-dev (>= 0.26.1-~~),
+ librust-openssl-0.10+default-dev,
+ librust-proxmox-auth-api-0.3+api-types-dev,
+ librust-proxmox-auth-api-0.3+default-dev,
+ librust-proxmox-lang-1+default-dev (>= 1.1-~~),
+ librust-proxmox-server-config-0.1+default-dev,
+ librust-proxmox-sys-0.5+default-dev,
+ librust-proxmox-time-1+default-dev (>= 1.1.4-~~)
+Provides:
+ librust-proxmox-cert-management+default-dev (= ${binary:Version}),
+ librust-proxmox-cert-management-0-dev (= ${binary:Version}),
+ librust-proxmox-cert-management-0+default-dev (= ${binary:Version}),
+ librust-proxmox-cert-management-0.1-dev (= ${binary:Version}),
+ librust-proxmox-cert-management-0.1+default-dev (= ${binary:Version}),
+ librust-proxmox-cert-management-0.1.0-dev (= ${binary:Version}),
+ librust-proxmox-cert-management-0.1.0+default-dev (= ${binary:Version})
+Description: Certificate management and utilities - Rust source code
+ This package contains the source for the Rust proxmox-cert-management crate,
+ packaged by debcargo for use with cargo and dh-cargo.
diff --git a/proxmox-cert-management/debian/copyright b/proxmox-cert-management/debian/copyright
new file mode 100644
index 0000000..0d9eab3
--- /dev/null
+++ b/proxmox-cert-management/debian/copyright
@@ -0,0 +1,18 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files:
+ *
+Copyright: 2019 - 2023 Proxmox Server Solutions GmbH <support@proxmox.com>
+License: AGPL-3.0-or-later
+ 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 <https://www.gnu.org/licenses/>.
diff --git a/proxmox-cert-management/debian/debcargo.toml b/proxmox-cert-management/debian/debcargo.toml
new file mode 100644
index 0000000..b7864cd
--- /dev/null
+++ b/proxmox-cert-management/debian/debcargo.toml
@@ -0,0 +1,7 @@
+overlay = "."
+crate_src_path = ".."
+maintainer = "Proxmox Support Team <support@proxmox.com>"
+
+[source]
+vcs_git = "git://git.proxmox.com/git/proxmox.git"
+vcs_browser = "https://git.proxmox.com/?p=proxmox.git"
diff --git a/proxmox-cert-management/src/lib.rs b/proxmox-cert-management/src/lib.rs
new file mode 100644
index 0000000..dd327f7
--- /dev/null
+++ b/proxmox-cert-management/src/lib.rs
@@ -0,0 +1,182 @@
+use anyhow::{bail, format_err, Error};
+use lazy_static::lazy_static;
+use openssl::pkey::{PKey, Private, Public};
+use openssl::rsa::Rsa;
+use openssl::sha;
+
+use proxmox_auth_api::types::Userid;
+use proxmox_lang::try_block;
+use proxmox_server_config::get_server_config;
+use proxmox_sys::fs::{file_get_contents, replace_file, CreateOptions};
+
+fn compute_csrf_secret_digest(timestamp: i64, secret: &[u8], userid: &Userid) -> String {
+    let mut hasher = sha::Sha256::new();
+    let data = format!("{:08X}:{}:", timestamp, userid);
+    hasher.update(data.as_bytes());
+    hasher.update(secret);
+
+    base64::encode_config(hasher.finish(), base64::STANDARD_NO_PAD)
+}
+
+pub fn assemble_csrf_prevention_token(secret: &[u8], userid: &Userid) -> String {
+    let epoch = proxmox_time::epoch_i64();
+
+    let digest = compute_csrf_secret_digest(epoch, secret, userid);
+
+    format!("{:08X}:{}", epoch, digest)
+}
+
+pub fn verify_csrf_prevention_token(
+    secret: &[u8],
+    userid: &Userid,
+    token: &str,
+    min_age: i64,
+    max_age: i64,
+) -> Result<i64, Error> {
+    use std::collections::VecDeque;
+
+    let mut parts: VecDeque<&str> = token.split(':').collect();
+
+    try_block!({
+        if parts.len() != 2 {
+            bail!("format error - wrong number of parts.");
+        }
+
+        let timestamp = parts.pop_front().unwrap();
+        let sig = parts.pop_front().unwrap();
+
+        let ttime = i64::from_str_radix(timestamp, 16)
+            .map_err(|err| format_err!("timestamp format error - {}", err))?;
+
+        let digest = compute_csrf_secret_digest(ttime, secret, userid);
+
+        if digest != sig {
+            bail!("invalid signature.");
+        }
+
+        let now = proxmox_time::epoch_i64();
+
+        let age = now - ttime;
+        if age < min_age {
+            bail!("timestamp newer than expected.");
+        }
+
+        if age > max_age {
+            bail!("timestamp too old.");
+        }
+
+        Ok(age)
+    })
+    .map_err(|err| format_err!("invalid csrf token - {}", err))
+}
+
+pub fn generate_csrf_key() -> Result<(), Error> {
+    let server_config = get_server_config()?;
+    let path = server_config.config_dir().join("csrf.key");
+
+    if path.exists() {
+        return Ok(());
+    }
+
+    let rsa = Rsa::generate(2048).unwrap();
+
+    let pem = rsa.private_key_to_pem()?;
+
+    use nix::sys::stat::Mode;
+
+    replace_file(
+        &path,
+        &pem,
+        CreateOptions::new()
+            .perm(Mode::from_bits_truncate(0o0640))
+            .owner(server_config.privileged_user().uid)
+            .group(server_config.user().gid),
+        true,
+    )?;
+
+    Ok(())
+}
+
+pub fn generate_auth_key() -> Result<(), Error> {
+    let server_config = get_server_config()?;
+    let priv_path = server_config.config_dir().join("authkey.key");
+
+    let mut public_path = priv_path.clone();
+    public_path.set_extension("pub");
+
+    if priv_path.exists() && public_path.exists() {
+        return Ok(());
+    }
+
+    let rsa = Rsa::generate(4096).unwrap();
+
+    let priv_pem = rsa.private_key_to_pem()?;
+
+    use nix::sys::stat::Mode;
+
+    replace_file(
+        &priv_path,
+        &priv_pem,
+        CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)),
+        true,
+    )?;
+
+    let public_pem = rsa.public_key_to_pem()?;
+
+    replace_file(
+        &public_path,
+        &public_pem,
+        CreateOptions::new()
+            .perm(Mode::from_bits_truncate(0o0640))
+            .owner(server_config.privileged_user().uid)
+            .group(server_config.user().gid),
+        true,
+    )?;
+
+    Ok(())
+}
+
+pub fn csrf_secret() -> &'static [u8] {
+    lazy_static! {
+        static ref SECRET: Vec<u8> = {
+            let dir = get_server_config().unwrap().config_dir().join("csrf.key");
+            file_get_contents(dir).unwrap()
+        };
+    }
+
+    &SECRET
+}
+
+fn load_public_auth_key() -> Result<PKey<Public>, Error> {
+    let pem_path = get_server_config()?.config_dir().join("authkey.pub");
+    let pem = file_get_contents(pem_path)?;
+    let rsa = Rsa::public_key_from_pem(&pem)?;
+    let key = PKey::from_rsa(rsa)?;
+
+    Ok(key)
+}
+
+pub fn public_auth_key() -> &'static PKey<Public> {
+    lazy_static! {
+        static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
+    }
+
+    &KEY
+}
+
+fn load_private_auth_key() -> Result<PKey<Private>, Error> {
+    let pem_path = get_server_config()?.config_dir().join("authkey.key");
+    let pem = file_get_contents(pem_path)?;
+    let rsa = Rsa::private_key_from_pem(&pem)?;
+    let key = PKey::from_rsa(rsa)?;
+
+    Ok(key)
+}
+
+pub fn private_auth_key() -> &'static PKey<Private> {
+    lazy_static! {
+        static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
+    }
+
+    &KEY
+}
-- 
2.30.2





  parent reply	other threads:[~2023-10-18 10:39 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-18 10:39 [pbs-devel] [RFC proxmox/proxmox-backup] refactor common server code from pbs Dominik Csapak
2023-10-18 10:39 ` [pbs-devel] [RFC proxmox 1/3] new proxmox-server-config crate Dominik Csapak
2023-10-25 16:38   ` Thomas Lamprecht
2023-10-30 11:05     ` Dominik Csapak
2023-10-30 11:41       ` Thomas Lamprecht
2023-10-18 10:39 ` [pbs-devel] [RFC proxmox 2/3] new proxmox-jobstate crate Dominik Csapak
2023-10-18 10:39 ` Dominik Csapak [this message]
2023-10-18 10:39 ` [pbs-devel] [RFC proxmox-backup 1/1] use proxmox_jobstate and proxmox_cert_management crates Dominik Csapak
2023-10-24  8:17 ` [pbs-devel] [RFC proxmox/proxmox-backup] refactor common server code from pbs Lukas Wagner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231018103911.3798182-4-d.csapak@proxmox.com \
    --to=d.csapak@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal