2913 Commits

Author SHA1 Message Date
afc9de2f14 ALT: fix testing source.list samples
Now there's no need fo redudant blank line at the end of file
2025-06-17 23:25:49 +03:00
3370f971e5 ALT: fix Alt's repos type to List 2025-06-17 23:12:57 +03:00
009c35cc3e Merge remote-tracking branch 'upstream/master' 2025-06-17 23:08:00 +03:00
35c28da99f router: bump version to 3.2.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-06-16 13:13:24 +02:00
4b8065d9d5 router: ReSt synopsis: avoid transition at start of section
> Transitions separate other body elements. A transition should not
> begin or end a section or document, nor should two transitions be
> immediately adjacent.
-- https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#transitions

Never sphinx output an error for these. Unify the simple/nested cases
while at it and drop the initial one, the "usage" string is defined
above and will always be empty.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-06-16 13:13:24 +02:00
b95e3a6819 router: bump version to 3.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-06-11 15:57:11 +02:00
4bb0cbf742 router: add missing #[cfg(feature = "server")] on import
Otherwise this does not build with `default-features = false`

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-06-11 15:55:41 +02:00
5625354acc resource-scheduling: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:04:25 +02:00
e85ab755f4 resource-scheduling: inherit meat info, dependencies and buildsys from workspace
Like in the other crates from this workspaces

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:03:02 +02:00
96cba26c2d resource-scheduling: replace lazy_static crate with std lib's LazyLock
Might be also a LazyCell, as this is currently mainly used in Perl,
where we cannot really use threads anyway, but never say never and it
might be used in pure rust too someday; besides, it can be changed if
needed.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:26 +02:00
966f653e62 resource-scheduling: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:26 +02:00
75f8bc049f Merge remote-tracking branch 'proxmox-resource-scheduling/master'
This is a merge of git.proxmox.com/git/proxmox-resource-scheduling.git
but with moving the whole history to a subdirectory first using:
git filter-repo --to-subdirectory-filter proxmox-resource-scheduling

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:26 +02:00
003086adf2 buildsys: gather pbs-* crates too
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:26 +02:00
af508c0e8e buildsys: derive upload dist automatically
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
74eaa2dd41 bump version to 0.3.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
a361cab793 buildsys: add sbuild target for convenience
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
3ae85d2d5a buildsys: drop no-pre-clean flags for buildpackage invocation
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
7a7be8c38c buildsys: cleanup and expand clean target
also remove *.build files (generated by sbuild)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
f100cb1c2c bump version to 0.2.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
a08e84b838 pve static: add one to avoid boosting tiny relative differences
Only the relative difference for values between different alternatives
is relevant, meaning 0.002 vs 0.004 and 0.2 vs 0.4 will influence the
scoring in the same way. This is not really desirable, because values
closer to 1.0 indicate higher load and thus should influence the
scoring more than differences that are actually tiny, but big when
viewed as a relative difference.

To avoid the issue, simply add 1.0 to all values. Like that, 1.002 vs
1.004 will also be small when viewed as a relative difference.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
01d0da2694 bump version to 0.2.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
843edb67ff pve ha: fix typo in comment
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
02be4dabe4 pve ha: fix scoring issue when a node is overcommitted compared to others
When nodes have different stats, the sum of percentage values will be
different for different alternatives, so the linear average is enough.
But when nodes have the same stats, this is not the case, the sum will
be the same, thus the average won't influence the scoring. If there is
an already overcommitted node, all alternatives besides the already
overcommitted node would be scored the same.

To fix it, use the squares of percentages instead, where more evenly
distributed usage across nodes will lead to a smaller value and thus
better scoring.

It's not really necessary to divide by length or take the sqrt, but it
seemed nicer to have something that would give 1.0 if all inputs are
1.0.

Reported-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
9b4e3dcb04 bump version to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
201bd80fc6 Cargo.toml: remove lib section
as this is just the default anyway

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
4e875256ff comment on lack of optimization of array::from_fn
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
a2bbe17cc7 avoid some more allocations
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
e3abad14c0 avoid extra allocation in rank_alternatives
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
e5af38b400 shorten code
this now wouldn't cause the weigh() lines to exceed 100
chars ;-)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
4c7e3587c7 simplify weighing
We already use functions that take or return the full [T; N]
array, so let's just do the same here, it's a bit shorter
this way.

If we get worried about large values we should implement
versions with `&mut [T; N]` consistently for all math
functions.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
88e8c3fb14 simplifications
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
3a45b25efc drop Topsis prefix of all the types in topsis::
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
8bf1761840 make ideal_alternatives a method of IdealAlternatives
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
8398e38693 reorder impl blocks
so they're next to their types

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
021c0dcfa7 add criteria_struct helper macro
This enforces the order in the Criteria and its From into
the Matrix entry.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
53fa2b86af add Debian packaging
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
2b325aa5c7 add pve_static module
Models usage of guests and nodes, and allows scoring nodes on which to
start a new service via TOPSIS. For this scoring, each node in turn is
considered as if the service was already running on it.

CPU and memory usage are used as criteria, with memory being weighted
much more, because it's a truly limited resource. For both, CPU and
memory, highest usage among nodes (weighted more, as ideally no node
should be overcommited) and average usage of all nodes (to still be
able to distinguish in case there already is a more highly commited
node) are considered.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
d23de546cf initial commit
Implement the TOPSIS[0] algorithm to score multi-valued alternatives
according to a given set of weighted criteria.

The number of alternatives cannot be known at compile time, but the
number of criteria should be (a given module using the topsis module
should have one (or more) fixed sets of criteria). Therefore, the
TopsisMatrix is implemented as a Vec of N_CRITERIA-sized arrays.

Compared to the description in [0] the weighing of the matrix
according to the weights of the criteria only happens during distance
calculation to the idealized alternatives. It turned out more natural
like that, because the matrix doesn't need to be mutable.

[0]: https://en.wikipedia.org/wiki/TOPSIS

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-31 16:02:25 +02:00
956cd368bb pbs-api-types: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:57:03 +02:00
9bc9b1918d pbs-abi-types: do not declare source as native
it actually would be, it's our code through and through, but we used
the revision for meta updates or if we borked a bump in the past, so
lets keep it for now like (almost) all other librust-* source code
packages from this workspace here.
If we switch, we should switch them all at once for consistency.
And FWIW, this is really not final in any direction, we could switch
back and forth as much as we want as long as something gets bumped,
not that we should ;-)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:57:03 +02:00
5602b42b2e pbs-api-types: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:57:03 +02:00
160506ea38 access-control: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:46:34 +02:00
f7baf3e2a5 access-control: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:46:02 +02:00
2dcdb435ec openid: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:42:15 +02:00
4a4c611c3c openid: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:36:55 +02:00
2b21ed6747 client: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:31:45 +02:00
cc39038ed9 client: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:31:11 +02:00
80f76ec62f apt: bump version to 0.99.0-1
Avoid using a 1.0.0 version as the API of this crate might change a
bit soonish.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
d2269e53f5 apt: test: add unified flag to proposed diff command
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
67dceb8411 apt: stop-gap update repo output for trixie
This is not really nicely done, but this crate caused me much more
friction and time required to get it bootstrap than the rest combined,
so it really needs to continue for now, even if still being far from
ideal.

I hinted a few things in a bigger FIXME comment in the tests, but
probably not complete and to be fair, it might not be easy to improve
this substantially, but certainly also not impossible; in short, repos
should be much simpler to add centrally, without having to sprinkle
stuff all over the place, the RepoHandle type might be avoidable, and
if then it really should go, and the test system should be simpler to
edit and shouldn't need any change at all when adding a new distro, or
building for a new one, besides maybe a coverage test to ensure that
all existing distro have some tests.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
053cbb1c1e apt: file serialization: inline some variables into format strings
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
5a3507c4e3 apt: avoid extra blank lines in sources output
While a small thing, this often points to an issue where newline
addition was handled by a component which cannot do a proper job.

Like here, the newlines where added in a function that serializes a
single repo entry, it simply has no idea in which context and as such
it really cannot decide if a separating newline after this entry is
warranted or not. So rather move that handling to the place where we
serialize whole files, as there we actually got the required context
to handle this correctly.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
1c63f379f6 apt: make test repos separator consistent with other repos
PDM did this right from the beginning, the others will change this for
Trixie.

If this should be fully correct it would need to have the suite passed
as parameter, as is we can only return the default for the one for
which the crate release is currently build for, this is a bit of an
API limitation.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
4ed946b99c apt: standard repos info: inline product variable into format strings
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
48ab96212e apt: output standard repos as in deb822 sources format for Trixie and newer
This was not tested in practice and might need follow ups, I enable it
nonetheless as it should better ensure that we do not forget and thus
avoid a nosiy warning by apt.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
f36fa518a0 apt: drop the Ceph repos for releases not supported in Trixie
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
e2305b8f8e apt-api-types: bump version to 2.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
6337701740 apt-api-types: drop repo entries for Ceph Quiny and Ceph Reef
Those will not be supported anymore by the future Debian Trixie based
Proxmox VE release.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
c90b221355 buildsys: test all features when building binary package
debcargo only tests with default features due to *potential* build
conflict between optional features. While that certainly is a thing,
it's not for our crates here (currently).

For the broader ecosystem this probably could be improved in debcargo,
either looking if there's info available to improve coverage
automatically or by providing an opt-out config option for specific
feature combos–just my relatively uninformed opinion though, albeit
I'm very certain the status quo is improvable.

Anyhow, until such an improvement manifests, lets just add a simple
`cargo test` to the build.sh script, which is used for all common
makefile targets like the ${pkg}-deb ones. The downside here is that
it does not "seeps" into the source package, so if one, e.g., uses
sbuild they still won't profit from this increased coverage; but as
debcargo creates the build-dependency field of d/control with the
reduced feature set, we cannot just add the full cargo test call to
d/rules.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-30 18:24:14 +02:00
972e0844dc sendmail: fix test failures outside CET/CEST timezones
The `Date` header in the generated email includes a timestamp with a
time zone offset (RFC 2822 format). This causes test failures when the
host system's time zone differs from CET/CEST, as the expected and
actual outputs no longer match.

As a workaround, the `Date` header is excluded from comparisons between
the generated and reference emails.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20250526134211.278900-1-l.wagner@proxmox.com
2025-05-26 17:17:13 +02:00
a73841292a notify: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:37 +02:00
58696c1527 notify: remove legacy filters and groups
The notification system as we know it know underwent a serious overhaul
shortly before its initial release in PVE one year ago. Before the
overhaul, there were filters (the 'inverse' of a matchers as we know
them today) and groups (allowed one to send a notification to
multiple targets at once, they were replaced by being able to select
multiple targets in a matcher).

Unfortunately, this previous iteration of the notification was already
merged and packaged in the pvetest repository, resulting in an unknown
number of systems which may have already been using filters and groups.
To avoid breaking these systems when updating to the overhauled
notification system, we've kept the section config type
for both config sections. In anticipation of a future removal of these
section types, we proactively removed these entries when loading and
saving the notification config file.

With the upcoming release of PVE 9 and PBS 4 we can drop the
schema. The upgrade check scripts (e.g. pve8to9) will
include checks to make sure that no legacy config sections are present
any more.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20250526114138.204228-1-l.wagner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
0161d1e36a notify: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
a5caa6e125 notify: update mail-parser dependency to 0.11
Thanks to the regression tests introduced in the previous commit we can
be certain that everything works as it should.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
3fc1ee4621 notify: smtp: add test for building forwarded messages
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
6d28188a3b notify: smtp: move building of forwarded mails to separate function
This allows us to write some tests for it.

No behavioral changes intended.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
b7209ed23b notify: stop using deprecated functions from handlebars
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20250523133158.306128-2-l.wagner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
8c5d7d0c85 notify: migrate to handlebars 5
No major changes were needed, just some minor changes on how we have to
handle errors in helpers.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20250523133158.306128-1-l.wagner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
6fdd2353d3 notify: switch to proxmox-base64
This also fixes any warnings that were shown during compilation because
we were using the deprecated `base64::encode/decode` functions.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20250526095220.132201-1-l.wagner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-26 15:55:16 +02:00
f37374475b login: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 15:09:13 +02:00
91ea79041a login: switch to proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 15:09:13 +02:00
1e6ebdb845 login: use webauthn-rs-proto 0.5
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 15:09:13 +02:00
9f7e5b0714 fix auth-api dependeny version in workspace Cargo.toml
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 15:01:10 +02:00
125cc32b77 auth-api: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 14:53:40 +02:00
c0e41aef34 auth-api: switch to proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 14:53:40 +02:00
3ee2c779f4 tfa: bump version to 6.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 14:53:40 +02:00
6180502527 tfa: update to webauthn-rs 0.5
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 14:53:40 +02:00
894c7bd856 tfa: move webauthn-rs deps into the crate
We generally don't want anything else in the workspace to depend on
the webauthn-rs crate directly.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 14:53:40 +02:00
647114b7dc tfa: switch to proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-23 14:53:39 +02:00
e869c0f24e apt: add future release codenames for Forky (14) and Duke (15)
See:

- https://wiki.debian.org/DebianForky
- https://wiki.debian.org/DebianDuke

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-23 14:43:23 +02:00
1cecdeef5c apt: add comment for how the code name test failing should be handled
Add some basic non-exhaustive guidelines–this cannot be complete
without a crystal ball–as otherwise a dev might be really unsure about
what the expected course of action here is.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-23 14:43:23 +02:00
9573fdd9a4 apt: update expected codename for build host in tests
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-23 14:43:23 +02:00
d99f091a9a apt: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-23 14:43:23 +02:00
01894fd657 rest-server: make minimal example compile again
This was quite definitively applied due to debcargo not running the
tests with all features enabled.
While that indeed cannot be done in general due to potential
conflicting features where that itself will result in compiler errors,
it could be enabled as opt-out, or with some settings for feature
combinations to skip, but I digress.

Fixes: 48fc0a2a ("http: use Watcher of GracefulShutdown to avoid races")
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-23 14:43:23 +02:00
ace0f43bae Cargo.toml: housekeeping
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-23 11:03:15 +02:00
f5d7b9b4df openid: update to openidconnect 4
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-23 11:02:48 +02:00
ef48ea5971 openid: update to ureq 3/http 1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-23 11:02:34 +02:00
3d8d38799e time: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 17:02:38 +02:00
5f641e5a99 subscription: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:57:35 +02:00
c0d81269f2 subscription: switch to proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:57:04 +02:00
4ea106bee2 dns-api: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:54:33 +02:00
00c86fa998 network-api: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:52:14 +02:00
0d7cde29e7 network-api: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:51:25 +02:00
4bac2db195 network-api: update to nix 0.29
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:51:25 +02:00
3ccba3065a ldap: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:45:39 +02:00
beaadf89ea acme-api: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
1e56e57019 acme-api: switch to proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
521ebf5bf0 serde: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
e2010ed80f serde: fixup string_as_base64url_nopad mapping
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
a5015e9684 base64: bump version to 1.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
e8db97ca11 base64: add decode_to_vec variants
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
3b25bc05d5 base64: add string-specific deserializers and modules
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
9dd397f84e rest-server: bump version to 1.0.0-2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
c54d689db2 http-error: bump version to 1.0.0-2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
8842d8c7ad router: bump version to 3.2.0-2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
6b4de64dc5 acme: bump version to 1.0.0-2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
8a8fe1e830 acme: use workspace dependency for http
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
c6830856f5 metrics: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
fe85188161 http: bump version to 1.0.0-2
Since we do not consider trixie crates to be *released* yet, but want
updates to work via apt for internal trixie development, this is a
revision bump.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
48fc0a2a07 http: use Watcher of GracefulShutdown to avoid races
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
891a116520 workspace: update hyper-util dependency to 0.1.12
We need the GracefulShutdown's `.watcher()` method so we can get rid
of the `Arc<>` around it so we can *actually* call `.shutdown()` which
*consumes* for some reason...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
ecd2cf941b http: cleanup unnecessary match statement
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
458b39d5a4 proxmox-acme-api: update to hyper 1.0
since this hyper-based server is not related to the rest of the stack
and has very simple requirements, it is implemented using just
hyper(-util) and not proxmox-http.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
9b12964183 proxmox-auth-api: update to hyper 1.0
and switch to proxmox-http's Body type

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
0633f6f594 proxmox-rest-server: fix and extend example
enabling logging while we are at it (useful when debugging things like
the graceful shutdown implementation).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
abada4eb46 proxmox-rest-server: update to hyper 1.0
and switch to proxmox-http's Body implementation.

hyper now has a special (opaque) Body implementation called Incoming
which is used for incoming request bodies on the server side, and
incoming response bodies on the client side, so our API handler's now
consume an instance of this type.

the Accept trait previously offered by hyper is gone in 1.0, and needs
to be replaced with an accept loop. our corresponding Acceptor
implementations have been dropped as well.

hyper now has its own Service and async Read/Write traits, but helpfully
provides wrappers for tower's Service and tokio's AsyncRead/AsyncWrite
variants.

graceful shutdown handling is now exposed differently on the hyper side,
and to allow usage with upgradable connections the variant form
hyper-util needs to be used, as the one straight from hyper doesn't
support it (yet).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
a30f1b09a4 proxmox-router: update to hyper 1.0
and switch to proxmox_http's body. hyper now has a special (opaque) Body
implementation called Incoming which is used for incoming request bodies
on the server side, and incoming response bodies on the client side, so
our API handler's now consume an instance of this type.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
32710f27ca acme: switch to http/hyper 1.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
7ddfdf274e metrics: update to hyper/http 1.0
and use new Body from proxmox-http

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
99fa15678a client: switch to hyper/http 1.0
and adapt to the corresponding proxmox-http changes.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
584c8d5dde proxmox-login: switch to http 1.x
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
c514c34693 openid: use http 0.2 to avoid openidconnect update
to avoid too much churn right now

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
c5f68f2010 http: websocket: update to http/hyper 1
use the new Empty body type, since Body is now a trait and no longer a
struct containing different body types.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
f0a797a4e4 http: adapt simple client to hyper 1.x
the main change requiring adaptations here is that hyper no longer
provides a Body struct, but switched to a trait, so we use our own Body
type introduced by the previous commit.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
47df48597f http: add Body implementation
hyper/http 1.0 now only have a Body trait and some implementations for
specific use cases. following reqwest's lead (and copying some parts of
its implementation), implement our own Body struct for the two common
use cases:
- a body instance containing the full body data as Bytes
- a streaming body instance

together with the most common helper methods (empty body, convert, wrap
existing stream as body) this should make the rest of the upgrade fairly
straight-forward.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
[WB: merge with previously included http_1 dependency]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 16:39:29 +02:00
d87b391f57 http: adapt connector to hyper 1.x
by switching to tower's Service and wrapping in TokioIo as needed. hyper
now uses their own Service type to not expose tower in their public API,
and their own Async IO traits, but they provide wrappers to not require
too many changes for crates like ours here that already used hyper 0.14.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
603331ef40 http: adapt MaybeTlsStream to hyper 1.x
using the legacy client from hyper_util, which is the replacement for
the pre 1.0 Client from hyper itself

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
cfc530de7f http: rate-limited-stream: update to hyper/http 1.0
using the legacy client from hyper_util, which is the replacement for
the pre 1.0 Client from hyper itself

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
2b5fc31ef5 http: order feature values
put optional dependencies first, followed by features on external
crates, followed by internal features.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-05-22 16:39:29 +02:00
d26239c1cf update proxmox-acme workspace dependency
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-22 09:45:30 +02:00
bcc3278737 apt-api-types: bump version to 1.1.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 08:54:07 +02:00
aba74124b6 apt-api-types: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 08:49:06 +02:00
ea050ff46d rest-server: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 07:20:15 +02:00
a810c94df1 rest-server: update to newer proxmox-sys and nix
This slightly increases IO safety.

For the connection peeking we need to keep the raw FD handling, but
move the use statements to a more narrow scope so that using those
low-level dangerous traits won't proliferate to easily.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 07:17:45 +02:00
52b0724c1c rest-server: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 07:17:45 +02:00
0695308d28 workspace: update handlebars to version 5
no adaptions on use sites yet, this is bootstrapping.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 07:17:45 +02:00
0386c37b1a workspace: fixup local version of proxmox-log and proxmox-product-config crates
I will never not froget this today...

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-22 07:17:45 +02:00
4a70ad5666 log: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 18:02:45 +02:00
ca2487214a log: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 18:01:51 +02:00
03ee036351 rrd-api-types: bump version to 1.1.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:49:38 +02:00
aee1ab42c4 rrd-api-types: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:47:43 +02:00
253a96077d rrd: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:27:42 +02:00
f056e60c81 rrd: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:27:42 +02:00
227483a082 workspace: fixup local version of proxmox-human-byte crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:27:42 +02:00
b79913168a syslog-api: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:21:09 +02:00
8cc984d838 syslog-api: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 17:20:16 +02:00
000432028d human-byte: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 16:12:55 +02:00
6fd33579f9 human-byte: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 16:12:55 +02:00
a0088a9e27 acme: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 15:45:32 +02:00
b140c5428f acme: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 15:43:32 +02:00
5720ec0060 acme: update to ureq 3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 15:43:32 +02:00
1eeb876f1a acme: use proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 15:43:32 +02:00
343ce512ac http: rebuild version 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 15:17:39 +02:00
34613ebf00 http: add missing dep:http to client feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 15:16:12 +02:00
d42d5038bc product-config: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 15:08:02 +02:00
f522950cdd product-config: update to newer nix, proxmox-sys crates from workspace
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 15:06:20 +02:00
a1766995b5 sortable-macro: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 15:00:14 +02:00
c273e86e0c simple config: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 14:55:25 +02:00
4b69c36384 simple-config: refresh d/control for initial Debian Trixie versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 14:55:25 +02:00
c5086f48cc bump proxmox-http dep to 1.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 14:48:01 +02:00
79d460d17b http: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 14:46:30 +02:00
a0dc071ee1 http: update to ureq 3
Since ureq 3 is a complete rewrite and switched to using the http
crate, we now depend on http version 1.

We still carry around the 0.2 dependency in public interfaces as long
as we use the old hyper code and need to upgrade this crate again to
use *only* http 1.0 in the future.

For now, for the client-trait impl we just convert the http 1.0
Response to a http 0.2 Response.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 14:45:44 +02:00
a61db84612 proxmox-http: use proxmox-base64
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 14:45:44 +02:00
eb1116c1e3 shared-memory: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 12:48:21 +02:00
74689aed0b shared-memory: update to newer nix version from workspace
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 12:46:12 +02:00
d23c49fe82 shared-cache: bump to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 12:11:15 +02:00
de88fc9a48 daemon: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 12:03:33 +02:00
de06504d6a daemon: update to newer nix, proxmox-sys & proxmox-systemd from workspace
Mostly changes related to nix adopting (more) IO safety.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:51:36 +02:00
e5f06e71f8 sendmail: run rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:50:33 +02:00
f03e665103 sendmail: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:17:28 +02:00
c952c0adb4 sendmail: switch to proxmox-base64 crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:16:54 +02:00
af6bcb08a4 workspace: fixup local version of proxmox-compression and proxmox-systemd crates
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:11:30 +02:00
fb3e8ca768 systemd: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:06:01 +02:00
2a4a106614 sys: fixup changelog release
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 11:05:02 +02:00
4919e03d9b sys: bump version to 1.0.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 10:38:34 +02:00
bd53ba7c39 sys: update nix usage to newer 0.29.1 version from workspace
Mainly adapting to nix adopting more IO Safety by switching the mmap
module and set_tcp_keepalive to accept any AsFd.
Note that the mmap accepting that as value while set_tcp_keepalive
accepting that as reference is mirroring the signatures of the
underlying nix methods for consistency, but nix has no real reason for
this mix either I think.

Besides that most methods that can take a dirfd switched to wrapping
that in an Option, with the main reason being seemingly just because
some other methods already do that, i.e. nix sometimes tries for
consistency...

I also adopted NonNull for the data member in the mmap struct, as nix
switched to that and that type _should_ provide a bit of a safer API.

The newer nix version also updated it's bitflags dependency, which it
uses on lots of places. We mostly notice that in the unsafe
from_bits_unchecked method now being a safe from_bits_retain.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 10:38:34 +02:00
94ff59dff0 borrow, compression: update changelog to reality
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-21 10:09:53 +02:00
b7292443a1 worker-task: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-21 09:54:23 +02:00
82beb937ad borrow: bump version to 1.1.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 17:06:07 +02:00
851c3de28c serde: bump version to 1.0.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 16:53:06 +02:00
3d5b8e6d1f serde: mark source format as native
Was already uploaded that way in Bookworm and for these crates it
should be fine, as they _are_ native and even meta changes can do a
cargo version bump.

So continue this as sort of experiment, maybe we can switch over other
crates too depending on the experience here.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 16:53:06 +02:00
af6f503403 serde: replace base64 with saner proxmox-base64 API
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 16:53:06 +02:00
68a284ee3c client: move to proxmox_serde perl helpers
The perl helpers have been moved to proxmox_serde from proxmox_login,
so fix all occurences using the proxmox_login crate.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Link: https://lore.proxmox.com/20250513101459.122641-4-s.hanreich@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 16:53:06 +02:00
9509c5fb2a login: move parse module to proxmox-serde
Remove the parse module, that has been moved to proxmox-serde. Fix all
usages of the parse module in the process and add the new dependency.
No functional changes intended.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Link: https://lore.proxmox.com/pbs-devel/20250513101459.122641-3-s.hanreich@proxmox.com/
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 16:53:06 +02:00
31e4cd6bd2 serde: add parsing helpers for perl
Those have been moved from the proxmox-login crate. This crate seems
like a more natural place for hosting helpers to parse data coming
from perl via serde.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Link: https://lore.proxmox.com/20250513101459.122641-2-s.hanreich@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 16:53:06 +02:00
ccac94ec63 base64: bump to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-20 14:49:47 +02:00
908f503854 add proxmox-base64 crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-20 14:48:57 +02:00
47719bb583 compression: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 13:47:00 +02:00
e8359727d5 workspace: use zstd 0.13 from Trixie
Using crates not updated yet, will be done one by one.

Drop the bindgen feature, it's now always enabled and not a feature
anymore.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 13:47:00 +02:00
a62cc96541 workspace: actually update config-digest dependency to 1.0.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 12:41:01 +02:00
70bba250e1 workspace: update config-digest dependency to 1.0.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 12:37:39 +02:00
177bb21c50 Merge remote-tracking branch 'origin/master'
Was pushed without coordination.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 12:32:58 +02:00
04fa1610da config-digest: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-20 12:31:10 +02:00
8d01d27423 auth-api: remove ticket info in old create ticket endpoint
this should make the endpoint behave closer to how it behaved before
the HttpOnly changes. the `ticket_info` field is superfluous anyway,
as the response also includes the proper ticket in this case already.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-05-20 10:03:31 +02:00
a6f43cbef0 Merge remote-tracking branch 'upstream/master' 2025-05-14 14:51:10 +03:00
3f59c3f381 api-macro: bump version to 1.4.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:38:21 +02:00
8eac37edf5 router: bump version to 3.2.0-1
and enable nix' dir feature.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
454ad5dfdd router: update to signature changes in nix openat/fstatat
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
9820e1ca76 async: bump version to 0.5.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
b04e04b13d io: bump version to 1.2.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
4cf0bd3843 http-error: bump version to 1.0.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
61b5788a63 section-config: bump version to 3.1.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
11076aa817 lang: bump version to 1.5.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
997bcc2f9d schema: bump version to 4.1.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
f447ffd7e6 workspace: update time and uuid to current versions
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 22:34:49 +02:00
aced6d2b7d uuid: bump version to 1.1.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 21:14:43 +02:00
a79ccc1907 time: bump version to 2.1.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 21:14:43 +02:00
5d2c61a29e buildsys: switch upload target to trixie
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-05-12 21:14:43 +02:00
786795fc40 serde: also include forwarding to serde
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-07 13:04:11 +02:00
2aefe96dc0 serde: simplify forwarded implementations
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-07 12:52:31 +02:00
3b59323480 lang: redo constnamedbitmap without local_inner_macros
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 14:58:06 +02:00
e47fdf411b client: bump to 0.5.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 13:55:00 +02:00
07dcd73aca proxmox-client: add query builder
A big proportion of the consumers of this API use the `format!` macro to
define the base of the url, hence we use a `Cow<'_, str>` on the
constructor to prevent unnecessary allocations.

A `perl-api-path-builder` feature was added to satisfy the needs of
pve-api-types.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
2025-05-06 13:15:18 +02:00
981172dced access-control: bump to 0.2.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 11:50:50 +02:00
212bb7aeb7 acme-api: bump to 0.1.8-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 11:49:26 +02:00
b0757b7453 api-macro: bump to 1.3.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 11:47:15 +02:00
0a20420b85 notify: bump to 0.5.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 11:45:46 +02:00
43419da4e3 section-config: bump to 3.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 11:43:40 +02:00
5d8989badf section-config: make hash access parameters more flexible
This is how the stdlib's HashMap defines them and is a little more
flexible.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 10:59:19 +02:00
a5058be79a section-config: consistent path parameters, monomorphise
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-05-06 10:53:20 +02:00
a77ef4191a section-config: add lookup and convert_to_typed_array helpers
These helpers exist on the untyped SectionConfigData struct, but not on
the typed one. To facilitate the migration to the typed
SectionConfigData<T> add these as macros.

These can be used to simply lookup an item with a specific type by
passing the type, e.g.:

    lookup!(config, "root", UserSectionConfig::User)

or retrieve a vector with the sections that correspond to the passed
type, e.g.:

    convert_to_typed_array!(config, UserSectionConfig::ApiToken)

Also add some simple unit-tests to test them.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2025-05-06 10:49:29 +02:00
f3a738370f section-config: remove DerefMut and make underlying HashMap private
Currently to insert a SectionConfig-section correctly we need to insert
it into the HashMap and insert the key in the order vector as well. This
is not ensure the SectionConfig gets written in the same order as it was
read. By implementing DerefMut and making the underlying HashMap `pub`
we allow to insert sections in the HashMap without inserting the key
into the the order. This caused a whole range of bugs where sections
where inserted, but never written (because we iterate over the order).

To improve this, we add our own insert and remove functions that
automatically add and remove the keys from the order. Also delete the
DerefMut impl and make the underlying Vec and HashMap private.

Note that this is a breaking change.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2025-05-06 10:49:29 +02:00
1dca221140 section-config: make write_section_config parameter more generic
The underlying function which gets called by `write_section_config` also
takes a `impl AsRef<Path>` and in some cases we do pass a path, so
taking the same parameter is better.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2025-05-06 10:49:29 +02:00
f92f773aed run cargo fmt
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-24 09:10:40 +02:00
bc078b9f59 notify: clippy fixes
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2025-04-15 12:26:47 +02:00
08c28ddeaf router: format: add newline at the end
Without this newline running commands with missing arguments, e.g.
```
proxmox-backup-client backup
```

will print their usage string without a newline at the end, which is
costing me my sanity.

As per commit d872eb9d7e ("router: cli: rework newline handling for
doc and help output"), usage shouldn't contain trailing new lines and
this in principle should not print double new lines.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2025-04-15 07:57:17 +02:00
550ebbed7c bump proxmox-log to 0.2.9-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-09 16:41:37 +02:00
b01036f5cd log: set up custom priority mapping for tracing-journald
By default, the mapping between tracing/log error levels to
syslog priorities is as follows:
  error! -> Error
  warn! -> Warning
  info! -> Notice
  debug! -> Informational
  trace! -> Debug

Before using tracing-journald, we used `syslog` to write log messages
to the journal. That one has the following mapping:

  error! -> Error
  warn! -> Warning
  info! -> Informational
  debug! -> Debug
  trace! -> Debug

The changed mapping of info! log messages led to many messages being
written with the Notice priority, which are formatted bold when running
journalctl to view log messages.

This commit changes the mapping so that it is the same as with the
syslog crate.

Support for custom priority mappings was only introduced in
tracing-journald 0.3.1, so we have to bump the dependency
to that version.

Reported-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2025-04-09 16:38:40 +02:00
179a413fe8 bump proxmox-http to 0.9.5-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-09 14:06:06 +02:00
ca351ecf7e http: client: make https connector generic over resolver
Allow to instantiate a `HttpsConnector` not using the default
`getaddrinfo` based `GaiResolver` for domain name resolution, but
rather a custom resolver implementing the required traits.

The usecase for this is to swap out the DNS resolver for the
statically linked proxmox-backup-client binary, where the glibc
dependency is problematic because of possible ABI incompatibility.

However, set the generic type on `HttpsConnector` to default to
`GaiResolver` to limit inconvenience for implementations using it.

Adds tower-service as cargo workspace dependency and build dependency
to debian/control.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2025-04-09 13:33:44 +02:00
64b2f082f5 rest-server: mark version to 0.8.9-1 as released
it's just a source code package so not end user visible and no point
in rebumping.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-08 17:04:07 +02:00
92f6e7455e bump: rest-server: bump version to 0.8.9-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-08 16:23:14 +02:00
e042f447bd rest-serve: move max body size limit to a constant
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-08 16:16:48 +02:00
cf9e6c03a0 rest-server: increase max body size
Increase maximum POST request body size from 64 kiB to 512 kiB to
match the value used in pve-http-server [0]. This change addresses
potential limitations with the newly introduced consent-banner
feature, which can contain lots of text that could approach the
previous limits [1].

[0]: https://git.proxmox.com/?p=pve-http-server.git;a=commit;h=2650923a42c9ea357dc0e663a69294410190cc7c
[1]: https://lore.proxmox.com/pbs-devel/e0cfec76-5149-4d3d-80be-b96ae633e1ee@proxmox.com/

Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Link: https://lore.proxmox.com/20250408132626.381476-1-g.goller@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-08 16:16:48 +02:00
22ffd650c2 notify: bump version to 0.5.4-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-08 12:37:41 +02:00
684ffacdf9 fix #6143: notify: allow overriding notification templates
Previously, notification templates could be modified by the user, but
these were overwritten again with installing newer package versions of
pve-manager and proxmox-backup.

Now override templates can be created cluster-wide in the path
“/etc/{pve,proxmox-backup}/notification-templates/{namespace}”, which
are used with priority. The folder structure has to be created and
populated manually (e.g. /etc/pve/notification-templates/default).

If override templates are not existing or their rendering fails, the
vendor templates in
"/usr/share/{pve-manager,proxmox-backup}/templates/default/" are used.

Sequence: [override html -> vendor html ->] override txt -> vendor txt

An error is only returned if none of the template candidates could be
used. Using an override template gets not logged.

Signed-off-by: Alexander Zeidler <a.zeidler@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20250321133341.151340-1-a.zeidler@proxmox.com
2025-04-08 12:25:32 +02:00
0afeba4b67 sys: bump version to 0.6.7-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 20:55:58 +02:00
f980cdc661 sys: add MemAvailable to ProcFsMemInfo
To promote using this over the rather useless, or well very different,
MemFree.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 20:50:01 +02:00
a3381b6197 sys: add doc-comments for ProcFsMemInfo type
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 20:50:01 +02:00
58d6e8d492 sys: memory info: use MemAvailable from kernel to compute used memory
The old code was wrong and overestimated the memory used because it
did not take into account things like "SReclaimable", a part of slab
(in-kernel memory allocator) describing things like caches that can be
reclaimed, plus the memory for "Active(file)" and "Inactive(file)",
and other internal kernel things that even though small for each one,
can add up quickly.

Most of these metrics are exposed and could be included in the
calculation, but this will simply become obsolete in the future as the
kernel changes how it does things and how it calculates such available
memory, as it has done many times in the past.

To solve this problem for the long term, the MemAvailable field was
added to /proc/meminfo as of kernel 3.14. It describes "the amount of
memory available for a new workload without pushing the system into
swap". While it is only an estimate, it is as good as it gets, and
since it comes from the kernel, we can always assume that it is
correct for the currently booted kernel.

So, switch over to this metric for calculating the used memory by
subtracting MemAvailable from MemTotal.

Also adds a simple test case for the parser.

This commit is based on a patch from Dietmar [1].

[0]: https://git.kernel.org/torvalds/c/34e431b0ae398fc54ea69ff85ec700722c9da773
[1]: https://lore.proxmox.com/all/20250313114535.99912-2-dietmar@proxmox.com/

Originally-by: Dietmar Maurer <dietmar@proxmox.com>
 [TL: rewrite comments and commit message from scratch]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 20:49:00 +02:00
deb32a6c4a pbs-api-types: bump to 0.2.2
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-05 17:25:33 +02:00
573d4d149b pbs-api-types: add garbage collection cache capacity tuning option
Allows to adjust the capacity for the LRU cache used to keep track of
recently touched chunks during phase 1 of garbage collection.

Values are provided as multiples of 1024 cache entries, the default
value of 1024 * 1024  was chosen as tradeoff between runtime
improvements and memory usage [0]. The maximum of 8192 * 1024 was
chosen based on the linear regression from [1], resulting in about
8 * 80 MiB = 640 MiB of memory requirement, while allowing to keep
chunks which can reference about 32 TiB of data in case of 4 MiB
fixed size chunks.

[0] https://git.proxmox.com/?p=proxmox-backup.git;a=commit;h=03143eee0a59cf319be0052e139f7e20e124d572
[1] https://lore.proxmox.com/pbs-devel/fa3800dd-e812-4c9a-9d3d-2d8673e05355@proxmox.com/

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/pbs-devel/20250404130713.376630-1-c.ebner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-05 15:59:11 +02:00
88e4f4831a pbs-api-types: sync: add sync encrypted/verified snapshots only flags
Add optional sync job config options to allow to include only
encrypted and/or verified backup snapshots, excluding others from the
sync.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/pbs-devel/20250404132106.388829-2-c.ebner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-05 15:59:11 +02:00
969e1ad645 pbs-api-types: introduce proper types for PAM and PBS realms
Introduces two new, simple API types representing the built-in PAM and
PBS authentication realms.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
 [TL: adapt doc-comment for new default field like Shannon pointed out
 but with a slightly different text than she proposed]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-05 15:59:11 +02:00
b143160c12 fix #5379: pbs-api-types: add default field for all realm types
The field indicates whether the realm should be the default realm to
select in the login dialog.

Per definition, the field should only ever be set to `true` on exactly
one realm - up to the consumer/storage to ensure that.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
 [TL: adapt doc-comment for new default field like Shannon pointed out
  but with a slightly different text than she proposed and change
  regular comment to doc-comment.]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-05 15:59:08 +02:00
8468dd18d2 pbs-api-types: add option to set GC chunk cleanup atime cutoff
Add the `gc-atime-cutoff` option to the datastore tuning parameters.
This allows to specify the time after which the chunks are not
considered in use anymore if their atime has not been updated since
then.

The default is to keep chunks within the 24h 5m timespan (given no
active writers).

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2025-04-05 15:58:45 +02:00
0f658cf79b pbs-api-types: add garbage collection atime safety check flag
Add the `gc-atime-safety-check` flag to the datastore tuning
parameters. This flag allows to enable/disable a check to detect if
the atime update is honored by the filesystem backing the chunk store
during phase 1 of garbage collection and during datastore creation.

The default is to perform the check.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2025-04-05 15:58:39 +02:00
f1bcce2796 openid: adapt d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-04 15:51:23 +02:00
56763c1184 bump proxmox-openid to 0.10.4-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-04 15:41:08 +02:00
6f5fefecc0 fix #4234: openid: add library functions for optional userinfo endpoint
Signed-off-by: Thomas Skinner <thomas@atskinner.net>
FG: rebased
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-04 15:40:28 +02:00
1c25b76c5d fix #4411: openid: add library code for generic id token claim support
Signed-off-by: Thomas Skinner <thomas@atskinner.net>
Tested-by: Mira Limbeck <m.limbeck@proxmox.com>
Reviewed-by: Mira Limbeck <m.limbeck@proxmox.com>
2025-04-04 14:52:23 +02:00
c057adcfed pbs-api-types: bump to 0.2.1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-02 15:51:17 +02:00
6dd53a89b5 pbs-api-types: add REGENERATE_TOKEN_SCHEMA
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2025-04-02 15:01:35 +02:00
1fc5630c71 pbs api types: tape backup job: add worker threads option
Sometimes it's useful to give the user control over how much
parallelized the job is, so introduce a worker thread option.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2025-04-02 14:47:57 +02:00
9db7bc90f6 Merge remote-tracking branch 'upstream/master' 2025-03-28 15:28:55 +03:00
4097d3697d notify: gotify: use constant from http crate for 'Authorization' header
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2025-03-25 19:37:37 +01:00
6d4c115f05 notify: webhook: gotify: set Content-Length header
To quote from RFC 9110 [1]:

  A user agent SHOULD send Content-Length in a request when
  the method defines a meaning for enclosed content and it
  is not sending Transfer-Encoding. For example, a user agent
  normally sends Content-Length in a POST request even when
  the value is 0 (indicating empty content).
  A user agent SHOULD NOT send a Content-Length header field
  when the request message does not contain content and the
  method semantics do not anticipate such data.

It seemed like our HTTP client lib did not set the header
automatically, which is why we should do it manually.

While most services seemed to have worked fine without setting
the header, some Microsoft services seem to require it
to accept the webhook request [2].

[1] https://datatracker.ietf.org/doc/html/rfc9110#name-content-length
[2] https://forum.proxmox.com/threads/158827

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2025-03-25 19:37:37 +01:00
7abd2da759 pbs-api-types: acl: fix indentation error in macro
expand tabs to spaces

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-03-24 17:56:13 +01:00
ec8a3de133 sys: procfs: split read_meminfo into read and parse functions
So that we can write tests.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2025-03-20 18:42:05 +01:00
da8fdea632 rest-server: bump to 0.8.8-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-20 14:45:19 +01:00
10cf5ed7b4 rest-server: worker task: include context in state error message
Currently the anyhow error context of a given error is not included
in the error message, as `to_string` does use the default formatting
[0].

Include the error context, formatting it as single line as the
message is also shown to the users in e.g. the Proxmox Backup Severs
task state in the UI.

[0] https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2025-03-20 14:40:58 +01:00
25c08ad247 sys: add variable bindings for temporaries in unsafe blocks
These will produce an error in edition 2024 otherwise. The reason this
is needed is because the `unsafe` block has its own scope.

The bytes were defined inside of the let-mut block to preserve the
lifetime they had before this commit.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2025-03-19 12:32:42 +01:00
e06277ac7a log: bump to 0.2.8-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-19 12:19:43 +01:00
86a517d087 sys: bump to 0.6.6-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-19 12:19:43 +01:00
57eb5a36e9 sys, shared-memory: deny unsafe_op_in_unsafe_fn explicitly
can be removed in these and the other crates when switching to edition
2024

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-19 12:19:43 +01:00
d42810e3c1 log, rest-server: cargo fmt / formatting cleanups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-19 12:19:43 +01:00
3db442fb8f async: accommodate to edition 2024 changes to RPIT
Prevents the following error:

```
error[E0597]: `inner` does not live long enough
   --> proxmox-async/src/broadcast_future.rs:109:24
    |
107 |         inner: Arc<Mutex<BroadCastFutureBinding<T>>>,
    |         ----- binding `inner` declared here
108 |     ) -> impl Future<Output = Result<T, Error>> {
109 |         let mut data = inner.lock().unwrap();
    |                        ^^^^^ borrowed value does not live long enough
...
121 |         data.broadcast.listen()
    |         ----------------------- argument requires that `inner` is borrowed for `'static`
122 |     }
    |     - `inner` dropped here while still borrowed

error[E0597]: `data` does not live long enough
   --> proxmox-async/src/broadcast_future.rs:121:9
    |
109 |         let mut data = inner.lock().unwrap();
    |             -------- binding `data` declared here
...
121 |         data.broadcast.listen()
    |         ^^^^-------------------
    |         |
    |         borrowed value does not live long enough
    |         argument requires that `data` is borrowed for `'static`
122 |     }
    |     - `data` dropped here while still borrowed
```

The use<...> pattern was introduced in rust 1.82.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2025-03-19 12:19:33 +01:00
51c3a31115 mark blocks inside unsafe fns unsafe
In edition 2024 unsafe code inside unsafe functions has to be explicitly
marked as such.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2025-03-19 12:19:19 +01:00
9be42ea5ad daemon: set_var is now unsafe
In edition 2024 set_var is unsafe.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2025-03-19 12:19:19 +01:00
abd07ffcff mark extern C blocks as unsafe
This is required in edition 2024.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2025-03-19 12:19:19 +01:00
a75b97da76 log, rest-server: worker_task: add log_unfiltered
To write result message manually, bypassing tracing.

The workertasks currently get their status from parsing the log
messages in the task-log file. The problem is that if these messages are
filtered – which is now possible using the PBS_LOG env variable – some
workertasks will end up with a "stopped: unknown" status. This is not
desirable so write the message manually to the workertask file and
bypass tracing.

This way we are guaranteed that, regardless of the max logging level the
user sets, the final message (and status) is written.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2025-03-19 12:19:02 +01:00
c99308ecfc log: factor out NoWorkerTask filter
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-19 11:52:08 +01:00
6bdd07075d log: fix doctests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-19 11:51:53 +01:00
656fedb0c4 log: add layer for pve workertasks in perlmod crates
Add a layer that outputs messages to stderr in a specific format. In
PVE, stderr is rerouted to the tasklog if the we are within a
workertask. Therefore, ensure the stderr output is formatted
appropriately.

Reported-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2025-03-19 09:52:35 +01:00
f6269b800d log: introduce logging builder
Add a builder-like struct to compose a tracing logger using different
layers. Instead of having an init function per product/binary or
super-specific init functions that describe the logger, have a dynamic
builder. The builder improves the usability and makes the logging
initialization more self-explaining.

Suggested-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2025-03-19 09:52:35 +01:00
3e382fd29c auth-api: set content type header for the new HttpOnly ticket endpoint
otherwise some clients might struggle to interpret the body correctly

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-14 09:28:11 +01:00
b82e51f15a bump proxmox-router to 3.1.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-03-13 13:58:50 +01:00
e4bc435beb env_logger: bump to 0.11
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-03-13 13:55:29 +01:00
f32f48b119 router: cli: avoid unnecessary clones/allocation
The `new_args` Vec is directly passed to the other Vec's `.extend()`,
which takes an `IntoIterator` consuming it, so just pass the
intermediate `Iterator`.

The `rest` Vec owns its strings and we don't need it afterwards, so
similarly, we can consume it via `.extend()` instead of a manual
push(s.clone()) loop.

The .truncate(0) can just be .clear() - they are equivalent according
to their documentation.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-13 11:46:52 +01:00
586535e456 Merge remote-tracking branch 'upstream/master' 2025-03-07 14:06:22 +03:00
00c75c734d tree-wide: fix private intra doc links
a previous commit fixed up all intra doc links that were present on
public apis, this also fixes the links for private members.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-07 11:35:03 +01:00
9e27dfa8a5 Use 'alt-linux' feature by default 2025-03-07 05:44:25 +03:00
a9a7bbdabc auth-api: fix intra doc link for Empty
`Display` isn't used directly anymore, so fix up the intra doc link
here again.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 16:35:51 +01:00
5c7b1ab4ab tfa, auth-api: simplify and restyle Display implementation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-06 15:25:24 +01:00
4836cb5334 tree-wide: fix intra doc links
this fixes intra document links or rephrases the documentation in a
more appropriate way to remove all `broken_intra_doc_links` warnings.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
ccb34b33e2 api-macro: re-order ObjectSchema fields to be sorted
this panics when running `cargo test` otherwise, as the api macro
requires fields in `ObjectSchema`s to be sorted now.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
f0b23def30 router: fix nested doc test cases to match inteded output
commit 68b13965 (router: docs: add horizontal line before nested
command docs) broke the nested command group test case. this commit
adapts the expected output accordingly.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
4fc074b4ba network-api: ignore clippy lint about upper case acronyms
while the lint is correct about how these enum members should be
capitalized, the enum is marked as `pub` and all users of it would
need to adapt. so ignore the lint for now [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
730f908458 apt: ignore clippy lint about new having to return Self
while this is a reasonable convention to follow, in this case the new
function is part of a public trait and changing the signature would
force all users to adapt. so ignore the lint for now [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
8fc324ee73 apt: ignore clippy lint about using a slice reference instead of &Vec
while the function would be more useful as pointed out by the clippy
lint, it i currently `pub` and users of the function would need to
adapt to the change here. so ignore the lint for now.

[1]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
59898d0177 rest-server/router: ignore type complexity clippy lint
the `type_complexity` clippy lint [1] is intended to make the code
more legible in most cases. however, the lint triggers on a member of
a private enum, an example minimal rest server and a private static
variable here. so the benefits of declaring a new type that would
encapsulate this complexity is minimal. hence, ignore the warnings for
now.

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
f9dd576783 router: ignore clippy lint missing_transmute_annotations
the `ApiHandler`'s `PartialEq` implementation makes heavy use of
`transmute`. clippy wants the types to be explicitly stated here and
not inferred, to avoid potential undefined behaviour if an unexpected
type is inferred. however, the types that would be inferred here are
so complex, that the code would become illegible anyway, so ignore
this lint for now.

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#missing_transmute_annotations

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
dcc6eb9918 shared-memory: specify generic types for transmute
this annotates a `transmute` call with proper types to avoid possible
undefined behaviour, as suggested by clippy [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#missing_transmute_annotations

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
ab2d5c9777 acme/auth-api: add Default for types with un-parameterized new()
this fixes a clippy lint for types that have a `new()` function that
has no parameters [1]. this should allow using these types with
functions such as `unwrap_or_default()`.

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:25:24 +01:00
efc8556c27 auth-api/tfa: prefer Display over ToString/an inherent function
this fixes two clippy lints that check if either `ToString` or an
inherent `to_string()` function is implement [1, 2]. `Display`
provides `ToString` for free and, thus, is preferable.

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#/inherent_to_string
[2]:
https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:17:35 +01:00
d95a4f25e0 router: allow from_str on Confirmation that is not for FromStr
while usually this would improve ergonomics, in this case it isn't
clear whether all uses of `FromStr` would be considered valid here.
renaming the function would also make the type more confusing to use
as `from_str_with_default` also exists, so keep this for consistency.
this ignores a clippy lint [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:16:04 +01:00
92ecc301b6 sys: add truncate option to OpenOptions in test case
this resolves a clippy lint that checks for uses of `create()` without
`truncate()` [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_open_options

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:15:43 +01:00
946d95cfcd access-control/tfa: use ? instead of unnecessary match statements
this makes the code more concise and legible. fixes a clippy lint [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#question_mark

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:15:22 +01:00
a318fcedd3 tfa: remove needless as_bytes call
len() already returns the length in bytes, no need to call `as_bytes`
first. this fixes a clippy lint [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#needless_as_bytes

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:15:11 +01:00
0b9c1485c0 tfa: don't use block in conditions
this fixes a clippy style lint that does not allow blocks in
conditionals. moving the block out and the result into a temporary
variable should make this more legible [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_conditions

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:14:19 +01:00
d980c2229b tree-wide: remove clone calls on types that are Copy
this resolves a clippy lint that checks that `clone()` isn't called on
`Copy` types as that is unnecessary [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:12:16 +01:00
2c07729ff3 tree-wide: add parantheses to clarify precedence
this resolves a clippy lint that aims to improve legibility for people
unaware of rust's precendence rules [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#precedence

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:11:00 +01:00
2134657529 io: clippy fix: replace map() followed by any() with just any()
this fixes a clippy lint that complains about map invocations followed
by any invocations that are just checking for identity as this can be
replaced by just the any invocation alone [1].

[1]:
https://rust-lang.github.io/rust-clippy/master/index.html#map_all_any_identity

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-06 15:03:10 +01:00
b5e238613e auth-api: bump to 0.4.8-1
While *technically* breaking as it changes the method signature for
the `create_ticket` call to use a struct for its parameters, this is
only (supposed to be) used via its `CREATE_TICKET_API_METHOD` handler
to be passed to a router. Direct use of this does not make sense.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-04 16:03:17 +01:00
e73bc1509d rest-server: bump to 0.8.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-04 15:54:19 +01:00
3f6345021c router: bump to 3.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-04 15:54:19 +01:00
fda6fc9def client: bump to 0.5.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-04 15:54:19 +01:00
86336f6c88 login: bump to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-04 15:54:19 +01:00
986b465d48 time: bump to 2.0.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-03-04 15:54:19 +01:00
0c9ed7daa4 client: specify cookie names for authentication headers where possible
if the client knows the auth cookie's name, it now passes it on to the
relevant parts of `proxmox-login` so that the ticket is send the
correct cookie

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
bedf92c0df client: add compatibility with HttpOnly cookies
this should make it possible to use the proxmox-client crate outside
of context where HttpOnly cookies are handled for us. if a cookie
name is provided to a client, it tries to find a corresponding
`Set-Cookie` header in the login response and passes tries to parse
it as a ticket. that ticket is then passed on to proxmox-login like
any regular ticket.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
7dee2d7016 login: add functions to specify full cookie names
previously the name in which the ticket was send was derived by the
product abbreviation in the ticket itself. the assumption was that
authentication cookies would always have a name like this:
`<PRODUCT_ABBREVIATION>AuthCookie`.

this commit adds helpers that allow specifying the cookie's name by
users of this crate.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
f199b02a7a login: add helper to check whether a ticket is just informational
tickets that end in `::ticketinfo` are not properly signed and just
include information such as the timestamp when the ticket was created.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
867e890141 login: add TicketResult::HttpOnly member
this allows client to be aware that the ticket they manage is
informational only and that the real ticket should have been set via
a HttpOnly cookie.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
b28e98ca99 login: add helpers to pass cookie values when parsing login responses
depending on the context a client may or may not have access to
HttpOnly cookies. this change allows them to pass such values to
`proxmox-login` to take them into account when parsing login
responses.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
f137b5e528 login: make password optional when creating Login requests
in certain context (for example, the browser), no password needs to be
provided when using HttpOnly cookies as they are handle by said
context. so make renewing ticket with password optional and add a new
helper function that does not require a password.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
9c6d6b8d2a login: add optional field for ticket_info and make password optional
tickets created through the new HttpOnly ticket endpoint won't return
a ticket in the password field. so this field will be left empty.
hence make it optional.

the endpoint does return a ticket_info parameter, though, that
includes the information when a ticket needs to be refreshed. so add
a new optional field for that too.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
1b9def4736 auth-api: add logout method
adds a new endpoint that is useful when dealing with HttpOnly cookies
that cannot be removed by client-side javascript (and by extension
wasm) code. the logout handle simply removes the cookie that is used
for storing the current ticket. this works the same way as it does in
the front-end: by setting an expired cookie with the same name.

as cookies are now prefixed with `__Host-` by default, the cookie here
also needs to be `Secure` and have the same `Path` to not be rejected
by the browser before it can remove the old cookie.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
6a7f631709 auth-api: make regular ticket endpoint use the new types and handler
so we can re-use more code between the different ticket endpoints

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
8405154c6d auth-api: add endpoint for issuing tickets as HttpOnly tickets
this adds a new endpoint for requesting tickets. instead of returning
the ticket in the responses body, the ticket is set as a HttpOnly
cookie. this has a couple of advantages:

- the cookie cannot be stolen if an attacker downgrades the connection
  to http and injects malicious javascript (`HttpOnly`)
- we don't need to rely on the client to make sure that the cookie is
  only send in the appropriate context and only over https
  connections (`Secure`, `SameSite`).
- the cookie cannot be overwritten by other subdomains, insecure
  connections etc. (the default is to prefix them with `__Host-`)

this follows the best practice guide for secure cookies from MDN
[1]. we also set the cookies to expire when the ticket would so that
the browser removes the cookie once the ticket isn't valid anymore.

the endpoint still returns a ticket that only contains the
informational portions of the ticket but not a valid signature. this
is helpful to let clients know when to refresh the ticket by querying
this endpoint again. it still protects the cookie, though, as it
isn't a valid ticket by itself.

[1]: https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
acaffffaf9 auth-api: introduce new CreateTicket and CreateTickeReponse api types
these types are used for creating a ticket and responding to a new
ticket request.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
6f61b991a0 auth-api: check for new prefixed cookies as well
this makes sure that newly generated cookies that are prefixed with,
for example, `__Host-`, for security purposes, are correctly picked
up on. otherwise, the new cookies would not be able to yield proper
authentication.

currently this still falls back to insecure non-prefixed cookies. we
should deprecate them in the long-term and remove this fallback.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
b598e03287 auth-api: extend AuthContext with prefixed cookie name
this adds the function `prefixed_auth_cookie_name` to the
`AuthContext` trait. said function can be used by users of this crate
to modify the expected prefix of the auth cookie. most products
should be able to use the default of `__Host-` though, so this also
adds a default implementation.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
1f58e40f3f router/rest-server: add new AsyncHttpBodyParameters api handler type
this allows us to write api handlers that have access to a request's
headers and to create a low level response while being able to also
specify the parameter in the request's body. this is useful for
endpoints that should not use url parameters, but still need to
access/set specific headers.

previously, `AsyncHttp` did not allow for parameters that were marked
as non-optional to be passed in the body of a request.

as a side-effect, the body is parsed by the rest server to check for
parameters and consumed. so it cannot be passed on by the handler.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
b1ed231ed2 rest-server: borrow parts parameter in get_request_parameter
this function does not require ownership of the parts parameter, so we
can simply borrow it. this way we can pass the parameter on to a
handler that consumes them even when using `get_request_parameter`

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
316fe71133 time: add new epoch_to_http_date helper
this makes it easy to generate RFC9110 preferred HTTP Dates.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2025-03-04 15:53:29 +01:00
b6d1410151 Better add_repository_handle, proper GostCrypto 2025-02-23 08:59:23 +03:00
9c7993f1b4 Merge remote-tracking branch 'upstream/master' 2025-02-23 04:45:35 +03:00
d8128614d3 (ALT) Adapted proxmox-apt tests 2025-02-23 04:28:46 +03:00
93dd6ea520 (ALT) Proper default repo's checks and creation 2025-02-23 04:22:43 +03:00
6339b60fc6 (ALT) Better apt repos list checks 2025-02-22 07:54:58 +03:00
68b1396553 router: docs: add horizontal line before nested command docs
Lines before commanmd groups are missing (i.e. see proxmox-backup-client command
syntax docs)

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2025-02-21 10:05:46 +01:00
29c1ceefd5 pbs api types: relax influx organization and bucket length
Relax the length limit for organization and bucket name for InfluxDB
metric servers in Proxmox Backup Server.

Commit 57fa20406 ("pbs-api-types: add metrics api types") introduced
the api schema definition for InfluxDB metric servers, limiting the
name of the organization and the name of the bucket to a length of 3,
most likely as a result of reusing the same values as for the
corresponding config id schema restrictions.

This is however not enforced by the InfluxDB REST api [0, 1] and
stricter than what is defined in Proxmox VE [2].

Reported in the community forum [3].

[0] https://docs.influxdata.com/influxdb/v2/api/#operation/PostOrgs
[1] https://docs.influxdata.com/influxdb/v2/api/#operation/PostBuckets
[2] https://git.proxmox.com/?p=pve-manager.git;a=blob;f=PVE/Status/InfluxDB.pm;h=13a96711e766e2f1ea71424ed6d9d8ec8450504c;hb=HEAD#l24
[3] https://forum.proxmox.com/threads/162521/

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2025-02-20 16:11:50 +01:00
052d84ef37 schema: drop useless doc comment
(it's on the wrong function, and also it's wrong - std's str
comparison forwards to the byte comparison anyway since this is fine
for utf-8 if we're not doing anything natural-language-aware...)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-20 10:07:08 +01:00
ce54bd1b11 schema: verify object fields/variants are sorted and unique
This verifies *at compile time* that the properties and variants of
ObjectSchemas and OneOfSchemas are sorted.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 13:02:18 +01:00
59a9ddbf06 schema: add const-fn helper to compare strings
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 13:02:18 +01:00
e3286d3758 notify: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 13:02:00 +01:00
ad3657011e notify: bump to 0.5.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 13:00:56 +01:00
ef54afe65f api-macro: bump to 1.3.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 12:55:38 +01:00
a36f0f3bf9 api-macro: enums: sort OneOf variants
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 12:53:46 +01:00
d38ce91384 notify: fix dummy user schema property ordering
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-02-19 12:53:46 +01:00
0f1b84e93c sys: fs: derive Copy for CreateOptions
Pretty much all functions accepting `CreateOptions` take a value and not
a reference, so I've found myself using `.clone()` quite often in code
I've written recently.
The struct is only 24 bytes large (verified by a
`std::mem::size_of::<CreateOptions>()`), so it should be absolutely fine
to just derive Copy for it.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2025-01-29 18:09:24 +01:00
31ae72ba6a schema: property ReST docs: correctly separate nested list from parent
This fixes the reStructuredText (ReST) output for more complex formats
where we got, for example, a definition list of all properties of a
(section) configuration with one or more properties then having a
sub-format that is also rendered as a list.

Such nested lists are possible in ReST but need to be separated by a
blank line [0].

Without this we got quite a few warnings/errors from the Sphinx/ReST
buildsystem looking like:

> config/datastore/config.rst:19: ERROR: Unexpected indentation.
> config/datastore/config.rst:20: WARNING: Block quote ends without a blank line; unexpected unindent.

[0]: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#lists-and-quote-like-blocks

Reported-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-01-28 14:25:21 +01:00
c0641c6e13 schema: use inline named params in format macro calls
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-01-28 14:25:21 +01:00
703c2dee04 api-macro: mark parameter defaults as #[allow(dead_code)]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-28 10:56:28 +01:00
922c605d69 rest-server: Improved panic errors with formatted strings
Improved errors when panics occur and the panic message is a
formatted (not static) string. This worked already for &str literals,
but not for Strings.

Downcasting to both &str and String is also done by the Rust Standard
Library in the default panic handler. See:
b605c65b6e/library/std/src/panicking.rs (L777)

Signed-off-by: Laurențiu Leahu-Vlăducu <l.leahu-vladucu@proxmox.com>
2025-01-27 15:12:59 +01:00
f639695c87 pbs-api-types: add debian/control to git repository
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2025-01-22 12:33:33 +01:00
57b0477657 pbs-api-type: add as workspace member with correct debian files
Also changed the version to 0.2.0

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2025-01-22 11:57:00 +01:00
b7b00b5205 Merge remote-tracking branch 'prepare-pbs-api-types-for-move/prepare-pbs-api-types-for-move'
Move over the whole history of pbs-api-types from the proxmox-backup
git repo to the common workspace as we want to re-use that in PDM and
the yew UI components, and thus require a real source-code package for
this crate.

Choose this repo over proxmox-api-types to avoid the need to copy this
build-system over there, rather we want to also merge pve-api-types
from that repo into ours here in the future.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-01-22 11:55:09 +01:00
0477adaf88 client: fix code-style issue when formatting error
Fixes a style issue introduced by b31ab119 ("client: improve api error
message (avoid duplicate status code)"), as one always should use
inline variables for format template strings even if there are some
that cannot be used, e.g. as some method needs to be called on them
like here. The reason for this is to reduce the amount of
free-standing "{}" and parameters as that reduces the need to manually
match what "{}" resolves to which expression. While it is not that bad
for only two format variables it's still not winning us anything if we
don't do it and breaks consistency with newer code style.

Noticed as that commit made the line overly long, causing rustfmt
changing the line.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-01-21 09:22:08 +01:00
daff73bdd6 sys: use correct pointer type for mkdtemp return value
The libc function mkdtemp takes a C char pointer while we previously
cast our OSString buffer as i8 pointer, but that's not valid on
platforms like AArch64 (ARM), where char is equivalent with a u8.

Fix that by using the c_char type that was explicitly made to always
get the correct, platform-independent type for C chars when doing FFI.

This was reported by OJaksch on our Arch Linux User Repo (AUR) package
[0].

https://aur.archlinux.org/packages/proxmox-backup-client#comment-1006851

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-01-21 09:14:43 +01:00
5a7993beb0 update to proxmox-schema 4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 13:03:42 +01:00
4be2392d59 time-api: bump to 0.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:54:28 +01:00
e0bdcda6e6 syslog-api: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:53:28 +01:00
27c50bd037 rrd-api-types: bump to 1.0.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:52:15 +01:00
975f29bd7f network-api: bump to 0.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:51:19 +01:00
ce206f0a96 dns-api: bump to 0.1.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:50:26 +01:00
891413ffb7 access-control: bump to 0.2.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:49:23 +01:00
f01f934963 auth-api: bump to 0.4.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:47:08 +01:00
0807248c15 acme-api: bump to 0.1.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:46:13 +01:00
7e25958623 tfa: bump to 5.0.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:43:41 +01:00
40f0a0bdd3 subscription: bump to 0.5.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:42:10 +01:00
c84376e2c8 apt: bump to 0.11.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:40:14 +01:00
5382b8ce13 apt-api-types: bump to 1.0.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:38:33 +01:00
ca92328569 api-macro: bump to 1.3.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:36:57 +01:00
59d1ca87e5 rrd: bump to 0.4.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:36:56 +01:00
106aa95ced rest-server: bump to 0.8.6-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:35:43 +01:00
698b6782cd router: bump to 3.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:35:43 +01:00
69298c8d20 acme: bump to 0.5.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:28:06 +01:00
ed74a6a8a2 simple-config: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:26:42 +01:00
2782c72747 config-digest: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:25:33 +01:00
94b9f8db60 notify: bump to 0.5.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:25:31 +01:00
f2fefb7ffd section-config: bump to 2.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:20:00 +01:00
c7f4afb3fc human-byte: bump to 0.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:20:00 +01:00
e4f5179ae6 schema: bump to 4.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 12:07:49 +01:00
2e2edcf833 schema: add Schema::unwrap_*_schema_cloned
This way we can copy and modify a schema.

Eg. via the following ways:

    const FOO_SCHEMA: Schema = SOME_SCHEMA
        .unwrap_integer_schema_cloned()
        .description("Foo")
        .schema();

Note that for example there is currently no builder to set a
`default_key` for an `ObjectSchema` back to None, so one could do:

    const FOO_SCHEMA: Schema = const {
        let mut schema = SOME_SCHEMA.unwrap_object_schema_cloned();
        schema.default_key = None;
        schema.schema()
    };

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 11:56:57 +01:00
c040054827 schema: add 'description' builder methods to schema types
To cover the use case where we want to change only the description of
an existing type.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 11:07:08 +01:00
72c95c35bb schema: make schema types #[non_exhaustive]
This way extending them is not a breaking change anymore.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 10:56:58 +01:00
c794c42919 schema: drop deprecated code
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 10:56:58 +01:00
8d9cf97611 schema: doc: replace use of deprecated methods
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 10:56:58 +01:00
dc232f8bd4 schema: support PVE's "keyAlias" legacy property strings
Because the UI kept producing them...
And still does...

This is NOT meant to be used by anything other than generated legacy
code for PDM (pve-api-types) and only affects the serde based
deserializer. The "old" `schema.parse_property_string() -> Value`
method does not utilize this for now.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 10:56:58 +01:00
4378d44bcb notify: use builder for ObjectSchema
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-15 10:56:58 +01:00
3c34ef5573 section-config: use builder pattern for ObjectSchema
To prepare for a breaking change in proxmox-schema.
Since new fields in the schema constitute a breaking change, using the
builder methods will let this compile later as well.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-14 11:48:52 +01:00
c99a31c66f router: test: sort object schema properties
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-14 10:34:29 +01:00
be57fb0122 schema: updater: add blanked implementation for PropertyString
so that we can have a property:

```
foo: Option<PropertyString<Bar>>,
```

within a struct that derives `Updater`

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2025-01-14 10:19:54 +01:00
8c5fa1c562 schema: fix pointer/length confusion in str_slice_to_range
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-13 15:22:40 +01:00
62e105a34e api-macro: bump to 1.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-09 14:21:53 +01:00
dce9102163 api-macro: add json_schema!() macro
This allows using the json schema notation to generate `Schema`
expressions.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-09 14:19:42 +01:00
619c290cf8 client: bump to 0.5.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-09 14:16:03 +01:00
b31ab119bb client: improve api error message (avoid duplicate status code)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2025-01-09 12:02:28 +01:00
ddc154e5cd notify: add missing tracing::error macro import
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-01-07 10:20:37 +01:00
b74391cada daemon: bump version to 0.1.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-30 15:34:16 +01:00
1db49cb269 daemon: try to remove existing unix socket in bind directly
We tried this unconditionally on start-up in the PDM for the priv. API
daemon, but we actually only want to clean-up on fresh bind, not on
restoring the FD on daemon reload. Otherwise the unprivileged daemon
cannot connect to the privileged one anymore after the latter got
reloaded.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-30 15:30:10 +01:00
7648aabf42 daemon: output debug log when restoring FD passed through env
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-30 15:29:34 +01:00
cc4ee60452 apt: bump version to 0.11.6-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-19 12:46:05 +01:00
aba50ebc38 apt: bump version to 0.11.6-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-19 12:42:45 +01:00
942186e2b7 apt: pdm uses separator for test repo
For consistency with it's other repositories, we might migrate other
products also to this schema with a future major release (and nginx
rewrite config for backward compatibility)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-19 12:40:26 +01:00
ca7e4c9874 Merge remote-tracking branch 'upstream/master' 2024-12-09 21:17:41 +03:00
df6b705f56 notify: migrate from log to tracing
Migrated from `log` to `tracing`. Imported `tracing` only as it has a
smaller footprint (and less dependencies) than `proxmox_log`.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-12-05 13:22:24 +01:00
212249e009 sys: bump version to 0.6.5-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-05 08:32:47 +01:00
87dd908530 client: run cargo fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-05 08:32:47 +01:00
8eae5a8330 product-config: use inner mutability for PRODUCT_CONFIG
with edition 2024 `static mut` references will be disallowed [1]. the
recommended way to work around this is to use inner mutability, so use a
`OnceLock` for the `PRODUCT_CONFIG` as that should not change throughout
the run time of an application.

[1]:
https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-12-04 09:54:12 +01:00
115adf42bd acme-api: use inner mutability for ACME_ACME_CONFIG
in edition 2024 references to mutable static will be disallowed [1]. the
recommended way around this is to use types with inner mutability. so
use a `OnceLock` for `ACME_ACME_CONFIG` as the directoryfor ACME
configuration purposes shouldn't change throughout the run time of an
application.

[1]:
https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-12-04 09:53:51 +01:00
b256ee391c apt: repositories: remove unnecessary if-let in iterator
Fixes the manual_flatten clippy lint:

```
warning: unnecessary `if let` since only the `Some` variant of the iterator element is used
  --> proxmox-apt/src/repositories/mod.rs:40:5
   |
40 |       for digest in digests.values() {
   |       ^             ---------------- help: try: `digests.values().copied().flatten()`
   |  _____|
   | |
41 | |         if let Some(digest) = digest {
42 | |             common_raw.extend_from_slice(&digest[..]);
43 | |         }
44 | |     }
   | |_____^
   |
help: ...and remove the `if let` statement in the for loop
  --> proxmox-apt/src/repositories/mod.rs:41:9
   |
41 | /         if let Some(digest) = digest {
42 | |             common_raw.extend_from_slice(&digest[..]);
43 | |         }
   | |_________^
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
   = note: `#[warn(clippy::manual_flatten)]` on by default
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
FG: use `into_values().flatten()` instead of `values().copied().flatten()
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-12-03 13:50:23 +01:00
34c66e1542 sys: systemd: remove empty line after outer attribute
Fixes the clippy lint:

```
warning: empty line after outer attribute
 --> proxmox-sys/src/systemd.rs:7:1
  |
7 | / #[allow(clippy::manual_range_contains)]
8 | |
  | |_
9 |   fn parse_hex_digit(d: u8) -> Result<u8, Error> {
  |   ---------------------------------------------- the attribute applies to this function
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
  = note: `#[warn(clippy::empty_line_after_outer_attr)]` on by default
  = help: if the empty line is unintentional remove it
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
c8d49a7d38 docs: remove empty lines in docs
Fixes the clippy lints:

```
warning: empty line after doc comment
  --> proxmox-lang/src/lib.rs:33:1
   |
33 | / /// ```
34 | |
   | |_
35 |   #[macro_export]
36 |   macro_rules! try_block {
   |   ---------------------- the comment documents this macro
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
   = note: `#[warn(clippy::empty_line_after_doc_comments)]` on by default
   = help: if the empty line is unintentional remove it

warning: empty line after doc comment
   --> proxmox-router/src/cli/mod.rs:308:5
    |
308 | /     /// Can be used multiple times.
309 | |
    | |_
310 |       /// Finish the command line interface.
311 |       pub fn build(self) -> CommandLineInterface {
    |       ------------------------------------------ the comment documents this method
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
    = note: `#[warn(clippy::empty_line_after_doc_comments)]` on by default
    = help: if the empty line is unintentional remove it
help: if the documentation should include the empty line include it in the comment
    |
309 |     ///
    |
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
3f62a4dacf remove unnecessary return statement
Fixes the clippy lint:

```
warning: unneeded `return` statement
  --> proxmox-time/src/week_days.rs:31:14
   |
31 |         _ => return Err(parse_error(text, "weekday")),
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
   = note: `#[warn(clippy::needless_return)]` on by default
help: remove `return`
   |
31 |         _ => Err(parse_error(text, "weekday")),
   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
3492a0cee5 elide lifetimes where possible
This is possible on newer rustc.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
4cb3579786 router: parsing: docs: fix 'instead' typo
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
10b15fb2ad router: parsing: docs: fix Records::from link
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
1b5e8ee0dd apt: repositories: use if-let instead of match for Option
Fixes the single_match clippy lint:

```
warning: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
  --> proxmox-apt/src/repositories/mod.rs:41:9
   |
41 | /         match digest {
42 | |             Some(digest) => common_raw.extend_from_slice(&digest[..]),
43 | |             None => (),
44 | |         }
   | |_________^ help: try: `if let Some(digest) = digest { common_raw.extend_from_slice(&digest[..]) }`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_match
   = note: `#[warn(clippy::single_match)]` on by default
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
90e4dc5f8f api: webhook: doc: add indentation to list item
```
warning: doc list item without indentation
   --> proxmox-notify/src/api/webhook.rs:131:5
    |
131 | ///   (`400 Bad request`)
    |     ^^
    |
    = help: if this is supposed to be its own paragraph, add a blank line
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
    = note: `#[warn(clippy::doc_lazy_continuation)]` on by default
help: indent this line
    |
131 | ///     (`400 Bad request`)
    |       ++
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
8062e1abb3 apt: file: Use unwrap_or_default instead of match
Fixes the manual_unwrap_or_default clippy lint:

```
warning: match can be simplified with `.unwrap_or_default()`
   --> proxmox-apt/src/repositories/file.rs:369:30
    |
369 |               let mut origin = match repo.get_cached_origin(apt_lists_dir) {
    |  ______________________________^
370 | |                 Ok(option) => option,
371 | |                 Err(_) => None,
372 | |             };
    | |_____________^ help: replace it with: `repo.get_cached_origin(apt_lists_dir).unwrap_or_default()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
    = note: `#[warn(clippy::manual_unwrap_or_default)]` on by default
```

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-12-03 13:44:05 +01:00
2f3e985b5f docs: escape <foo> in doc comments
warning: unclosed HTML tag `nodename`
   --> pbs-api-types/src/metrics.rs:224:5
    |
224 | /     /// Unique identifier for this metric object, for instance 'node/<nodename>'
225 | |     /// or 'qemu/<vmid>'.
    | |_________________________^
    |
    = note: `#[warn(rustdoc::invalid_html_tags)]` on by default

warning: unclosed HTML tag `vmid`
   --> pbs-api-types/src/metrics.rs:224:5
    |
224 | /     /// Unique identifier for this metric object, for instance 'node/<nodename>'
225 | |     /// or 'qemu/<vmid>'.
    | |_________________________^

warning: `pbs-api-types` (lib doc) generated 2 warnings

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-12-03 11:52:50 +01:00
cf85c7c277 Removed CheckInstall repo's proccessing 2024-12-02 20:44:07 +03:00
491814c1cd Merge remote-tracking branch 'upstream/master' 2024-12-02 20:25:20 +03:00
9205e65d21 rest-server: bump version to 0.8.5
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-02 18:15:11 +01:00
787c2e03d7 sys: open directories with O_CLOEXEC
Factor out the open-flags to use for directories and add the CLOEXEC
flag to ensure that open FDs do not get passed to any child process.

A prominent cases where this can happen is the proxmox-daemon reload
code, which re-execs itself in a forked child-process and thus gets
all FDs passed.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
 [ TL: expand doc-comment and reword commit message to point at actual
   thing this fixes (exec not daemon reload) ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-02 17:06:25 +01:00
0547659e2c sys: fs: set CLOEXEC when creating temp files
In general we want all open files to have set CLOEXEC since our
reloading mechanism can basically fork at any moment and we don't want
newer daemons to carry around old file descriptors, especially lock
files.

Since `make_tmp_file` is called by many things (e.g. open_file_locked,
logrotate, rrd), set O_CLOEXEC with mkostemp.

This fixes issues with leftover file descriptors e.g. tape backups not
working because of lingering locks after a reload, or having deleted
rrd files open.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-12-02 16:59:44 +01:00
b2d31f075d rest-server: increase task index lock timeout to 15s
this lock can be quite contended, until the surrounding code is properly split
to reduce this contention it should help to give the worker task
creation/cleanup code a bit more breathing room.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-12-02 16:56:46 +01:00
57c8242dc2 rest-server: close race window when updating worker task count
this mimics how the count is updated when spawning a new task - the lock scope
needs to cover the count update itself, else there's a race when multiple
worker's log their result at the same time..

Co-developed-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-12-02 16:54:52 +01:00
4e51ac3527 rest-server: handle failure in worker task setup correctly
if setting up a new worker fails after it has been inserted into the
WORKER_TASK_LIST, we need to clean it up instead of bubbling up the error right
away, else we "leak" the worker task and it never finishes..

a worker task that never finishes will indefinitely block shutdown
of the rest server process, including the "old" process when reloading
the rest server.

this issue was found in the wild on a system with lock contention on the
file-based lock covering task index updating leading to lock acquiring
timeouts.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-12-02 16:54:18 +01:00
6e600c74a8 notify: use proxmox-sendmail forward implementation
moves to depending on `proxmox-sendmail` for forwarding mails via
`sendmail` too.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2024-12-02 15:38:48 +01:00
043fec42f8 sendmail: add mail-forwarder feature
this moves the mail forwarding implementation from `proxmox-notify` into
`proxmox-sendmail` to cover more `sendmail` related use-cases in one
place.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2024-12-02 15:38:48 +01:00
57c84dbfb5 notify: switch sendmail endpoint over to new crate
use the new `proxmox-sendmail` crate instead of the bespoke
implementation in `proxmox-notify`.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2024-12-02 15:38:48 +01:00
a69e86dff1 sendmail: add sendmail crate
add the `proxmox-sendmail` crate that makes it easier to send mails via
the `sendmail` utility. features include:

- multipart/alternative support for html+plain text mails
- multipart/mixed support for mails with attachments
- automatic nesting of multipart/alternative and multipart/mixed parts
- masks multiple receivers by default, can be disabled
- encoding Subject, To, From, and attachment file names correctly
- adding an `Auto-Submitted` header to avoid triggering automated mails

also includes several tests to ensure that mails are formatted
correctly. debian packaging is also provided.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
 [ TL: update years in d/copyright ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-12-02 15:38:48 +01:00
00a1980903 api types: version: implement traits to allow for version comparison
Derive and implement the traits to allow comparison of two
`ApiVersion` instances for more direct and easy api version
comparisons. Further, add some basic test cases to reduce risk of
regressions.

This is useful for e.g. feature compatibility checks by comparing api
versions of remote instances.

Example comparison:
```
api_version >= ApiVersion::new(3, 3, 0)
```

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-12-02 15:27:37 +01:00
4308bb0ca9 api types: version: drop unused repoid field
The `ApiVersion` type was introduced in commit ba850a25
("api/api-types: refactor api endpoint version, add api types")
including the `repoid`, added for completeness when converting from
a pre-existing `ApiVersionInfo` instance, as returned by the
`version` api endpoint.

Drop the additional `repoid` field, since this is currently not used,
can be obtained fro the `ApiVersionInfo` as well and only hinders the
implementation for easy api version comparison.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-12-02 15:27:37 +01:00
a36d3fdf29 datastore: extract nesting check into helper
and improve the variable namign while we are at it. this allows the check to be
re-used in other code paths, like when starting a garbage collection.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-27 15:25:37 +01:00
8a1166be4b log: bump version to 0.2.7
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-27 13:31:57 +01:00
cc79b2f08a log: ignore to_stdout parameter
This parameter causes the FileLogger to duplicate the log output to
stdout. This causes duplicate output on proxmox-backup-manager because
this is now handled by tracing. This should be removed completely in the
future.
In the worst case this will only result in missing log lines on stdout
(which is visible only on proxmox-backup-manager/client invocations
anyway).

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
 [ TL: add doc-comment to struct, note why it can be removed ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-27 13:29:14 +01:00
7200cd7e23 time: bump version to 2.0.3-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-26 16:53:50 +01:00
17bc0ac616 time: also implement From<&TimeSpan> for f64
Extend the already present `From<TimeSpan> for f64` implementation to
allow using the reference as well. There is no need to take ownership
and consume the `TimeSpan` object for conversion.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-26 16:50:56 +01:00
548411808e time: fix typos in TimeSpan related docstring
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-26 16:50:56 +01:00
7ae942f941 sync jobs: remove superfluous direction property
since the SyncJobConfig struct now contains a 'sync-direction' property, we can
omit the 'direction' property of the SyncJobStatus struct. This makes a
few adaptions in the ui necessary:

* use the correct field
* handle 'pull' as default (since we don't necessarily get a
  'sync-direction' in that case)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-11-26 16:02:22 +01:00
ce7239c24a api types: drop unused config type helpers for sync direction
Jobs for both sync directions are now stored using the same `sync`
config section type, so drop the outdated helpers.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Tested-by: Dominik Csapak <d.csapak@proxmox.com>
2024-11-26 16:00:50 +01:00
d70f6d9f09 api: admin/config: introduce sync direction as job config parameter
Add the sync direction for the sync job as optional config parameter
and refrain from using the config section type for conditional
direction check, as they are now the same (see previous commit).

Use the configured sync job parameter instead of passing it to the
various methods as function parameter and only filter based on sync
direction if an optional api parameter to distingush/filter based on
direction is given.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Tested-by: Dominik Csapak <d.csapak@proxmox.com>
2024-11-26 16:00:40 +01:00
efc45db20c api: admin: sync: add direction to sync job status
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-11-26 14:54:33 +01:00
f4868ff519 tree-wide: check in d/control meta changes for newer debcargo
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-26 14:44:33 +01:00
42d1128d9d noitfy: bump version to 0.5.1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-26 14:38:56 +01:00
bfe099f4f1 workspace: update proxmox-http-client to 0.9.4
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-26 14:37:43 +01:00
dfe81b5db8 notify: webhook, gotify: set HTTP request timeout of 10s
By default, the sync client from proxmox-http (powered by ureq) does not
have any request timeout. To avoid blocking the current thread for a
prolonged period of time, we now make use of the previously added
`Client::new_with_timeout` function to create a new HTTP client with a
default timeout of 10 seconds.

In the long run it would be nicer to have a higher timeout here, say
60s, to cope with flaky and high-latency networks and potentially
overloaded targets. But for that we need to change the architecture of
how notifications are send out to ensure that now thread accepting
connections can be blocked.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
 [ TL: Change timeout from 5s to 10s as trade-off and expand commit
   message slightly with some reasoning for that still relatively
   short time value ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-26 14:12:57 +01:00
6664b4150d http: bump version to 0.9.4
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-26 14:00:42 +01:00
a7c68a3166 http: sync client: add HTTP request timeout option
This commits adds the possibility to set a HTTP request timeout for the
sync client.

For now, I've opted to add this as a separate option than can be set via
a separate new_with_timeout method as compared to adding it to the HttpOptions
struct. While it of course would make a lot of sense to add it to the
latter, this would require adding support for request timeouts to the
async client as well. Some users of the async client handle request
timeouts externally via tokio::time::timeout, so these would need to
modified as well. I don't want to touch this at the moment,
so I've opted to introduce the timeout to the sync client only for now.
We can always revisit this at a later time and move the option to the
HttpOptions struct.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-26 13:57:11 +01:00
bfffbef9b2 api types: add missing conf to blob archive name mapping
Commit 0d66acd3 ("api types: introduce `BackupArchiveName` type")
introduced a dedicated archive name api type to add rust type
checking and bundle helpers to the api type. Since this, the backup
archive name to server archive name mapping is handled by its parser.

This however did not cover the `.conf` extension used for VM config
files. Add the missing `.conf` to `.conf.blob` to the match statement
and the test cases.

Fixes: 0d66acd3 ("api types: introduce `BackupArchiveName` type")
Reported-by: Stoiko Ivanov <s.ivanov@proxmox.com>
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-26 13:28:17 +01:00
f1f8c65c70 api: types: add 'mount_status' to schema
... and deserialize with default if field is missing in data.

Reported-by: Aaron Lauterer <a.lauterer@proxmox.com>
Fixes: 35fb5d4f7f ("pbs-api-types: add mount_status field to DataStoreListItem")
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-26 13:08:04 +01:00
2ed9c4bfca api: maintenance: allow setting of maintenance mode if 'unmounting'
So it is possible to reset it after a failed unmount, or abort an
unmount task by resetting it through the API.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-26 13:07:53 +01:00
622e43d5c3 notify: remove irritating 'html template not found' log message
The proxmox-notify crate can render notification text based on two
different templates, plaintext and html. The html template is at the
moment only used for email-based notifications. If we try to render
a html-formatted message but there is no html template, we try to
fall back to the plaintext template and wrap the rendered message
in <pre> tags.
As a preparation for user-supplied/overridden templates, I added a log
message "html template not found, falling back to plaintext ..." to
educate the user about this behavior.

In Proxmox Backup Server, we only ship plaintext templates at the
moment, meaning that this log message will be shown for every single
(email) notification that is sent out. This might be a bit confusing,
because the log message can be interpreted as an error, which it isn't.

This commit removes the log message completely for now. Once we add
support for user-overridable notification templates we could consider
adding it back it, but maybe phrased a bit differently, to avoid it
being interpreted as an error.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-26 12:08:49 +01:00
549cb082ef notify: sendmail: code style improvements
No functional changes intended.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-25 23:13:46 +01:00
296e2a2117 notify: sendmail: always send multi-part message
Even if we don't have an HTML template available, we always
send an HTML part (the plain text part wrapped in <pre>) to
improve rendering in certain mail clients. This means
we can simply message formatting, since we do not have to
distinguish between single-part and multi-part messages.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-25 23:13:46 +01:00
b09ee57341 notify: move mail formatting to separate function
This way we can test this in a sane manner and refactor
safely.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-25 23:13:46 +01:00
888ec2efe7 notify: sendmail: make mailfrom and author non-optional
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-25 23:13:46 +01:00
35fb5d4f7f pbs-api-types: add mount_status field to DataStoreListItem
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-25 21:34:22 +01:00
fd1f8413f7 maintenance: add 'Unmount' maintenance type
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-25 21:34:22 +01:00
6134a73b1e maintenance: make is_offline more generic
... and add MaintenanceType::Delete to it. We also want to clear any
cach entries if we are deleting the datastore, not just if it is marked
as offline.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-25 21:34:22 +01:00
d291f67236 pbs-api-types: add backing-device to DataStoreConfig
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-25 21:34:22 +01:00
6fed7301ea rrd: bump version to 0.4.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-25 18:42:35 +01:00
993e1fc878 rrd: selective code style clean-up
Selective because there are quite a few more such old-style format
strings, but I had those already adapted and currently do not have
time do clean-up tree-wide, but it's fine to change this
incrementally.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-25 17:46:44 +01:00
08cee13c03 rrd: do not log tree info-level messages on applying journal
That's rather excessive and has not much value for users. So degrade
two of the messages to debug-level.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-25 17:43:56 +01:00
9f135cf16e time: run cargo fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-25 17:20:01 +01:00
cc85a72391 rest-server: bump version to 0.8.4-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-25 17:16:16 +01:00
34e307461b rest-server: add custom handlebars escape fn
Add a custom handlebars escape function. It's nearly identical to the
default `html_escape` fn [0], but it does not escape the '='. This is
needed to support base64 encoded values.

[0]: https://docs.rs/handlebars/latest/handlebars/fn.html_escape.html

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
 [ TL: use full width for comment ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-25 16:34:30 +01:00
b51b0be153 api: enforce minimum character limit of 8 on new passwords
we already have two different password schemas, `PBS_PASSWORD_SCHEMA`
being the stricter one, which ensures a minimum length of new
passwords. however, this wasn't used on the change password endpoint
before, so add it there too. this is also in-line with NIST's latest
recommendations [1].

[1]: https://pages.nist.gov/800-63-4/sp800-63b.html#passwordver

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-11-25 15:51:47 +01:00
6e3c5afce5 api types: replace PathPatterns with Vec<PathPattern>
PathPatterns is hard to distinguish from PathPattern, so would need to be
renamed anyway.. but there isn't really a reason to define a separate API type
just for this.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-25 12:28:40 +01:00
85256a6b6c api-types: implement dedicated api type for match patterns
Introduces a dedicated api type `PathPattern` and the corresponding
format and input validation schema. Further, add a `PathPatterns`
type for collections of path patterns and implement required traits
to be able to replace currently defined api parameters.

In preparation for using this common api type for all api endpoints
exposing a match pattern parameter.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-25 11:57:07 +01:00
32969b47e1 fix #3786: api: add resync-corrupt option to sync jobs
This option allows us to "fix" corrupt snapshots (and/or their chunks)
by pulling them from another remote. When traversing the remote
snapshots, we check if it exists locally, and if it is, we check if the
last verification of it failed. If the local snapshot is broken and the
`resync-corrupt` option is turned on, we pull in the remote snapshot,
overwriting the local one.

This is very useful and has been requested a lot, as there is currently
no way to "fix" corrupt chunks/snapshots even if the user has a healthy
version of it on their offsite instance.

Originally-by: Shannon Sterz <s.sterz@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-25 10:53:26 +01:00
916c46905b api types: extend backup archive name parsing tests
and also test the error triggered by a directory path being passed in.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-22 13:47:25 +01:00
5a22076e67 api types: add unit tests for backup archive name parsing
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-22 13:47:25 +01:00
0d66acd390 api types: introduce BackupArchiveName type
Introduces a dedicated wrapper type to be used for backup archive
names instead of plain strings and associated helper methods for
archive type checks and archive name mappings.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>

FG: use LazyLock for constant archive names reduces churn, and saves some
allocations

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-22 13:46:35 +01:00
a2773ddd79 datastore: move ArchiveType to api types
Moving the `ArchiveType` to avoid crate dependencies on
`pbs-datastore`.

In preparation for introducing a dedicated `BackupArchiveName` api
type, allowing to set the corresponding archive type variant when
parsing the archive name based on it's filename.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-22 11:45:43 +01:00
fda1f99479 version: remove named features
and use version comparison for the push code that previously used it.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-21 11:53:49 +01:00
2f4c9f784e api types/config: add sync-push config type for push sync jobs
In order for sync jobs to be either pull or push jobs, allow to
configure the direction of the job.

Adds an additional config type `sync-push` to the sync job config, to
clearly distinguish sync jobs configured in pull and in push
direction and defines and implements the required `SyncDirection` api
type.

This approach was chosen in order to limit possible misconfiguration,
as unintentionally switching the sync direction could potentially
delete still required snapshots.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
ba850a25a3 api/api-types: refactor api endpoint version, add api types
Add a dedicated api type for the `version` api endpoint and helper
methods for supported feature comparison.
This will be used to detect api incompatibility of older hosts, not
supporting some features.

Use the new api type to refactor the version endpoint and set it as
return type.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
9aaad591c6 api types: implement api type for BackupGroupDeleteStats
Make the `BackupGroupDeleteStats` exposable via the API by implementing
the ApiTypes trait via the api macro invocation and add an additional
field to account for the number of deleted groups.
Further, add a method to add up the statistics.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
62270f8fef datastore: move BackupGroupDeleteStats to api types
In preparation for the delete stats to be exposed as return type to
the backup group delete api endpoint.

Also, rename the private field `unremoved_protected` to a better
fitting `protected_snapshots` to be in line with the method names.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
92b652935b api types: define remote permissions and roles for push sync
Adding the privileges to allow backup, namespace creation and prune
on remote targets, to be used for sync jobs in push direction.

Also adds dedicated roles setting the required privileges.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
8614be4ceb api types: implement remote acl path method for sync job
Add `remote_acl_path` method which generates the acl path from the sync
job configuration. This helper allows to easily generate the acl path
from a given sync job config for privilege checks.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
48809ab0db api types: add remote acl path method for BackupNamespace
Add a `remote_acl_path` helper method for creating acl paths for
remote namespaces, to be used by the priv checks on remote datastore
namespaces for e.g. the sync job in push direction.

Factor out the common path extension into a dedicated method.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-11-21 10:14:53 +01:00
072ca695f5 README: describe [patch.crates-io] and sysext workflow
For how to work on the crates in this workspace while actually working
on a separate project without having to constantly reinstall `.deb`
files.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-11-19 14:08:34 +01:00
2f25debee6 buildsys: clean old sysext dir before installing
So version bumps don't getted mixed into previous builds.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-11-19 13:58:16 +01:00
96e76d7f72 client: use correct error for protocol errors
The 'Anyhow' error is not useful and meant for throw-away errors which
cannot be dealt with anyway, and we'd like to be able to tell apart
network problems from actual HTTP responses, so that we can
potentially try a different node in a cluster connection.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-11-19 13:41:09 +01:00
c01318d966 log: bump version to 0.2.6
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-19 11:33:11 +01:00
7bffb9fe92 config: factor out method to get the absolute datastore path
removable datastores will have a PBS-managed mountpoint as path, direct
access to the field needs to be replaced with a helper that can account
for this.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-11-17 19:57:33 +01:00
1e7c0fc3ac rest-server: bump version to 0.8.3-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-15 11:28:04 +01:00
9529f730e0 rest-server: drop log intended for debugging again
I considered keeping it as log::trace level, but IMO that's just not
worth it, as just the peek_len is not giving one much more and can
also be basically also gathered through strace.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-15 10:29:33 +01:00
1539bc1ce3 rest-server: bump version to 0.8.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-14 14:39:19 +01:00
dc9531d302 fix #5868: rest-server: handshake detection: avoid infinite loop on connections abort
When a connection is closed by the client before we have enough data
to determine if it contains a TLS Handshake or not, the socket stays
in a readable state.
While we setup a tokio backed timeout of 10s for the connection
build-up here, this timeout does not trigger on said early connection
abort from the client side, causing then the async_io loop to
endlessly loop around peeking into the client, which always returns
the last available bytes before the connection was closed. This in
turn causes 100% CPU usage for one of the PBS threads.
The timeout not triggering is rather odd, and does indicate some
potential for further improvement in tokio itself, but our
questionable use of the WouldBlock error does violate the API
contract, so this is not a clear cut.

Such an early connection abort is often triggered by monitoring
solutions, which use it to relatively cheaply check if TCP on a port
still works as "is service up" heuristic.

To fix this, save the amount of bytes peek returned and if they did
not change between invocations of the callback, we can assume that the
connection was closed and thus exit the connection attempt with an
error.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
 [ TL: reword commit message and change error to ConnectionAborted ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-14 14:31:47 +01:00
f22fae3852 apt: bump version to 0.11.5-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-11 21:10:23 +01:00
fd48033644 apt: add Ceph Squid to standard repos for PVE
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-11 21:08:10 +01:00
2cc7eadb45 notify: bump version to 0.5.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-10 18:58:59 +01:00
64943d0a3c notify: add api for webhook targets
All in all pretty similar to other endpoint APIs.
One thing worth noting is how secrets are handled. We never ever
return the values of previously stored secrets in get_endpoint(s)
calls, but only a list of the names of all secrets. This is needed
to build the UI, where we display all secrets that were set before in
a table.

For update calls, one is supposed to send all secrets that should be
kept and updated. If the value should be updated, the name and value
is expected, and if the current value should preseved, only the name
is sent. If a secret's name is not present in the updater, it will be
dropped. If 'secret' is present in the 'delete' array, all secrets
will be dropped, apart from those which are also set/preserved in the
same update call.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-By: Stefan Hanreich <s.hanreich@proxmox.com>
2024-11-10 18:55:11 +01:00
16260195b9 notify: implement webhook targets
This target type allows users to perform HTTP requests to arbitrary
third party (notification) services, for instance
ntfy.sh/Discord/Slack.

The configuration for these endpoints allows one to freely configure
the URL, HTTP Method, headers and body. The URL, header values and
body support handlebars templating to inject notification text,
metadata and secrets. Secrets are stored in the protected
configuration file (e.g. /etc/pve/priv/notification.cfg) as key value
pairs, allowing users to protect sensitive tokens/passwords.
Secrets are accessible in handlebar templating via the secrets.*
namespace, e.g. if there is a secret named 'token', a body
could contain '{{ secrets.token }}' to inject the token into the
payload.

A couple of handlebars helpers are also provided:
  - url-encoding (useful for templating in URLs)
  - escape (escape any control characters in strings)
  - json (print a property as json)

In the configuration, the body, header values and secret values
are stored in base64 encoding so that we can store any string we want.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-By: Stefan Hanreich <s.hanreich@proxmox.com>
2024-11-10 18:55:11 +01:00
0517d7b94e notify: renderer: adapt to changes in proxmox-time
A recent commit [1] changed the `Display` implementation of `TimeSpan` such
that minutes are now displayed as `20m` instead  of `20min`.
This commit adapts the tests for the notification template renderer
accordingly.

[1] 19129960 ("time: display minute/month such that it can be parsed again")

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-11-10 18:55:11 +01:00
3817b3ba50 apt: bump version to 0.11.4
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-10 18:53:26 +01:00
272953d72d apt: add support for Ceph Squid repositories
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-10 18:48:03 +01:00
6158d53697 apt-api-types: bump version to 1.0.2
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-10 18:45:57 +01:00
8e74afbca7 apt-api-types: add Ceph Squid as valid Proxmox APT repository handle
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-10 18:42:13 +01:00
0ea27021a2 rest-server: bump to 0.8.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-11-08 12:09:53 +01:00
a092b06d9f rest-server: shorten some format strings
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-11-08 12:09:53 +01:00
d8fa495a50 rest-server: check permissions on proxy.key and proxy.pem files
To avoid openssl's unhelpful error messages when the proxy.key or
proxy.pem files have the wrong permissions, we open the files. To load
the private key, we can simply read from the file and pass it to the
`set_private_key` openssl function. Sadly such a function does not exist
for loading certificate chains, so we have to open and close the file
before calling the `set_certificate_chain_file` fn.

Motivation: https://forum.proxmox.com/threads/proxmox-backup-tailscale-proxmox-backup-proxy-service-wont-boot.153204

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-11-08 12:09:53 +01:00
db69867d4d rest-server: pass cipher suite/list to acceptor
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-11-08 12:09:53 +01:00
35c60f652b subscription: use correct debian release name
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-11-07 13:35:00 +01:00
996c86bb32 subscription: bump version to 0.5.0-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-11-07 13:19:13 +01:00
d1141ab2ec fix url to apt-pkg-native crate
Signed-off-by: Алексей Шабалин <shaba@noreply.gitea.basealt.ru>
2024-11-07 14:57:14 +03:00
ae55575f2a subscription: move most of the implmentation into impl feature
so we can use the types without having openssl, proxmox-sys, etc. as
dependencies.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-11-07 12:43:17 +01:00
58f1446429 Merge pull request 'ALT linux support for APT crates' (#1) from konevsa/proxmox:sisyphus into master
Reviewed-on: #1
2024-11-07 14:30:35 +03:00
9fea8ca0ce Tests fixed 2024-11-07 13:51:06 +03:00
5a02867d44 Added more URLs to account as good Classic repos 2024-11-07 12:58:38 +03:00
dbaa2df500 Small fix for cross-platforming of low-level C bindings 2024-11-07 02:15:55 +03:00
a6f5fd8c24 (ALT) Better parsing host from uri 2024-11-06 19:26:44 +03:00
bbceaa13d7 Test fixed according to correct suite check 2024-11-06 15:34:33 +03:00
284c483b6b Adapted repo file suite check for ALT 2024-11-06 15:27:38 +03:00
f5ef96d506 (ALT) Now files with .sisyphus (corresponds to README.sisyphus), .rpmnew and .rpmsave ignored during repo lists parsing 2024-11-01 12:40:58 +03:00
f96c0e6036 http: update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-10-30 12:20:36 +01:00
e8e5c11c6a fix #5808: http: use native-tls instead of rustls for the sync client
In the reference Bugzilla entry, a certificate with an IP address as a
SAN was used. rustls seems to have problems with that [1].
Also, pretty much all of our code uses native-tls at the moment, so
it makes sense to not pull in a second TLS implementation.

Tested by rebuilding libpve-rs-perl and testing a Gotify notification
target with a self-signed TLS certificate (one that is accepted by
OpenSSL but not by rusttls).

[1] https://github.com/rustls/rustls/issues/184

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-10-30 12:19:56 +01:00
b945c8f6b7 Fix(tests): upstream tests after a merge request 2024-10-29 15:47:06 +03:00
5908f1261e Commented out upstream config.toml 2024-10-28 17:44:15 +03:00
35b1de18d0 Changed .toml deps to depend on Alt's crates with alt's feature 2024-10-28 15:37:45 +03:00
314e563210 Altlinux tests fixed according to changed API 2024-10-28 14:58:42 +03:00
c1ec0a7ee0 Some Traits and usings fixed 2024-10-28 13:57:45 +03:00
59d0e20da9 Applied "alt-linux" feature to cache api and proxmox-apt-api-types crate 2024-10-28 13:50:10 +03:00
f559a28390 Adapted supplementary structures (taken from Alexander Burmatov commits at git://git.altlinux.org/people/thatman/packages/proxmox.new.git) 2024-10-28 13:42:54 +03:00
235ed36f10 Initial merge with Alt's proxmox 2024-10-28 12:50:49 +03:00
56d7012996 Merge remote-tracking branch 'proxmox.new/altlinux' 2024-10-28 12:37:10 +03:00
f5e7f4ed7f proxmox-apt-api-types: use workspace excludes
else the `debian` dir is contained in the .crate archive, breaking the build..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-10-23 13:29:22 +02:00
cba72020dd api-types: add missing doc-comment description for api enums
this is used as description in the api schema

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-22 15:25:52 +02:00
ba3ee7a4db api-marcro: throw compiler error if description for enums is empty
A description is required for the API schema types and we fallback to
the rust doc-comment when no explicit one is set.
But a empty string was returned if no doc-comment existed, so check
for the comment to be non-empty and throw a compile-time error
otherwise.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-22 15:14:43 +02:00
1c4467841d schema: property description: output indentation where its required
The wrap_text helper accepts and initial indentation, so use that as
central point to add the indentation that glues the list entry
together with its description.

Mostly a small optimization, should not matter in practice, i.e. where
all properties should have a description.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-22 14:51:19 +02:00
bd1133fcd2 notify: add missing doc-comment description for api enums
this is used as description in the api schema

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-22 14:49:10 +02:00
331fa7a732 apt-api-types: add missing doc-comment description for api enums
this is used as description in the api schema

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-22 14:48:47 +02:00
1b70270b2d log: only print error level to syslog/stderr
We only want to print the error level, and not all the levels below
error to stderr/syslog.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-10-18 18:28:37 +02:00
21c314b56e schema: property description: switch format strings to inline template variables
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-18 17:37:30 +02:00
b809d86d73 cli: format: switch some format strings to inline template variables
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-18 17:29:17 +02:00
aa12dcbba0 time: bump version to 2.0.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-17 16:16:56 +02:00
44e7ca98cd time: add some simple unit tests for time span conversions
For starters, could definitively be expanded.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-17 14:23:27 +02:00
17bf3ec9fe time: add module level docs for time span
There was basically no documentation at all, so try to document the
basic format syntax and where it comes from. The text is partially
adapted from the systemd docs.

Could be still expanded with some example code and the methods might
do good with getting some docs too, but those parts can be relatively
easily be figured out from the code itself, the basic underlying
design and format background is much harder to guess..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-17 14:23:27 +02:00
191299605f time: display minute/month such that it can be parsed again
Previously we displayed, e.g., "4m 1h 1min", i.e. using "m" for months
and "min" for minutes but "m"  was not accepted as month when parsing
a timespan string, so a 4 month timespan would be printed "4m" but if
parsed again it would result in a timespan of 4 minutes.

So switch month to an uppercase "M" and minute to the lower case "m",
which makes renderings of common timespans nicer, as in most of our
use cases they are in the range of minutes to hours, sometimes days
but seldom longer than weeks. So using single letters for all but
"min" stuck out quite a bit, e.g.: "1h 5min 2s" looks odd compared to
"1h 5m 1s"

While the duplicate letter is not 100% ideal it's still better than
the status quo, where rendering and parsing would interpret things
differently.
Also, the order still makes it quite clear, e.g.:
"7m 2w 3d 1h 5min 44s" now becomes "7M 2w 3d 1h 5m 44s"

As a side effect this also brings the display format closer to what is
used inside PVE backup job taks logs.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-17 14:22:44 +02:00
9ae91303fd time: switch to inline template variables
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-10-17 11:40:21 +02:00
67822186a2 time: drop trailing space when not showing seconds at end
Seconds are not displayed when the value is smaller than 0.1s and
they are not at the start of the display output, e.g. `1h 2m`.
Drop the additional whitespace currently appended for this edge case.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
2024-10-17 10:38:53 +02:00
c76090b907 tfa: clean up unused 'use' statements
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-15 15:19:03 +02:00
deb07eeb31 pbs-api-types: add types for the new metrics endpoint
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-10-15 14:09:41 +02:00
11930517ef acme: bump to 0.5.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-03 09:52:46 +02:00
b52b3739be acme: deny(unsafe_code)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-02 12:49:17 +02:00
f298ed6aec acme: detect base64 vs base64url encoded hmac keys
We do this in the PVE code as well.

Link: https://forum.proxmox.com/threads/acme-with-custom-acme-directory-doesnt-work.147058/
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-02 12:45:31 +02:00
c30169d08f tfa: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-01 12:51:04 +02:00
2a1458126c tfa: bump to 5.0.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-01 12:48:36 +02:00
8698f3afc7 tfa: provide TfaUser via the 'types' feature and module
So we can access it from UI code.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-10-01 12:48:27 +02:00
ee113bf244 login: bump to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-30 11:48:12 +02:00
eb3dd9453b login: boolean parser: also accept "1" and "0" as strings
since that's what the pve api sometimes returns for booleans

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-09-30 11:45:06 +02:00
7379bdfb9c README: extend Adding Cates section, convert to markdown
(conveted with pandoc and some minor manual fixups)

Mention that the crates should activate `doc_cfg, doc_auto_cfg` and
ideally `#[deny(unsafe_op_in_unsafe_fn)]` and `#[deny(missing_docs)]`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-30 10:26:56 +02:00
f7e130d5b5 api-macro: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-26 14:56:56 +02:00
d601b57fd0 api-macro: bump to 1.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-26 14:52:52 +02:00
532d4d3e9a login: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-26 14:51:53 +02:00
2b3c356ece api-macro: allow declaring an additional-properties field
Object schemas can now declare a field which causes
'additional_properties' to be set to true and the field being ignored
in the schema.

This allows adding a flattened HashMap<String, Value> to gather the
additional unspecified properties.

    #[api(additional_properties: "rest")]
    struct Something {
        #[serde(flatten)]
        rest: HashMap<String, Value>,
    }

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-26 12:47:58 +02:00
e72528ca70 login: add 'raw' webauthn challenge access
So we can get going on the wasm side where we don't yet have access to
the webauthn-rs crate.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-26 12:40:32 +02:00
c85b534837 readme: update cargo config path
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-20 08:44:19 +02:00
005678cec2 buildsys: add a 'make list-packages' target
To ease development on new machines, this provides an easy way to just
do

    # apt install $(make list-packages)

to get started.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-20 08:43:08 +02:00
d6e86d670b tree-wide: unify workspace inherited attributes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-20 08:42:45 +02:00
6e8ad21227 rrd-api-types: bump to 1.0.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-19 15:17:08 +02:00
9ed8f7f110 rrd-api-types: follor acronym capitalization guidelines
Link: https://rust-lang.github.io/api-guidelines/naming.html
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-19 15:13:26 +02:00
111a883788 rrd-api-types: bump to 1.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-19 14:59:08 +02:00
4f787391de rrd-api-types: make mode and timeframe +Eq+PartialEq+Debug
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-19 14:55:51 +02:00
b38568158a sys: bump to 0.6.4-1
This should also fix a build issue on aarch64 caused by a signed-ness
differences of c_char.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-19 09:07:01 +02:00
fb8a706066 rrd-api-types: check in d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-19 09:06:55 +02:00
8f3eecab68 rrd-api-types: bump to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-18 16:05:21 +02:00
3cf67472a1 rrd: bump to 0.4.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-18 16:05:21 +02:00
0177b1d975 add proxmox-rrd-api-types crate - moved out of proxmox-rrd
so we can access them from wasm without pulling in proxmox-rrd as it
also pulls in sys...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-18 15:55:43 +02:00
e57a65879e rrd: bump to 0.3.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-18 15:24:16 +02:00
9426af0abf rrd: derive Display and FromStr for api types
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-09-18 15:21:58 +02:00
fda2cdb7ed log: bump to 0.2.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-16 15:12:11 +02:00
5d33d870ee log: print error if env-var parsing failed, print correct name
Print error if the parsing of the env-var fails on the proxmox-backup-*
daemons as well. Output correct env-var on binaries that use different
variables.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-09-16 15:08:41 +02:00
c9c9ade96e log: fallback to stderr if syslog not available
Don't panic when the syslog is not available - which happens commonly in
containers and sbuild environments (chroot and unshare) - instead
fallback to stderr.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
2024-09-16 15:07:43 +02:00
c6fd5604df access-control: bump to 0.2.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:25:49 +02:00
80503e409d buildsys: add targets for raw installation and sysexts
The following targets should be self-explanatory:

    # make install
    $ make DESTDIR=$SOME_PATH install
    # make proxmox-sys-install
    $ make DESTDIR=$SOME_PATH proxmox-sys-install
    # make proxmox-<any other crate name>-install

Additionally, these are used as building blocks to create
systemd-sysext(8) images:

    $ make proxmox-sys-sysext

builds an `extensions/proxmox-sys.raw`

This can be copied/symlinked to `/run/extensions/` and then activated.
As root:

    # ln -s $REPO_DIR/extensions/proxmox-sys.raw /run/extensions/
    # systemd-systext refresh

For the complete workspace, an `extensions/proxmox-workspace.raw` can
be built via

    $ make sysext

This also takes a `CRATES` var to limit the crates which should be
included, and takes an optional `NOCLEAN=1` which prevents cleaning
out the previously installed to "add" new crates on the go:
Assuming there's a symlink like:

    # ln -s $REPO_DIR/extensions/proxmox-workspace.raw /run/extensions/proxmox-workspace.raw

One can modify the installed crates like this:

    $ make CRATES=proxmox-sys sysext
    $ sudo systemd-sysext refresh

Now only the current proxmox-sys crate is overridden.

    $ make NOCLEAN=1 CRATES=proxmox-time sysext
    $ sudo systemd-sysext refresh

Now proxmox-sys as well as proxmox-time are installed.

To undo the changes, either just do, as root:

    # systemd-sysext unmerge

or remove the files which should specifically be dropped from
/run/extensions/ and run as root:

    # systemd-sysext refresh

Another way to temporarily install single crates is to just have the
extensions/ folder *be* the `/run/extensions` folder:

    # rmdir /run/extensions
    # ln -s $REPO_DIR/extensions /run/extensions

Then just build individual extensions:

    $ make proxmox-sys-sysext
    $ sudo systemd-sysext refresh
    $ make proxmox-router-sysext
    $ sudo systemd-sysext refresh

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
3d812952bc auth-api: bump to 0.4.6
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
420285ea8c acme-api: bump to 0.1.6-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
b1ddac7c45 client: bump to 0.5.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
61e4d35dea schema: bump to 3.2.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
dfc4e5f866 api-macro: bump to 1.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
ddbada0668 rest-server: bump to 0.8.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
98ed1bb28b router: bump to 3.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
d469463904 compression: bump to 0.2.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
3d6b3c4786 router: split streaming reader impl into 'stream' feature
so 'no-default-features' compiles again

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
2a4cf83799 rest-server: utilize flush_window for streams
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
78c29b33ef compression: add flush_window to DeflateEncoder
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
b22de1864a router: add stream helpers to async-decode json-seq
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
04923dd601 client: expose body, add generic request methods and streaming
The get/put/post/put_without_body/... methods now have a default
implementation forwarding to a generic `request` method as all our
implementations do the same already anyway.

Additionally, in order to allow easy access to a "streaming body", the
Body type is now exposed.

In the future, this crate may also require a wrapper to standardize
the handling of `application/json-seq` streams if we end up using
them, but for now, a simple way to expose the body is enough to get
going.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
8021f0a7f6 api-macro: support new streaming api methods
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
17bd32e90e router, rest-server: add StreamSync and StreamAsync API handlers
These are Iterators or Streams which continuously produce output. They
can either be formatted, in which they are serialized like the as
usually, or, if the client caccepts `application/json-seq` via an
`Accept` header, it will be streamed as a sequence directly.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
c31eaf0018 router, rest-server, api-macro: rename Streaming api to Serializing
This does not "stream", but rather skips the intermediate step to
serialize the entire output into a local json string.

We now reserve the "Stream*" prefix for actual *streaming*, that is,
producing an API response which gets streamed continuously as it is
asynchronously produced.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 14:15:11 +02:00
e8b8060b17 log: bump to 0.2.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-09-05 13:58:56 +02:00
5933c2f47b log: write to stderr when using init_cli_logger, export tracing::Level
Previously when using `env_logger` all of our cli-tools logged to
stderr, make tracing do the same. Export `tracing::Level` so that we can
use the `tracing::enabled!` macro.

Tested-by: Christian Ebner <c.ebner@proxmox.com>
Reviewed-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-09-05 13:45:43 +02:00
a46dd1a60a router: bump to 2.2.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-30 13:53:03 +02:00
9a17ff15f7 log: bump to 0.2.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-30 13:50:24 +02:00
021bab9304 add tracing init_cli_logger and deprecate old one
Deprecate the proxmox-router init_cli_logger function used in client
binaries such as `proxmox-backup-client`, `proxmox-backup-manager`,
'pxar', etc... Add a new init_cli_logger function that uses tracing
instead of env_logger. It checks if the task is in a workertask and
prints the message either to stdout or to the tasklog (this is
neccessary for commands in proxmox-backup-manager that call api handlers
that start workerthreads from the client).

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-08-30 13:47:55 +02:00
237b317678 router: sort cli properties in usage output
If we don't do this, then properties from a serde flattened struct will
be positioned at the end of the list, rather than properly sorted with
the other properties.

Since the tests also feature non-sorted properties, we have to adapt
them too.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-08-30 13:26:13 +02:00
1d5d7b7f98 fix #5622: backup client: properly handle rate/burst parameters
The rate and burst parameters are integers, so the mapping from value
with `.as_str()` will always return `None` effectively never
applying any rate limit at all.

Fix it by turning them into a HumanByte instead of an integer.

To not crowd the parameter section so much, create a
ClientRateLimitConfig struct that gets flattened into the parameter list
of the backup client.

To adapt the description of the parameters, add new schemas that copy
the `HumanByte` schema but change the description.

With this, the rate limit actually works, and there is no lower limit
any more.

The old TRAFFIC_CONTROL_RATE/BURST_SCHEMAs can be deleted since the
client was the only user of them.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-08-30 13:21:29 +02:00
d72ea40c80 schema: bump to 3.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 14:51:23 +02:00
5704cb43b9 schema: add Schema::unwrap_any_object_schema
so we have a version we can use in const fns and const{} expressions

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 14:50:21 +02:00
00ca04698d schema: seal ObjectSchemaType and assert Send + Sync
While this is technically a breaking API change since the trait is
public, we don't implement it anywhere and it isn't meant to be
implemented from the outside.

Also, encode that these types are all Send + Sync via a super trait
notation.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 14:43:42 +02:00
e750bce69b schema: make Schema::any_object a const fn
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 14:32:39 +02:00
d166dcff0a schema: rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 14:32:21 +02:00
3d044a5acf systemd: bump d/control to add missing dependencies
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 14:18:58 +02:00
a41da1f371 systemd: debcargo: add libsystemd-dev to dependencies
`Build-Depends` and `Depends` in d/control are missing `libsystemd-dev`,
resulting in mk-build-deps not being able to install all dependencies
needed by `make deb`.

After running `make deb` the control file looks:

```diff
modified   proxmox-systemd/debian/control
@@ -6,7 +6,8 @@ Build-Depends: debhelper (>= 12),
  cargo:native <!nocheck>,
  rustc:native <!nocheck>,
  libstd-rust-dev <!nocheck>,
- librust-libc-0.2+default-dev (>= 0.2.107-~~) <!nocheck>
+ librust-libc-0.2+default-dev (>= 0.2.107-~~) <!nocheck>,
+ libsystemd-dev <!nocheck>
 Maintainer: Proxmox Support Team <support@proxmox.com>
 Standards-Version: 4.6.2
 Vcs-Git: git://git.proxmox.com/git/proxmox.git
@@ -19,7 +20,8 @@ Architecture: any
 Multi-Arch: same
 Depends:
  ${misc:Depends},
- librust-libc-0.2+default-dev (>= 0.2.107-~~)
+ librust-libc-0.2+default-dev (>= 0.2.107-~~),
+ libsystemd-dev
 Provides:
  librust-proxmox-systemd+default-dev (= ${binary:Version}),
  librust-proxmox-systemd-0-dev (= ${binary:Version}),
```

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 14:18:22 +02:00
de004f736d section-config: fix link to SectionConfigData
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
ec3e8e05e4 section-config: fix link to section_config
Fixes:

warning: unresolved link to `seccfg`
  --> proxmox-section-config/src/typed.rs:18:71
   |
18 |     /// If the [`SectionConfig`] returned by the [`section_config()`][seccfg] method includes the
   |                                                                       ^^^^^^ no item named `seccfg` in scope
   |
   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
   = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default

warning: unresolved link to `seccfg`
  --> proxmox-section-config/src/typed.rs:22:10
   |
22 |     /// [seccfg] ApiSectionDataEntry::section_config()
   |          ^^^^^^ no item named `seccfg` in scope
   |
   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
0157b6fdea access-control: acl: add indentation to docs
Fixes:

warning: doc list item missing indentation
   --> proxmox-access-control/src/acl.rs:518:9
    |
518 |     /// -- user/token is more specific than group at each level
    |         ^
    |
    = help: if this is supposed to be its own paragraph, add a blank line
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
    = note: `#[warn(clippy::doc_lazy_continuation)]` on by default
help: indent this line
    |
518 |     ///   -- user/token is more specific than group at each level
    |         ++

warning: doc list item missing indentation
   --> proxmox-access-control/src/acl.rs:519:9
    |
519 |     /// -- roles lower in the tree are more specific than those higher up along the path
    |         ^
    |
    = help: if this is supposed to be its own paragraph, add a blank line
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
help: indent this line
    |
519 |     ///   -- roles lower in the tree are more specific than those higher up along the path
    |         ++

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
e60a53d80b apt: sources_parser: remove needless borrow
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
f8f57540c6 apt: cache_api: simplify match with unwrap_or_default
Fixes:

warning: match can be simplified with `.unwrap_or_default()`
  --> proxmox-apt/src/cache_api.rs:77:28
   |
77 |           let mut notified = match cache.notified {
   |  ____________________________^
78 | |             Some(notified) => notified,
79 | |             None => std::collections::HashMap::new(),
80 | |         };
   | |_________^ help: replace it with: `cache.notified.unwrap_or_default()`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
   = note: `#[warn(clippy::manual_unwrap_or_default)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
a2e5973982 router: completion: remove needles borrow
Fixes:

warning: this expression creates a reference which is immediately dereferenced by the compiler
   --> proxmox-router/src/cli/completion.rs:154:25
    |
154 |                         &completion_functions,
    |                         ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `completion_functions`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
    = note: `#[warn(clippy::needless_borrow)]` on by default

warning: this expression creates a reference which is immediately dereferenced by the compiler
   --> proxmox-router/src/cli/completion.rs:201:21
    |
201 |                     &completion_functions,
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `completion_functions`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
b7100ecc1e rrd_map: remove unnecessary use of get().is_some()
Fixes:

warning: unnecessary use of `get(rel_path).is_some()`
   --> proxmox-rrd/src/cache/rrd_map.rs:107:21
    |
107 |         if self.map.get(rel_path).is_some() {
    |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `contains_key(rel_path)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
5586eeaba8 rrd_map: remove unneded return statement
Fixes:

warning: unneeded `return` statement
   --> proxmox-rrd/src/cache/rrd_map.rs:117:13
    |
117 |             return Ok(true);
    |             ^^^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
    = note: `#[warn(clippy::needless_return)]` on by default
help: remove `return`
    |
117 -             return Ok(true);
117 +             Ok(true)
    |

warning: unneeded `return` statement
   --> proxmox-rrd/src/cache/rrd_map.rs:119:13
    |
119 |             return Ok(false);
    |             ^^^^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
help: remove `return`
    |
119 -             return Ok(false);
119 +             Ok(false)

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-28 13:09:15 +02:00
2da3121492 sys: crypt: style + drop unnecessary length check
These are statically sized arrays, not slices, they cannot be empty.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-28 13:04:43 +02:00
4f4b172c71 Disable Deb-822 format for ALT
There is no need for the deb822-style format.
2024-08-21 17:56:31 +03:00
30de6a59f5 Add tests for ALT
1. New tests for ALT Linux.
2. Move test files for Debian to a separate directory.
2024-08-21 17:56:31 +03:00
5a53078041 Set correct filename for cached file 2024-08-21 17:56:31 +03:00
3891685a18 Change Repository option to Vendor option
Apt uses vendor ID (alt, p10 etc) in square brackets of the source lists.
2024-08-21 17:56:31 +03:00
b99a6ccd0b New APTRepositoryHandles
ALT Linux uses checkinstall, classic, debuginfo and gostcrypto.
2024-08-21 17:56:31 +03:00
89ace6a311 Add RPM package type 2024-08-21 17:37:25 +03:00
6d4d707935 Release like in ALT
Refactoring the code for correct /etc/os-release processing.
2024-08-16 19:14:11 +03:00
661c1e14af New feature alt-linux
A new feature that adds support for ALT Linux.
2024-08-15 21:09:34 +03:00
959b7745f7 api-types: remove unused lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 12:08:01 +02:00
3129752da9 router: bump to 2.2.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 12:02:13 +02:00
c1a947c998 openid: bump to 0.10.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 12:00:58 +02:00
9ae3df424e product-config: bump to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 12:00:02 +02:00
5544c17ede log: bump to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:52:03 +02:00
ca04e129e4 client: bump to 0.4.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:51:22 +02:00
11e6173097 apt: bump to 0.11.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:48:32 +02:00
256bcfb0f1 rrd: bump to 0.3.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:47:35 +02:00
c1709d994e simple-config: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:44:26 +02:00
fc7a4be4eb uuid: bump to 1.0.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:43:11 +02:00
2c1d5fd741 daemon: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:42:00 +02:00
af9aa40d8b router: remove unused deps
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
89df077104 openid: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
c33cc9e350 product-config: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
d44ceba86d log: remove unused log dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
5e2179cf0a client: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
b01230f70c apt: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
9031fc88e1 rrd: remove unused libc dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
b6c6834d1f simple-config: remove unused log dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
c594d847b5 uuid: remove unused libc dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
709c237ebc daemon: Remove unused once_cell dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:38:48 +02:00
f36d0e5137 dns-api: bump to 0.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:38:45 +02:00
58e2fa5983 dns-api: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:36:49 +02:00
f94dc5b0a3 acme-api: bump to 0.1.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:36:32 +02:00
358bb76960 acme-api: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:35:45 +02:00
c319c5925d auth-api: bump to 0.4.5
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:35:15 +02:00
bdb28cc8bf network-api: bump to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:34:36 +02:00
693d206c94 rest-server: bump to 0.7.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:34:15 +02:00
21a39b3cda rest-server: remove use of once_cell
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:32:23 +02:00
901f2fb1bb rest-server: remove unused dependencies
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:32:23 +02:00
294a551e0e subscription: bump to 0.4.6-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:32:23 +02:00
3e8fa8b81c sys: bump to 0.6.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:32:20 +02:00
a86deb2783 sys: remove unused base64 dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 11:28:10 +02:00
a88be21074 time: bump to 2.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:08:23 +02:00
3fabb14d62 schema: bump to 3.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:06:44 +02:00
122f88d273 async: bump to 0.4.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-14 11:06:22 +02:00
b5f806797f cargo: remove lazy_static dependency on workspace
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
ab3f4a2fc4 schema: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
9530b75286 acme-api: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
0b19e344d7 auth-api: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
dde994ab57 network-api: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
8d5e864bf1 sys: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
669c39c59f time: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
25f83bce19 rest-server: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
692231d2a4 subscription: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
6c0f6890e4 async: remove lazy_static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
9693ceca7a dns-api: remove lazy-static dependency
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
9f9f736cfc cargo: set msrv to 1.80
In the following commits we make use of std::sync::LazyLock;

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-14 10:33:42 +02:00
cb509a1e6a client: bump to 0.4.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-13 15:45:20 +02:00
c54d5e06e6 client: change Token struct
API tokens between rust & perl code bases are inconsistent... this
needs fixing, but for now this is faster and more compatible.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-13 15:45:20 +02:00
9923f29622 section-config: bump to 2.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-13 15:45:20 +02:00
61d2eb891e section-config: make typed data usable with .with_type_key()
The original typed section config data would insert and remove the
type properties. With the introduction of `.with_type_key()` this is
done on the parse/write side instead, so we need to be able to opt out
of this.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-13 15:45:20 +02:00
3e83acfd89 api-types: rrd: use api-types from proxmox-rrd
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 13:04:57 +02:00
88af4ca549 rrd: bump to 0.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 13:03:27 +02:00
01947a9802 shared-cache: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 12:59:09 +02:00
7c6701aab9 shared-cache: remove unused dependency on proxmox-schema
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 12:57:42 +02:00
c352cb87a0 shared-cache: group and sort dependencies
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 12:56:30 +02:00
fa3c7690e7 sys: bump to 0.6.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 12:53:44 +02:00
93d42ad488 rrd: rustfmt and style fix
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 11:20:15 +02:00
c66e5432e7 shared-cache: minor style adaptation
(more concise & readable)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 11:10:48 +02:00
2d4050825d sys: don't duplicate the template path
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 11:06:25 +02:00
88731c52f0 rrd: add api-types
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
7ea63a6fb9 rrd: try to load database if not already present in cache
Before, a call to `update` was necessary to load an existing database
into the cache. If `update` was never called, `extract_cached_data`
would simply return no data.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
f01c1e0ce9 rrd: cache: add update_value_ignore_old
This function is the same as the regular `update_value`, but it
sets the `new_only` flag when updating the value in the rrd map.
This avoids "time in past" messages being logged in case a data point
happens to be added twice. This new function will just silently reject
values that have an older timestamp than the most recent one.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
4ca2e01442 rrd: deprecate create_pbs_default_rrd
This one should be in the client code.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
846e85ed2e cache: add benchmark example
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
0febb5045a cache: add new crate 'proxmox-shared-cache'
This crate contains a file-backed, rotating cache.
The cache should be safe to be accessed from multiple processes at
once.

The cache stores the value at the provided path.
If `keep_old` is >0, the cache will keep up to `keep_old` versions
around which can be queried via the `get_last` method.
The value and its history are stored in the same file,
each generation represented by a single line of JSON.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
2b6ecfd38d sys: add make_tmp_dir
Under the hood, this function calls `mkdtemp` from libc. Unfortunatly
the nix crate did not provide bindings for this function, so we have
to call into libc directly.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-09 11:01:30 +02:00
83c5ced7a9 sys: get rid of a try_block
by skipping over a utf8 scheck (serde_json can take a byte slice
already via `from_slice()`)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 09:13:39 +02:00
b266779ec0 sys: drop unused proxmox-time dependency
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-09 09:11:17 +02:00
642db84474 auth-api: docs: remove wrong return info
The method returns a boolean.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
920fc41b2f server: docs: fix unresolved link to systemd_notify
Fixes the cargo docs warning:

warning: unresolved link to `systemd_notify`
   --> proxmox-daemon/src/server.rs:314:6
    |
314 | /// [systemd_notify] with [SystemdNotify::Ready](proxmox_systemd::notify::SystemdNotify) when the
    |      ^^^^^^^^^^^^^^ no item named `systemd_notify` in scope
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
    = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
c8352531e4 serde: docs: escape html tags
Fixes the cargo docs warning:

warning: unclosed HTML tag `u8`
  --> proxmox-serde/src/lib.rs:55:18
   |
55 | /// Serialize Vec<u8> as base64 encoded string.
   |                  ^^^^
   |
   = note: `#[warn(rustdoc::invalid_html_tags)]` on by default
help: try marking as source code
   |
55 | /// Serialize `Vec<u8>` as base64 encoded string.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
2794c137d5 login: docs: fix typo and add escape html tags
Fixes the cargo docs warning:

warning: unclosed HTML tag `username`
  --> proxmox-login/src/api.rs:35:47
   |
35 |     /// realm is simply added to the username <username>@<relam>.
   |                                               ^^^^^^^^^^
   |
   = note: `#[warn(rustdoc::invalid_html_tags)]` on by default

warning: unclosed HTML tag `relam`
  --> proxmox-login/src/api.rs:35:58
   |
35 |     /// realm is simply added to the username <username>@<relam>.
   |                                                          ^^^^^^^

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
f80cb33993 ldap: docs: turn uri into link
Fixes the following cargo doc warning:

warning: this URL is not a hyperlink
   --> proxmox-ldap/src/lib.rs:199:9
    |
199 |     /// https://www.rfc-editor.org/rfc/rfc4512#section-5.1
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://www.rfc-editor.org/rfc/rfc4512#section-5.1>`
    |
    = note: bare URLs are not automatically turned into clickable links
    = note: `#[warn(rustdoc::bare_urls)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
0c889ff2da client: docs: remove redundant link
Fixes the cargo doc warnings:

warning: redundant explicit link target
 --> proxmox-http/src/client/mod.rs:4:19
  |
4 | //! in [`Client`](crate::client::Client).
  |         --------  ^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant
  |         |
  |         because label contains path that resolves to same destination
  |
  = note: when a link's destination is not specified,
          the label is used to resolve intra-doc links
  = note: `#[warn(rustdoc::redundant_explicit_links)]` on by default
help: remove explicit link target
  |
4 | //! in [`Client`].
  |        ~~~~~~~~~~

warning: redundant explicit link target
 --> proxmox-http/src/client/mod.rs:7:22
  |
7 | //! [`sync::Client`](crate::client::sync::Client).
  |      --------------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ explicit target is redundant
  |      |
  |      because label contains path that resolves to same destination
  |
  = note: when a link's destination is not specified,
          the label is used to resolve intra-doc links
help: remove explicit link target
  |
7 | //! [`sync::Client`].
  |     ~~~~~~~~~~~~~~~~

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
fe7f37e9b3 property_string: clippy: define bound once
Fixes the clippy lint:

warning: bound is defined in more than one place
   --> proxmox-schema/src/property_string.rs:352:14
    |
352 | pub fn parse<T: ApiType>(value: &str) -> Result<T, Error>
    |              ^
353 | where
354 |     T: for<'de> Deserialize<'de>,
    |     ^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#multiple_bound_locations
    = note: `#[warn(clippy::multiple_bound_locations)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
77fe0f6954 docs: clippy: add indentation to doc list items
Fixes the clippy warning:

warning: doc list item missing indentation
   --> proxmox-subscription/src/subscription_info.rs:179:9
    |
179 |     ///  (this mode is used to decide whether to refresh the subscription information)
    |         ^
    |
    = help: if this is supposed to be its own paragraph, add a blank line
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
    = note: `#[warn(clippy::doc_lazy_continuation)]` on by default
help: indent this line
    |
179 |     ///   (this mode is used to decide whether to refresh the subscription information)
    |          +

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
7052972212 apt: clippy: don't clone types implementing Copy
Fixes the clippy warnings:

warning: `proxmox-apt` (lib) generated 1 warning
warning: using `clone` on type `Option<[u8; 32]>` which implements the `Copy` trait
   --> proxmox-apt/tests/repositories.rs:117:22
    |
117 |     let old_digest = file.digest.clone().unwrap();
    |                      ^^^^^^^^^^^^^^^^^^^ help: try removing the `clone` call: `file.digest`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
    = note: `#[warn(clippy::clone_on_copy)]` on by default

warning: using `clone` on type `[u8; 32]` which implements the `Copy` trait
   --> proxmox-apt/tests/repositories.rs:135:24
    |
135 |     file.digest = Some(old_digest.clone());
    |                        ^^^^^^^^^^^^^^^^^^ help: try removing the `clone` call: `old_digest`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-08-07 20:58:04 +02:00
35e466410c fix typos in strings
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-07 16:49:31 +02:00
0106a14d43 fix typos in rust documentation blocks
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-08-07 16:49:31 +02:00
c3713cffe5 api-macro: fix warnings in tests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:23:38 +02:00
a7ab26a9d8 http-error: fix a warning in tests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:22:25 +02:00
15c64a5d00 api-macro: bump to 1.1.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:15:57 +02:00
451d3b0adf section-config: bump to 2.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:15:34 +02:00
839f508f55 api-macro: type-key support for derived enums
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:14:42 +02:00
4a154b3cb5 section-config: support a type_key property
For when the underlying datatype is supposed to contain the type
property and the schema does not mark it as optional.

The use case here is to support flat `Remote` type where the "type" of
pve/pmg/pbs is a property which is present in the `Remote` struct
while being derived from the section type.

This will implicitly include and strip the type of the json object
after/before de/serializing.

Alternatives would be
- to mark the type as optional and just fill it out later when loading
  the data, but that is technically wrong...
- have a 2nd version of the struct with the type field removed and
  From/Into implemented, but that's even more unwieldy.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:14:42 +02:00
4d96aa52d2 section-config, api-macro: add SectionConfig enum support
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:14:42 +02:00
51d78fdd2b api-macro: handle renames in updater derive
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:14:42 +02:00
4c37db22d2 sys: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 14:14:33 +02:00
1cb2bf85ba sys: bump to 0.6.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-06 13:50:57 +02:00
d0dab46539 sys: make fd::cwd crate-internal
it's not used by anything outside of proxmox-sys.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-08-06 13:37:36 +02:00
dc0b9dec4a sys: adapt to IO Safety changes in rustc
`OwnedFd`s are now (rustc 1.80+) checked for validity when dropped in a debug
build, to catch usage after closing. Unfortunately those checks don't account
for the special value `AT_FDCWD` (-100) which is not a "real" FD, but a magic
constant used by many libc functions to signify operations starting at the
current working directory.

changing our `cwd` helper to open the CWD for real, instead of just returning
the magic value that pretends to be an FD, works around those limitations with
the least API churn.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-08-06 13:37:34 +02:00
9d921901d3 rest-server: update examples code to daemon split
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-08-01 10:27:44 +02:00
bedbaae252 router: bump to 2.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-31 10:08:25 +02:00
65715bc096 io, serde, schema: doc fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-30 16:16:16 +02:00
ffd45c642f http: replace deprecated io_err_other
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-30 16:11:44 +02:00
482fd62423 io: drop the valgrind support code
valgrind-request is currently not packaged and we haven't used this in
a while...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-30 16:09:09 +02:00
a44fda92ef bump proxmox-log to 0.2.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-07-29 09:27:18 +02:00
7c403de278 lang: d/control bump
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 13:23:53 +02:00
a7ba12d0b8 lang: bump to 1.4.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 13:13:29 +02:00
e3a5ff78f4 async, sys: replace deprecated io_err_other
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 13:09:33 +02:00
ce0b21805c lang: deprecate io_err_other
For regular error cases, `std::io::Error` can now also box errors and
provides an `Error::other()` function since rust 1.74.

For the case where it was used directly with strings
(io_err_other("string")) -> that's what `io_format_err!()` and
`io_bail!()` are for.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 12:14:03 +02:00
ea3c37fd36 sys: drop unused import
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 12:14:03 +02:00
034bb9cdda router: cli: add OutputFormat enum api type
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 11:46:33 +02:00
dd36fec23d sys: replace CStr::from_bytes_... with c"literals"
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-26 11:46:33 +02:00
ba304a4f83 compression: make Deflate{De, En}coderBuilder public
These structs are returned by the public method
`Deflate{En,De}coder::builder`.

Reported-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-07-26 11:43:01 +02:00
2598699fcb daemon: remove useless comment
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 16:30:14 +02:00
35372b5337 daemon: boxed FnOnce has been usable for a while
While technically an API break, we don't use the public API for this
anywhere and the trait we're changing is explicitly marked as
`#[doc(hidden)]`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 16:27:32 +02:00
4502bc3b73 acme-api: bump to 0.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:33:36 +02:00
150f203209 auth-api: bump to 0.4.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:32:28 +02:00
15200b9f64 rest-server: bump to 0.7.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:32:01 +02:00
921d2bcc13 daemon: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:29:06 +02:00
f67c651929 systemd: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:29:06 +02:00
b54b4d3324 log: bump to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:29:06 +02:00
d3abd366c4 introduce proxmox-daemon crate
split from rest-server:
- "state" module (shutdown/reload state)
- shutdown futures
- "daemon" module (named 'server' module in proxmox-daemon)
- command socket

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:25:50 +02:00
fb1a75d48f systemd: add fd-store support
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:25:50 +02:00
2783f062a6 update rest-server to use proxmox-systemd crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:25:50 +02:00
ab41d326e4 introduce proxmox-systemd crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:25:50 +02:00
cecd08df58 log: remove unused init_logger argument
The `_application_name` argument is not used anymore. It was previously
used to set the correct journald unit-name, which is now handled
automatically.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-07-24 14:25:50 +02:00
a38b46b27d rest-server: drop proxmox-io dependency
Used only for read/writing a pid_t (an integer) to a socket.
The standard to_ne_bytes()/from_ne_bytes() should be sufficient here,
we already have libc::pid_t which we can use to get the correct type
namespace for accessing ::from_ne_bytes().

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:06:40 +02:00
c0e5776edd log: fix filter condition for journal layer
The condition was inverted, it would only send to the journal for
worker tasks.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-24 14:05:51 +02:00
f2130b69c8 router: bump to 2.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-22 09:07:22 +02:00
45a1e580c8 access-control: bump to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-22 09:05:28 +02:00
5eb7cbd250 cli: deal with commands without positional args
When reaching the final command we relied on the positional parameters
"ending" the global option parsing. This means we did not get global
options at the end, and failed with "unknown option" instead.
Fix this by simply retaining unknown options in that case and not
stopping at positional parameters.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-22 09:03:56 +02:00
fee00addab access-control: add init_user_config() method
So that we can make sure root@pam exists at the product level.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-22 09:03:56 +02:00
140fc0ad08 access-control: cleanup use statements
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-22 09:03:56 +02:00
c8b975799b fix typos in strings
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-07-22 08:49:42 +02:00
c88cdd7e67 fix typos in variable and function names
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-07-22 08:49:42 +02:00
254a37ae07 fix typos in code documentation
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-07-22 08:49:42 +02:00
72ab48eb55 fix typos in rust api documentation
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-07-22 08:49:42 +02:00
a50d2c715e rest-server: Encode with zlib headers
As per [RFC9110] the Deflate encoding is a "zlib" data format. This
makes the rest-server compatible with the http-client.

[RFC9110] https://www.rfc-editor.org/rfc/rfc9110#field.content-encoding

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2024-07-22 08:11:14 +02:00
500fb592f9 log: reorder filters as a small optimization
Reorder the filters for the journald layer. This sets the LevelFilter
last, which means tracing can disable all log statements lower than the
current level without evaluating the LogContext::exists function.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
 [ TL: note that this is just an optimization in the subject ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-07-18 12:11:43 +02:00
5262cefd34 router: bump to 2.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 14:28:07 +02:00
ff17bf5a2b router: don't deprecate generate_usage_str
This is used for the 'debug' binary to show the usage of an API
method.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 14:28:07 +02:00
3dc013bb57 schema: bump to 3.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 14:18:04 +02:00
4de22b9728 api-macro: fix warnigns
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
cff04a4502 router: cli: add parser option tests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
492cac4346 router: cli: simplify parsing logic
Deduplicate the check for whether the argument exists by trying to
fetch the schema once and use check the option instead of calling
'.contains_key()' multiple times.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
9288c00372 router: cli: move freestanding new parse loop into its method
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
95c0614ccd router: cli: improve doc-gen global options handling
Passing the &GlobalOptions through is more telling than an opaque
Iterator<&str>...

Also: actually generate the property descriptions in non-ReST mode for
global options as well, so the `help` output for a specific command
includes the property documentation instead of only showing the name.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
d872eb9d7e router: cli: rework newline handling for doc and help output
The rules are as follows:
- An "item" by itself does neither start nor end with a newline.
- Where items are connected, the appropriate amount of newlines must
  be inserted, assuming items do not have any trailing newlines.

Otherwise this just gets WAY too confusing everywhere!

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
667fa6bc6b router: cli: doc generation with global options
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
69c2f94aab schema: drop extra newline in proeprty description
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
f02ce77ad6 schema: make wrap_text less awkward
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
dc7273e888 schema: drop trailing double-newlines in wrap_text
This is completely wrong and make working with it extremely annoying.
Whether or not there should be separation should be decided where
multiple elements are connected, they shouldn't automatically come
with a bunch of trailing new lines for absolutely no reason.

Places using this will need to be fixed as they get noticed.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
d240ef1e92 router: set help context on help invocation
instead of during parsing...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
83b3c1794a router: completion callbacks for global options
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
afe746b02f router: let completion take global options into account
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
420e238126 router: hook help/completion/docgen into new cli parser
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
eb5614adf1 router: new cli parser with global option support
This one does *explicitly* *not* support long options with a single
dash because it is too ambiguous if we want to add support for short
options at some point.

The parsing of the command line and invoking of the command is
separated. `CommandLine::parse` returns an `Invocation` which is
called and consumed via its `call` method.
This allows updating the CLI environment between parsing and invoking
the command, in order to allow *handling* the global options in
between those two steps if desired.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
59f1bdbe85 router: cli: store extra CLI args by type
The CLI environment can now contain ApiType structs which can be
accessed by their type.
The TypeId is used since the options inside must be unique anyway and
we can't have the same type specified multiple times. It also makes
for a somewhat convenient interface:

    env.take_global_option::<ConnectInfo>()

where ConnectInfo is a struct containing the server, user, port, ...
since these will not be passed as *parameters* to the API functions.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
41b08323a7 router: AsAny: add as_any_mut
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-17 13:43:37 +02:00
f78c28dd11 acme: update d/control
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-07-17 11:41:11 +02:00
d1aa14eb71 apt: bump version to 0.11.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-07-17 11:39:46 +02:00
f41664e087 workspace: bump dependency for apt-api-types to 1.0.1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-07-17 10:07:43 +02:00
301e268fc4 apt-api-types: bump version to 1.0.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-07-17 09:54:33 +02:00
79f2b89d29 apt: updates for changed api (digest as array)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-17 09:50:29 +02:00
bcca060a93 apt-api-types: fix backward compatibility by encoding digest as array
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-17 09:45:07 +02:00
282e00d429 syslog-api: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-12 11:13:24 +02:00
870ec33574 log: documentation fixup
The scope and sync_scope methods simply activate the context, they
don't affect the counter, the counter is initialized when creating the
context with LogContext::new().

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-12 10:51:08 +02:00
64ff97d8e2 time-api: bump to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:31:50 +02:00
ae92195687 network-api: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:28:39 +02:00
98adeb73f2 dns-api: bump to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:27:13 +02:00
540e9a8134 auth-api: bump to 0.4.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:26:00 +02:00
e294d74026 acme-api: bump to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:25:25 +02:00
978f28d67c subscription: bump to 0.4.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:24:52 +02:00
a6a9ca1d70 rrd: bump to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:24:52 +02:00
9d1758dfa2 openid: bump to 0.10.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:24:52 +02:00
69e410d130 notify: bump to 0.4.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:24:52 +02:00
aa29c54859 shared-memory: bump to 0.3.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:24:52 +02:00
ad60e1bde5 rest-server: bump to 0.6.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:13:36 +02:00
b21034b485 http: bump to 0.9.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:13:36 +02:00
68125e67fc product-config: bump to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 15:11:39 +02:00
24210a3a86 apt: bump 0.11.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:50:54 +02:00
bf02bebbac access-control: bump to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:47:30 +02:00
355d949cd4 log: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:47:28 +02:00
1a00570898 sys: bump to 0.6.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:47:27 +02:00
36e552de47 worker-task: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:47:25 +02:00
4b9c907b68 log: introduce a shareable LogContext struct
Since hyper can spawn() more tasks, when we stop passing `WorkerTask`
references down the stack, we still need to be able to *inherit* the
current logging context. Hyper provides a way to replace its used
`spawn()` method, so we need to provide a way to reuse the logging
context.

Instead of having the `FileLogger` and warn counter separately
available with local-only access, put them behind an Arc<Mutex<>>.
Previously they already *were* behind an Arc<Mutex<>> as part of the
WorkerTaskState.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:45:26 +02:00
847a57740b new worker-task crate, move WorkerTaskContext from sys
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
f3021e686a sys: remove email module
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
3c1c34043e notify: copy sendmail/forward fn's from proxmox_sys
proxmox_notify is the only user of those functions, so it makes
sense to move them here. A future commit will mark the
original functions from proxmox_sys as deprecated.

The functions were slightly modified, mostly to not
rely on anyhow for error reporting. Also they
are now private functions.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-07-11 14:43:37 +02:00
c6cccff92e sys: remove deprecations
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
3dde52e5ce log: deny(unsafe_op_in_unsafe_fn) and feature(doc_cfg)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
cbc30882e7 acme-api: adapt to tracing infrastructure
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
2fd7b13fbe log: reexport the logging macros
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
ddb91a6594 enable tracing logger, remove task_log macros
Enable the tracing-system by setting the LOGGER task local variable
to a instance of a FileLogger and initializing the WARN_COUNTER.
Removed the task_log! macros and some occurences.

Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
[WB: remove flog! import in doctests]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
0550659cd1 proxmox-log: add tracing infrastructure
Add the `proxmox_log` crate which includes the new logging infra.
Export the `init_logger` function, which creates the `tracing` logger
that includes the default subscriber and two layer.

The first layer comes from the tracing-journald crate and logs
everything that does not come from a worker-task/thread to the syslog.
The second layer filters the exact opposite and writes the logs into the
corresponding task-log file.

Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-07-11 14:43:37 +02:00
f1920d9b94 rest-server: fix a build warning
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-11 14:43:37 +02:00
9c3e4d5ccf rest-server: bump version to 0.5.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-10 12:44:29 +02:00
ce802d8320 rest-server: drop some unnecessary 'pub's
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-10 12:37:51 +02:00
f6bacbb58f fix #5105: rest-server: connection: overhaul TLS handshake check logic
On rare occasions, the TLS "client hello" message [1] is delayed after
a connection with the server was established, which causes HTTPS
requests to fail before TLS was even negotiated. In these cases, the
server would incorrectly respond with "HTTP/1.1 400 Bad Request"
instead of closing the connection (or similar).

The reasons for the "client hello" being delayed seem to vary; one
user noticed that the issue went away completely after they turned off
UFW [2]. Another user noticed (during private correspondence) that the
issue only appeared when connecting to their PBS instance via WAN, but
not from within their VPN. In the WAN case a firewall was also
present. The same user kindly provided tcpdumps and strace logs on
request.

The issue was finally reproduced with the following Python script:

  import socket
  import time

  HOST: str = ...
  PORT: int = ...

  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
      sock.connect((HOST, PORT))
      time.sleep(1.5) # simulate firewall / proxy / etc. delay
      sock.sendall(b"\x16\x03\x01\x02\x00")
      data = sock.recv(256)
      print(data)

The additional delay before sending the first 5 bytes of the "client
hello" message causes the handshake checking logic to incorrectly fall
back to plain HTTP.

All of this is fixed by the following:

  1. Increase the timeout duration to 10 seconds (from 1)
  2. Instead of falling back to plain HTTP, refuse to accept the
     connection if the TLS handshake wasn't initiated before the
     timeout limit is reached
  3. Only accept plain HTTP if the first 5 bytes do not correspond to
     a TLS handshake fragment [3]
  4. Do not take the last number of bytes that were in the buffer into
     account; instead, only perform the actual handshake check if
     5 bytes are in the peek buffer using some of tokio's low-level
     functionality

Regarding 1.: This should be generous enough for any client to be able
to initiate a TLS handshake, despite its surrounding circumstances.

Regarding 4.: While this is not 100% related to the issue, peeking into
the buffer in this manner should ensure that our implementation here
remains correct, even if the kernel's underlying behaviour regarding
edge-triggering is changed [4]. At the same time, there's no need for
busy-waiting and continuously yielding to the event loop anymore.

[1]: https://www.rfc-editor.org/rfc/rfc8446.html#section-4.1.2
[2]: https://forum.proxmox.com/threads/disable-default-http-redirects-on-8007.142312/post-675352
[3]: https://www.rfc-editor.org/rfc/rfc8446.html#section-5.1
[4]: https://lwn.net/Articles/864947/

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 12:22:17 +02:00
847ca5d14d rest-server: connection: log peer address on error
.. in order to make debugging easier and logs more helpful.

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 12:22:17 +02:00
0d3e7c8eaf rest-server: connection: clean up accept data flow
This adds the structs `AcceptState` and `AcceptFlags` and adapts
relevant method signatures of `AcceptBuilder` accordingly. This makes
it easier to add further parameters in the future.

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 12:22:17 +02:00
9f33be3078 http: bump version to 0.9.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-10 12:17:59 +02:00
2269153522 compression: bump version to 0.2.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-10 12:15:54 +02:00
4d1c4ec829 http: teach the Client how to decode deflate content
The Backup Server can compress the content using deflate so we teach the
client how to decode it.

If a request is sent with the `Accept-Encoding` [2] header set to
`deflate`, and the response's `Content-Encoding` [1] header is equal to
`deflate` we wrap the Body stream with a stream that can decode `zlib`
on the run.

Note that from the `Accept-Encoding` docs [2], the `deflate` encoding is
actually `zlib`.

This can be also tested against
http://eu.httpbin.org/#/Response_formats/get_deflate by adding the
following test:

```rust
    #[tokio::test]
    async fn test_client() {
        let client = Client::new();
        let headers = HashMap::from([(
            hyper::header::ACCEPT_ENCODING.to_string(),
            "deflate".to_string(),
        )]);
        let response = client
            .get_string("https://eu.httpbin.org/deflate", Some(&headers))
            .await;
        assert!(response.is_ok());
    }
```

at `proxmox-http/src/client/simple.rs` and running

```
cargo test --features=client,client-trait
```

[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
[2] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding

Suggested-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 11:37:36 +02:00
8b8957b5ba compression: deflate: add test module
We test the deflate encoder against the deflate decoder using (or not)
zlib and with different small buffer sizes. We also test compression and
decompression against the flate2 crate.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 11:37:36 +02:00
e6ca8d6049 compression: deflate: add a decoder
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 11:37:36 +02:00
3f09610d86 compression: deflate: add builder pattern
This allows creating a encoder in a more general way and allows to
specify whether we want to set zlib headers. This is useful to compress
HTTP traffic, as per [1].

[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding#directives

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 11:37:36 +02:00
70a71e0c8e compression: deflate: move encoder into a mod
This allows to add a decompression mod inside the deflate mod. This does
not touch the public API.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2024-07-10 11:37:36 +02:00
7ae351854e apt-api-types: derive PartialEq for all types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-09 13:15:16 +02:00
54dcb0942c syslog-api: add helper for mini-journalreader
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-09 11:29:50 +02:00
4a69e1cf64 use new apt/apt-api-types crate 2024-07-08 15:28:59 +02:00
3479a9afe4 apt: bump version to 0.11.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-08 15:21:49 +02:00
67671399e8 apt-api-types: bump version to 1.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-08 15:20:40 +02:00
75c62574c6 apt: avoid global apt config
Because it was only used for the test setup. Instead, we simply
add an apt_lists_dir parameter where we need it.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-08 15:17:10 +02:00
f451a643ae apt: add cache feature
Save/read package state from a file, and add the api functions to manipulate
that state.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-08 15:17:10 +02:00
f536a91b2f apt: use api types from apt-api-types crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-08 15:17:10 +02:00
e56e39185a apt: avoid direct impl on api types (use traits instead)
So that we can use api types from expternal crate proxmox-apt-api-types.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-08 15:17:10 +02:00
960131925e apt-api-types: use serde-plain to display/parse enums
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-08 15:17:10 +02:00
b11dbfd105 apt-api-types: new crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-07-08 15:17:10 +02:00
9ec5a48701 access-control: bump to 0.2.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-04 14:33:42 +02:00
e8b5ad6b45 access-control: use ConfigDigest for digests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-04 14:31:24 +02:00
3545d67b1f auth-api: bump version to 0.4.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-03 15:26:23 +02:00
2ca71fe601 tfa: bump version to 5.0.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-03 15:25:39 +02:00
5079ff6a3c tfa: webauthn: serialize OriginUrl following RFC6454
We serialize `OriginUrl` using the ASCII serialization mentioned at
[RFC6454] section 6.2 or [1]. Note that the unicode serialization is not
used widely adopted [2].

Note that `url::Url` serialize with a trailign slash, e.g.
https://foo.bar serializes as https://foo.bar/ which is not the origin
for this domain.

[RFC6454] https://www.rfc-editor.org/rfc/rfc6454
[1] https://html.spec.whatwg.org/multipage/browsers.html#ascii-serialisation-of-an-origin
[2] https://html.spec.whatwg.org/multipage/browsers.html#unicode-serialisation-of-an-origin

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-07-03 15:20:30 +02:00
0652d81977 tree-wide: enable doc_cfg and doc_auto_cfg for docs
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-07-02 11:59:53 +02:00
635c8bcbed client: clippy: factor out complex type
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-28 11:38:59 +02:00
1f9cb87576 sys: process_locker: explicitly don't truncate the lock file
clippy rightfully complains about a create() with an unspecified
truncation behavior. This file has no contents so let's just not
truncate it in case we ever want to also have data in it...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-28 11:18:04 +02:00
b2dd0117d1 acme-api: allow clippy::manual_map where .map doesn't make sense
The code chooses whichever one of a multitude of functions returns
Some, switching to .map for the final else would make it less
readable.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-28 11:14:30 +02:00
a3c1d9d456 network-api: drop unnecessary string allocations
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-28 11:09:48 +02:00
52cf0c05f5 auth-api: do not clone struct implementing Copy
Fixes the clippy warning:

warning: using `clone` on type `Option<&dyn AuthContext>` which implements the `Copy` trait
   --> proxmox-auth-api/src/api/mod.rs:111:5
    |
111 | /     AUTH_CONTEXT
112 | |         .lock()
113 | |         .unwrap()
114 | |         .clone()
    | |________________^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
    = note: `#[warn(clippy::clone_on_copy)]` on by default
help: try dereferencing it
    |
111 ~     (*AUTH_CONTEXT
112 +         .lock()
113 +         .unwrap())
    |

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 11:09:48 +02:00
229cc6ae02 acl: directly return struct rather than a binding
Fixes the following clippy warning:

warning: returning the result of a `let` binding from a block
   --> proxmox-access-control/src/acl.rs:687:13
    |
686 |             let config = TestAcmConfig { roles };
    |             ------------------------------------- unnecessary `let` binding
687 |             config
    |             ^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
    = note: `#[warn(clippy::let_and_return)]` on by default
help: return the expression directly
    |
686 ~
687 ~             TestAcmConfig { roles }
    |

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
a4b57d6c3c acme: derive Default for Status
Fixes the clippy warning:

warning: this `impl` can be derived
  --> proxmox-acme/src/order.rs:36:1
   |
36 | / impl Default for Status {
37 | |     fn default() -> Self {
38 | |         Status::New
39 | |     }
40 | | }
   | |_^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
   = note: `#[warn(clippy::derivable_impls)]` on by default
   = help: remove the manual implementation...
help: ...and instead derive it...
   |
12 + #[derive(Default)]
13 | pub enum Status {
   |
help: ...and mark the default variant
   |
15 ~     #[default]
16 ~     New,
   |

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
baccbda477 acme: elide explicit lifetimes
Fixes the clippy warning:

warning: the following explicit lifetimes could be elided: 'a
  --> proxmox-acme/src/async_client.rs:65:30
   |
65 |     pub async fn new_account<'a>(
   |                              ^^
66 |         &'a mut self,
   |          ^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
   = note: `#[warn(clippy::needless_lifetimes)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
6b1e4b83bb http: remove unnecessary cast
Fixes the clippy warning:

warning: casting to the same type is unnecessary (`usize` -> `usize`)
   --> proxmox-http/src/websocket/mod.rs:446:40
    |
446 |             mask.copy_from_slice(&data[mask_offset as usize..payload_offset as usize]);
    |                                        ^^^^^^^^^^^^^^^^^^^^ help: try: `mask_offset`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
    = note: `#[warn(clippy::unnecessary_cast)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
b006e66361 http: remove redundant redefinition of binding
Fixes the clippy error:

error: redundant redefinition of a binding `data`
   --> proxmox-http/src/websocket/mod.rs:375:9
    |
375 |         let data = data;
    |         ^^^^^^^^^^^^^^^^
    |
help: `data` is initially defined here
   --> proxmox-http/src/websocket/mod.rs:369:27
    |
369 |     pub fn try_from_bytes(data: &[u8]) -> Result<Option<FrameHeader>, WebSocketError> {
    |                           ^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals
    = note: `#[deny(clippy::redundant_locals)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
82bc4db723 network-api: remove useless uses of format!
Fixes the clippy warning:

warning: useless use of `format!`
   --> proxmox-network-api/src/config/mod.rs:632:13
    |
632 | /             format!(
633 | |                 r#"
634 | | iface enp3s0 inet static
635 | |     address 10.0.0.100/16
636 | |     gateway 10.0.0.1"#
637 | |             )
    | |_____________^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
help: consider using `.to_string()`
    |
632 ~             r#"
633 + iface enp3s0 inet static
634 ~     address 10.0.0.100/16
635 ~     gateway 10.0.0.1"#.to_string()

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
d965713565 shared-memory: remove unneeded generic parameter
Fixes the clippy warning:

warning: type parameter `T` goes unused in function definition
  --> proxmox-shared-memory/tests/raw_shared_mutex.rs:80:19
   |
80 | fn create_test_dir<T: Init>(filename: &str) -> Option<PathBuf> {
   |                   ^^^^^^^^^ help: consider removing the parameter
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters
   = note: `#[warn(clippy::extra_unused_type_parameters)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
a87d52dad3 remove unneeded returns
Fixes the clippy warning:

warning: unneeded `return` statement
   --> proxmox-tfa/src/api/mod.rs:468:17
    |
468 | /                 return TfaResult::Failure {
469 | |                     needs_saving: true,
470 | |                     tfa_limit_reached,
471 | |                     totp_limit_reached,
472 | |                 };
    | |_________________^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
    = note: `#[warn(clippy::needless_return)]` on by default
help: remove `return`
    |
468 ~                 TfaResult::Failure {
469 +                     needs_saving: true,
470 +                     tfa_limit_reached,
471 +                     totp_limit_reached,
472 ~                 }
    |

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
d07a0243f4 use const blocks in thread_local! calls
Fixes the clippy warning:

warning: initializer for `thread_local` value can be made `const`
   --> proxmox-router/src/cli/command.rs:221:71
    |
221 |     static HELP_CONTEXT: RefCell<Option<Arc<CommandLineInterface>>> = RefCell::new(None);
    |                                                                       ^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(None) }`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#thread_local_initializer_can_be_made_const
    = note: `#[warn(clippy::thread_local_initializer_can_be_made_const)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
e3602c1943 acl: remove null pointer cast
Fixes the clippy warning:

warning: casting raw pointers to the same type and constness is unnecessary (`*mut fs::acl::libc::c_void` -> `*mut fs::acl::libc::c_void`)
   --> proxmox-sys/src/fs/acl.rs:130:23
    |
130 |         let mut ptr = ptr::null_mut() as *mut c_void;
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr::null_mut()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
    = note: `#[warn(clippy::unnecessary_cast)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
30461d091c acme: remove duplicated attribute
Fixes the following clippy warning:

warning: duplicated attribute
  --> proxmox-acme/src/lib.rs:42:7
   |
42 | #[cfg(feature = "impl")]
   |       ^^^^^^^^^^^^^^^^
   |
note: first defined here
  --> proxmox-acme/src/lib.rs:41:7
   |
41 | #[cfg(feature = "impl")]
   |       ^^^^^^^^^^^^^^^^
help: remove this attribute
  --> proxmox-acme/src/lib.rs:42:7
   |
42 | #[cfg(feature = "impl")]
   |       ^^^^^^^^^^^^^^^^
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
   = note: `#[warn(clippy::duplicated_attributes)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
bf68d540b8 time-api: remove redundant field names
Fixes the clippy warning:

warning: redundant field names in struct initialization
  --> proxmox-time-api/src/time_impl.rs:53:9
   |
53 |         localtime: localtime,
   |         ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `localtime`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
18dda8106b remove unnecesary pub(self)
Fixes the clippy warning:

warning: unnecessary `pub(self)`
    --> proxmox-tfa/src/api/mod.rs:1268:1
     |
1268 | pub(self) fn bool_is_false(v: &bool) -> bool {
     | ^^^^^^^^^ help: remove it
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pub_self

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
aff76f9e0e remove needless borrows
Fixes the following clippy warnings:

warning: the borrowed expression implements the required traits
  --> proxmox-tfa/src/api/recovery.rs:86:24
   |
86 |         Ok(hex::encode(&hmac))
   |                        ^^^^^ help: change this to: `hmac`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args

and

warning: this expression creates a reference which is immediately dereferenced by the compiler
   --> proxmox-network-api/src/api_impl.rs:108:47
    |
108 |                 interface.set_bond_slave_list(&slaves)?;
    |                                               ^^^^^^^ help: change this to: `slaves`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
    = note: `#[warn(clippy::needless_borrow)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
cb4acf6d93 use contains_key instead of .get().is_{some, none}()
Fixes the clippy lints:

warning: unnecessary use of `get("lo").is_none()`
   --> proxmox-network-api/src/config/parser.rs:603:30
    |
603 |         if config.interfaces.get("lo").is_none() {
    |            ------------------^^^^^^^^^^^^^^^^^^^
    |            |
    |            help: replace it with: `!config.interfaces.contains_key("lo")`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check
    = note: `#[warn(clippy::unnecessary_get_then_check)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
16aa2b74bc use unwrap_or_default instead of unwrap_or(Vec::new)
Fixes the clippy warning:

warning: use of `unwrap_or_else` to construct default value
    --> proxmox-tfa/src/api/mod.rs:1355:43
     |
1355 |         |cap| cap.map(Vec::with_capacity).unwrap_or_else(Vec::new),
     |                                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_default
     = note: `#[warn(clippy::unwrap_or_default)]` on by default

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-06-28 10:22:58 +02:00
8c9eb85706 ldap: fix Cargo.toml syntax
this throws a warning now..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-25 13:54:26 +02:00
af353659c8 run cargo fmt
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-24 10:02:31 +02:00
1ba198265c trivial clippy fix
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-24 10:01:13 +02:00
3663ae8255 async: bump to 0.4.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 14:30:10 +02:00
0e17606caf rest-server: bump to 0.5.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 14:06:12 +02:00
51680ea77e openid: bump to 0.10.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 14:04:41 +02:00
0a0d8a4d73 notify: bump to 0.4.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 14:02:10 +02:00
eededbeb93 dns-api: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 14:00:49 +02:00
eca6f31a22 compression: bump to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:59:46 +02:00
80baf82df2 tfa: bump to 4.1.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:58:55 +02:00
f1a5583932 acme-api: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:57:53 +02:00
2f77909a6d access-control: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:56:31 +02:00
19200f7415 rrd: bump to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:56:06 +02:00
54bc3abdfd subscription: bump to 0.4.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:55:39 +02:00
821fdb63b0 serde: bump to 0.1.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:55:39 +02:00
3cd7223cc1 sys: bump to 0.5.8-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:55:39 +02:00
ab41b5ce43 time-api: bump to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:49:56 +02:00
bb8460bc0f time: bump to 2.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:49:56 +02:00
214dbdf9a5 time: remove deprecated functions
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:48:42 +02:00
533954ed38 bump bitflags dependency to 2.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 13:48:32 +02:00
c96ad06247 move .cargo/config to .cargo/config.toml
the old location has been deprecated for a while, and rustc 1.78 will start to warn about it.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-20 12:29:30 +02:00
0f33d603ef router: bump to 2.1.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:56:05 +02:00
57fb1004b8 sys: bump to 0.5.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:56:05 +02:00
d8eb6d1bde lang: bump to 1.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:56:05 +02:00
b16922860a sys: make xattr CStrs constants, repalce c_str! macro
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:56:05 +02:00
0233c8c63b router: repalce c_str! with c"literals"
we can now drop the proxmox-lang dependency here

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:55:10 +02:00
38992a588a lang: deprecate c_str! and offsetof
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:55:10 +02:00
2e9526dcdd tfa: fix a compile warning
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-20 10:55:10 +02:00
937d985489 build: adapt workspace member command
to work with cargo 1.77

Originally-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-19 16:04:35 +02:00
1c5f27014c time: drop TryFrom/TryInto imports
they're in the prelude by now

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 14:55:41 +02:00
a4be52d4a6 time: exclude certain use statements and impl block on wasm32
otherwise the compiler will complain that they aren't used when
compiling the code for wasm32.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 14:55:41 +02:00
c547ea07ae access-control: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 14:45:52 +02:00
4197c0e26e access-control: minor code cleanup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 14:42:43 +02:00
5daf898b14 access-control: cleanup comment in Cargo.toml
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 14:42:43 +02:00
46d8423d72 access-control: split crate in default and impl features
this way the types defined in this crate can be re-used in places
without necessarily having to use the ACL, token shadow and
(cached) user config implementations.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 14:42:43 +02:00
1dc88b5e3c access-control: move to flatten User into UserWithToken
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 14:42:41 +02:00
48bd72763f access-control: increment user cache generation when saving acl config
since `CachedUserInfo` takes care of both, the user config and the acl
config, we need to also bump the cache generation when storing the
acl config.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 14:42:39 +02:00
84537a02b1 access-control: factor out user config handling
this commit factors out the user config. it also add two new functions
to the `AccessControlConfig` trait to handle caching in a more
generalized way.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 13:38:56 +02:00
ed6a17cec9 access-control: make token shadow implementation re-usable
this commit factors out the token shadow implementation from
`proxmox-backup` so it can be used in other products.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 13:38:54 +02:00
47eeecf711 access-control: define User, UserWithTokens and ApiTokens types
these types are used by the user config in `proxmox-backup` server.
this commit factors them out so we can re-use them in other products
as well as this crate.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 13:38:52 +02:00
86ffeef24a access-control: add the proxmox-access crate to reuse acl trees
this commit factors out the acl tree from proxmox-backup so we can
re-use it accross other products. to use it, the product needs to
implement the `AcmConfig` trait and provide this crate with a
location to safe its configuration files.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
2024-06-19 13:38:50 +02:00
c336cb9ab7 io: bump to 1.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:21:43 +02:00
74ecd47421 lang: bump to 1.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:20:57 +02:00
36032b892b time-api: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:14:04 +02:00
dfa1c0ce39 network-api: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:13:08 +02:00
ef7d4c6155 dns-api: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:11:43 +02:00
90f954005a auth-api: bump to 0.4.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:10:41 +02:00
a0c1369000 acme-api: bump to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:08:04 +02:00
e2cd917394 product-config: bump to 0.2.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 12:07:37 +02:00
cf5efb5c0a cleanup use statements
much more merge friendly this way...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 11:52:39 +02:00
e20cdbf8e2 router: bump to 2.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 11:33:02 +02:00
29b55dbcb3 router: make regex dep optional
It's only used in cli code.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 10:26:20 +02:00
e4afb0fe20 router: cli: add confirmation helper
Add confirmation helper that outputs a prompt and lets the user
confirm or deny it.
Implemented to close #4763.

Co-authored-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-06-19 10:26:20 +02:00
8240e5022f router: cli: print fatal errors including causes
as a first step of improving our error handling story, printing context
and causes if the error contains them.

The downside to adding context is that the default Display implementation
will *just* print the context, which hides the root cause. This is why
we print the errors using the pretty-print formatter in this change.

Originally-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
[WB: prefix commit message with crate]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-19 10:15:51 +02:00
5295da1b8a sys: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-17 14:10:56 +02:00
245d1ec2c1 sys: bmp to 0.5.6-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-17 14:09:19 +02:00
b25edb67de sys: use anyhow Error type for create_dir, and improve error messages
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-13 11:51:44 +02:00
86898b9a59 Revert "sys: cleanup, remove unnecessary crate prefix"
This reverts commit 26922d1796, because
it is necessary.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-13 11:51:44 +02:00
e5c8d70324 auth-api: add PasswordAuthenticator
This is the PbsAuthenticator with the hardcoded shadow.json/lock
configurable.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-13 11:14:36 +02:00
c12bbf6241 product-config: add open_secret_lockfile
We need this for things like shadow.json.lock.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-13 11:14:36 +02:00
26922d1796 sys: cleanup, remove unnecessary crate prefix
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-13 10:20:18 +02:00
0b17987c67 acme-api: show all certificate subject_alt_names (DNS, IP, EMAIL, URI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-11 14:24:23 +02:00
2c2475da5e simple-config: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-07 11:06:08 +02:00
7e4121d26e acme-api: add function to extract certificate data from .pem data
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-06 14:07:43 +02:00
fcaa4f6758 acme-api: implement funtion to create self signed certificates
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-06 14:07:43 +02:00
f7a22604ae apt: update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-06 11:40:49 +02:00
818ddf1283 bump proxmox-apt to 0.10.10-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-06 11:40:49 +02:00
4126a83e05 apt: fix test output dir
under autopkgtest, the rundir is not writable, but cargo gives us a tmpdir that
we can use in all cases.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-06 11:40:49 +02:00
6f532dfb7d various clippy fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-06 11:40:49 +02:00
7a8e948ee7 acme-api: pass parameters by reference
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-06 11:09:56 +02:00
2c2497e5be fix #5513: apt: do not assume that sources.list file exists
Some users might want to switch to using only the newer .sources files
already, which Debian is going to switch to in the long run.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2024-06-06 11:06:41 +02:00
04505ada7a acme-api: implement revoke certificate helper
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-06 10:23:35 +02:00
53ff71772f simple-config: new crate to read/write proxmox simple text config files
Copied from proxmox-backup/src/tools/config.rs

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-06 10:16:28 +02:00
a17430b38f run cargo fmt
(again)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-06-05 13:31:06 +02:00
7ab17e262c acme-api: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 12:12:52 +02:00
3787764db7 time-api: bump version to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 11:28:59 +02:00
c3d9d21308 syslog-api: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 11:28:10 +02:00
af75a203da network-api: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 11:27:30 +02:00
3eea0fd8ce dns-api: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 11:27:16 +02:00
1a0b39710c product-config: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 11:27:16 +02:00
053bb3d3d3 config-digest: bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-06-05 11:24:38 +02:00
10f32d4312 acme-api: use replace_secret_config to write acme config files
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-04 13:14:20 +02:00
ccbef4be87 acme-api: use create_secret_dir from product-config
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-04 12:56:57 +02:00
cb971b402f product-config: new create_secret_dir function
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-04 12:53:41 +02:00
6c30be2280 product-config: code cleanup
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-04 12:53:18 +02:00
8219565d6a acme-api: create all directorties inside init 2024-06-04 12:22:16 +02:00
2270f7bf94 product_config: introduce priviledged user.
Normally root, but can be the same as the api_user if the product
does not use priviledge separation.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-04 11:14:07 +02:00
0033f67e37 product-config: export get_api_user
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-04 11:02:23 +02:00
3aa07c117b acme-api: export ChallengeSchemaWrapper
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 12:55:43 +02:00
cae2b556fa acme-api: export account_config_filename
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 12:29:16 +02:00
c5731f916b acme-api: make register_account directory parameter optional
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 10:50:12 +02:00
95ea61183f acme-api: export known directories
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 10:40:12 +02:00
0582a13281 acme-api: export api types at top-level
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 10:03:06 +02:00
5250493e05 syslog-api: add debian control file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:32:11 +02:00
a334886f14 network-api: add debian control file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:29:57 +02:00
6c1798fb31 time-api: add copyright file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:27:38 +02:00
bd5c1ade4b dns-api: add debian control file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:24:26 +02:00
ba49720837 dns-api: avoid auto-generated cargo features
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:21:42 +02:00
3de2812254 product-config: add debian control file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:14:14 +02:00
2665b566c0 acme-api: remove stale feature gate and always compile api types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-06-03 09:08:16 +02:00
79a6f97c39 acme-api: commit missing file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-31 12:35:42 +02:00
5e00ee7bb0 acme-api: remove useless api-types feature
We always need those types, so there is no need to make this a feature.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-31 12:23:05 +02:00
d152e47d78 acme-api: add init method to setup directories
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-31 12:12:23 +02:00
e913330e09 product-config: simplify by removing the configuration directory
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-31 11:29:59 +02:00
40f812f324 remove system-management-api
Which is now split into separate crates:

- time-api
- network-api
- dns-api
- syslog-api
2024-05-30 09:53:59 +02:00
f6bcb6b50b syslog-api: new crate, split out from system-management-api 2024-05-30 09:44:48 +02:00
6bb74338b4 network-api: new crate, split out from system-management-api
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-30 09:27:49 +02:00
83b6f673b3 time-api: new crate, split out from system-managent-api
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-30 09:06:19 +02:00
b7f0cc7c1e dns-api: new crate, split out from system-management-api
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-30 08:44:50 +02:00
4768ad2200 product-config: remove digest implementation (move to proxmox-config-digest crate)
And use the new proxmox-config-digest crate instead.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 18:41:28 +02:00
34b21106dd config-digest: split out config digest api type into separate crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 17:59:03 +02:00
3497e9edc7 sys: use 0750 as default directory permissions
Should not make a difference because default umask is 022 ...

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 11:04:41 +02:00
06d25870ed product-config: remove functions to check permissions, which are now in proxmox-sys.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 10:22:05 +02:00
57723e98fd sys: add helpers to check file and directory permissions
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 10:14:24 +02:00
37c9dbf1eb sys: create options: make file parameter generic
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 09:32:48 +02:00
237f6218b0 product-config: factor out methods to create different file creation options
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 07:47:17 +02:00
7cd240bbad product-config: use Path instead of str everywhere
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-29 06:52:04 +02:00
484f12f3aa lang: drop commented-out c_str! implementation variants
This was an old version of a const-fn compatible checked c_str
implementation which was never enabled.

When we get rust 1.72, `CStr::from_bytes_with_nul` becomes usable in
const contexts.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-05-24 12:58:06 +02:00
1d68cc33a3 metrics: influxdb test uri creation
Extract the URI creation for write and health URIs. Add unit test to
test the encoding of special characters in the organization and bucket
parameters.

Follow-up-to: bfa73aad ("metrics: encode influxdb org and bucket parameters")

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>

FG: downgraded form_urlencoded version to packaged one
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-05-24 10:10:38 +02:00
aae8a03dc4 auth-api: bump version to 0.4.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-05-22 16:04:04 +02:00
bf9dc73246 sys: bump version to 0.5.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-05-22 15:48:34 +02:00
9c95b4d66e auth-api: rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-05-22 15:41:56 +02:00
fc75d98cb3 auth-api: cleanup a warning
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-05-22 15:40:12 +02:00
a6dc4d322d auth-api: remove unnecessary allocation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-05-22 15:38:26 +02:00
5b4cb9b124 auth-api: fix types compilefail test
due to missing `use` statements they failed, as they should, but for
the wrong reasons. also adapt a test case that presumably was meant
to test whether `TokennameRef` can be compared, but instead
duplicated the `UsernameRef` test case.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:44 +02:00
eef12f91a1 sys: crypt: use constant time comparison for password verification
by using `openssl::memcmp::eq()` we can avoid potential timing side
channels as its runtime only depends on the length of the arrays, not
the contents. this requires the two arrays to have the same length, but
that should be a given since the hashes should always have the same
length.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:43 +02:00
f82bb2fc2b sys: crypt: move to yescrypt for password hashing
previously we used `sha256scrypt` for password hashing. while this may
by safe if used with the correct parameters, we used the default
parameters which are considered unsafe. according to `man crypt(5)`:

> The default CPU time cost parameter is 5000, which is too low for
> modern hardware.

hence, we needed to adapt this code anyway. conveniently, verification
with crypt also works for older hashes as the parameters for the
hashing function are encoded in the output of crypt. so this is a drop
in replacement that will simply use yescrypt for new hashes while
old hashes will still verify properly.

this commit also adds a wrapper for `crypt_gensalt_rn` to more easily
generate correctly formatted salt strings. this is also useful for
switching the cpu time hardness parameter, as otherwise we'd need to
encode that ourselves.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:41 +02:00
4d6922e2c4 auth-api: move to hmac signing for csrf tokens
previously we used our own hmac-like implementation for csrf token
signing that simply appended the key to the message (csrf token).
however, this is possibly insecure as an attacker that finds a
collision in the hash function can easily forge a signature. after all,
two messages would then produce the same start conditions before
hashing the key. while this is probably a theoretic attack on our csrf
implementation, it does not hurt to move to the safer standard hmac
implementation that avoids such pitfalls.

this commit re-uses the hmac key wrapper used for the keyring. it also
keeps the old construction around so we can use it for a transition
period between old and new csrf token implementations.

this is a breaking change as it changes the signature of the
`csrf_secret` method of the `AuthContext` trait to return an hmac
key.

also exposes `assemble_csrf_prevention_toke` so we can re-use this
code here instead of duplicating it in e.g. proxmox-backup's
auth_helpers.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:40 +02:00
8609fb58ef auth-api: use constant time comparison for csrf tokens
by using openssl's `memcmp::eq()` we can avoid potential side-channel
attack on the csrf token comparison. this comparison's runtime only
depends on the length of the two byte vectors, not their contents.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:38 +02:00
b926ea1f5c auth-api: add ability to use hmac singing in keyring
previously we only used asymmetric cryptographic schemes to
authenticate tickets. this is fairly costly and not necessary in every
instance. imagine a service that runs as a single daemon. this daemon
is then the only party that needs to sign and verify tickets. this
makes hmac perfectly suitable for such usecases. hmac has some
advantages over asymmetric schemes:

- much simpler and well reviewed construction
- much faster and better optimized crypto primitives (hash functions)

this commit first introduces a new hmac key wrapper that uses openssl's
hmac implementation and can easily be reused by other parts of the
code. it also refactors the keyring code to make it easier to rotate
new hmac keys into place so switching to hmac keys is easier.

hmac keys are symmetric, so the verification key is the same key as the
signing key. this breaks the previous assumption by the keyring that
these correspond to public and private keys. thus, this commit
introduces two wrapper enums to distinguish between hmac and asymmetric
signature schemes.

the verification of hmac keys is also done via `openssl::memcmp::eq()`
to avoid potential timing side-channel attacks.

below are some simple benchmarks done with criterion.rs to show how much
faster hmac is, no matter the actual hash function:

rsa 4096 + sha256        time:   [2.7825 ms 2.7907 ms 2.7995 ms]
ed25519                  time:   [94.411 µs 94.840 µs 95.324 µs]
hmac sha256              time:   [5.7202 µs 5.7412 µs 5.7645 µs]
hmac sha384              time:   [6.6577 µs 6.6780 µs 6.7006 µs]
hmac sha3_256            time:   [5.6930 µs 5.7114 µs 5.7322 µs]

rsa with 4096 bit keys and a sha256 digest is our current default. the
test itself consists of a single sign + verification cycle. criterion
repeats this test as it sees fit to arrive at the above numbers.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:36 +02:00
09d31a1a8b auth-api: move to Ed25519 signatures
previously we used P-256 as the curve of our choice for ec signatures.
however, in the meantime Ed25519 has become a lot more wide-spread.
this simplifies our ec generation code significantly while keeping the
same security level. Ed25519 was also specifically designed and
reviewed to avoid implementation errors likely making it a more secure
choice

note that Ed25519 as a signature scheme always uses sha512, so signing
or verifying with a chosen digest is not supported.

as this mostly affects newly generated keys, this should not break any
existing setups.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:35 +02:00
8e566591d5 auth-api: move signing into the private key
this commit moves the current ticket signing code into the private key
implementation. the upside is that the caller does not need to deal
with openssl's `Signer` directly. it also simplifies and unifies the
code by using the same helper for verifying a signature and creating it.

also derive `Clone` on `PrivateKey` and `PublicKey`. as they are
essentially thin wrappers around `openssl::pkey::PKey<Private>` and
`openssl::pkey::PKey<Public>`, which can be cloned, deriving `Clone`
just makes them easier to use.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2024-05-22 10:26:33 +02:00
f240a2bfaa acme-api: add debian packaging
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-17 12:13:02 +02:00
7c899090e4 acme-api: use product-config instead of custom acme api configuration
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-17 11:52:57 +02:00
0ffe40fcfa bump proxmox-section-config to 2.0.2-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-17 11:04:10 +02:00
a2693c7046 section-config: pass filesystem paths as AsRef<Path>
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-17 10:59:20 +02:00
cfc155a06b acme-api: reusable ACME api implementation.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 12:35:14 +02:00
870948f1d7 bump proxmox-acme to 0.5.2
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 11:53:52 +02:00
c07c46cd82 acme: add async-client feature
The client code is copied from propxmox-backup, without the load/safe
account functionality.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 11:48:17 +02:00
c2450691c6 acme: allow to compile/use api types separately.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 11:23:19 +02:00
b082d7dafa system-management-api: network: add create_interface and update_interface
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 10:24:41 +02:00
2c0c7ca478 system-management: use ip/cidr schema types from proxmox-schema
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 09:42:10 +02:00
49b97b6a5f bump proxmox-schema to 3.1.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 09:35:50 +02:00
31b7b070b5 schema: api-types: add ip/cidr api schemas
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-16 09:09:37 +02:00
b74583dffe system-management-api: rename features (add suffix -api-types)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-15 13:33:55 +02:00
3c19dd757a rename proxmox-system-config-api to proxmox-system-management-api
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-15 13:23:42 +02:00
15e3779331 system-config-api: add syslog feature
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-15 12:31:50 +02:00
87aaa4e30a Revert "system-config-api: network: add alias 'iface' for interface name."
This reverts commit a4de726601.

Turn out we do not need this.
2024-05-14 11:50:44 +02:00
e226ddcc90 tape: include drive activity in status
Since we don't query each drives status seperately, but rely on a single
call to the drives listing parameter for that, we now add the option
to query the activity there too. This makes that data avaiable for us
to show in a seperate (by default hidden) column.

Also we show the activity in the 'State' column when the drive is idle
from our perspective. This is useful when e.g. an LTO-9 tape is loaded
the first time and is calibrating, since that happens automatically.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-05-14 10:31:33 +02:00
2bf32cb820 tape: add drive activity to drive status api
and show it in the gui for single drives. Adds the known values for the
activity to the UI.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-05-14 10:25:42 +02:00
175a9b3cd5 tape: add functions to parse drive device activity
we use the VHF part from the DT Device Activity page for that.
This is intended to query the drive for it's current state and activity.

Currently only the activity is parsed and used.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-05-14 10:11:06 +02:00
8002011f7c tape: save 'bytes used' in tape inventory
and show them on the ui. This can help uses with seeing how much a tape
is used.

The value is updated on 'commit' and when the tape is changed during a
backup.

For drives not supporting the volume statistics, this is simply skipped.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2024-05-14 10:07:57 +02:00
26c7a591eb system-config-api: expose helpers to set ports/slaves as string (list)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-13 12:25:07 +02:00
805b1d366b system-config-api: network: add api type to update network interfaces
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-13 11:06:53 +02:00
c101194f5a system-config-api: cleanup: remove useless serde rename property 2024-05-10 11:15:12 +02:00
a4de726601 system-config-api: network: add alias 'iface' for interface name.
So that we can use the Interface struct with create and update api calls (which
currently use 'iface' instead of 'name').

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-10 10:23:27 +02:00
e210c85d8e system-config-api: network: add helpers to check for duplicate gateway propertie
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-10 10:08:23 +02:00
943cfd5417 system-config-api: network: add helpers to set bridge ports and bond slaves
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-10 09:53:27 +02:00
729817efd3 system-config-api: add network feature
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-10 09:53:05 +02:00
3e8b0ee567 system-config-api: use cargo features to sparate functionality
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-08 11:13:20 +02:00
770c5dbd03 system-config-api: add functions to read/write time and timezone
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-07 14:18:25 +02:00
751b578d6e rename proxmox-dns-api to proxmox-system-config-api
Because we want to bundle system configuration APIs in one crate,
i.e. Time, DNS, Network. We may separate them in future using
cargo features.
2024-05-07 13:32:26 +02:00
78bd8eea24 dns-api: add feature "impl"
So the we can use the api types with our UI crates.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-06 13:52:57 +02:00
96a3656dd2 product-config: add feature "impl"
So the we can use the ConfigDigest with our UI crates.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-06 13:40:32 +02:00
53ee3f92ea product-config: add method to detect config digest modifications.
Using an object method with strong typing is considered cleaner.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-06 13:01:36 +02:00
cb4e3776f2 dns-api: do not serlialize option None
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-06 10:29:49 +02:00
5d3c3a8770 dns-api: export all defined api types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-03 09:51:34 +02:00
58bb112375 dns-api: add debian packaging
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-03 09:30:40 +02:00
03b4aed510 product-config: add debian packaging
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-03 09:29:46 +02:00
64e8a72a0b fix typo in proxmox-product-config workspace dependency
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-03 09:28:33 +02:00
c2f85d418a dns-api: new crate which implements the DNS api
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-02 13:04:43 +02:00
e64575b6a7 product-config: add rust API type for configuration digest
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-02 12:28:11 +02:00
9fc48d96d2 new crate for commonly used functions to read and write configuration files
Factor out functions to read and write configuration files with
product specific permissions.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-05-02 11:17:27 +02:00
16ac3ef458 api-types: remove influxdb bucket name restrictions
Remove the regex for influxdb organizations and buckets. Influxdb does
not place any constraints on these names and allows all characters. This
allows influxdb organization names with slashes.

Also remove a duplicate comment and add some missing ones.

This also aligns the behavior to PVE as there are no restrictions there
either.

The motivation for this patch is this forum post:
https://forum.proxmox.com/threads/influx-db-organization-doesnt-allow-slash.145402/

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-04-26 17:54:51 +02:00
afb48baca5 metrics: bump version to 0.3.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-26 17:36:19 +02:00
d426b6fe03 metrics: influxdb: inline variables into template format string
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-26 17:31:22 +02:00
bfa73aadf8 metrics: encode influxdb org and bucket parameters
In order to remove the current limitations on the bucket and
organization names, we need to make sure that they are transmitted
correctly. In order to do this, we encode them using the url crate.

This way we support organization/bucket names that include slashes,
whitespaces, etc.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-04-26 17:25:52 +02:00
5987eb0c3f sys: bump version to 0.5.4-2
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-26 17:24:23 +02:00
c68b69e949 sys: bump version to 0.5.4-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-26 16:52:46 +02:00
f2633b462f config: write vlan network interface
* Add vlan_id and vlan_raw_device fields to the Interface api type
* Write to the network config the vlan specific properties for vlan
  interface type
* Add several tests to verify the functionally

Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-24 21:48:50 +02:00
863d760340 auth-api: bump version to 0.3.5
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-24 21:22:33 +02:00
bd944b06f9 ldap: bump version to 0.2.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-24 21:13:45 +02:00
7db5cd8c48 realm sync: add sync job for AD realms
Basically just a thin wrapper over the existing LDAP-based realm sync
job, which retrieves the appropriate config and sets the correct user
attributes.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2024-04-24 21:06:14 +02:00
8bdf9ac45c api: access: add routes for managing AD realms
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2024-04-24 21:06:14 +02:00
f4a9afd17c notify: fix TemplateType::Subject doc comment
wrongly copied

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-04-24 08:05:16 +02:00
1372617876 api-types: api: tape: add notification-mode parameter
Same as with datastores, this option determines whether we send
notifications the old way (send email via sendmail to a user's email
address) or the new way (emit matchable notification events to the
notification stack).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-23 23:14:46 +02:00
90603f6e25 api-types: api: datatore: add notification-mode parameter
This one lets the user choose between the old notification behavior
(selecting an email address/user and always/error/never behavior per
datastore) and the new one (emit notification events to the
notification system)

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-23 23:14:46 +02:00
0af0bad742 notify: bump version to 0.4.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-04-23 23:14:19 +02:00
8f408ea4af notify: add getter for notification timestamp
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-04-23 23:06:56 +02:00
08b7c501ac notify: endpoints: matcher: improve descriptions for API types
proxmox-schema will automatically append text (e.g. 'Can be specified
more than once'), so we should end every comment with a '.'.

Also copy over some text from PVE docs, since these doc comments will
now be visible in the PBS documentation.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-04-23 23:06:56 +02:00
b2000d1f75 notify: pbs-context: exclude successful prunes in default matcher
PBS sends notifications for all events but successful prune jobs.
There we only care about errors.

This commit adapts the 'default-matcher' to reflect that behavior
as well.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-04-23 23:06:56 +02:00
7035d57312 notify: use std::sync::OnceCell instead of lazy_static!
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-04-23 23:06:56 +02:00
d0b1502803 notify: expose config module
This is needed because we want to access CONFIG and PRIVATE_CONFIG
from the docgen helper in PBS.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-04-23 23:06:56 +02:00
c028a32c1e notify: renderer: add relative-percentage helper from PBS
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
803bf7cdc7 notify: pbs context: include nodename in default sendmail author
The old notification stack in proxmox-backup includes the nodename, so
we include it here as well.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
c55f37b8c4 notify: derive Deserialize/Serialize for Notification struct
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
1a40d34083 notify: derive api for Deleteable*Property
The API endpoints in Proxmox Backup Server require ApiType to be
implemented for any deserialized parameter.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
87f7dfa111 notify: api: add get_targets
This method allows us to get a list of all notification targets.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
e83269be1d notify: give each notification a unique ID
We need this for queuing notifications on PBS from the unprivileged
proxy process.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
efb576385b notify: cargo.toml: add spaces before curly braces
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
2f40b79f8f notify: make the mail-forwarder feature depend on proxmox-sys
It uses proxmox_sys::nodename - the dep is needed, otherwise the code
does not compile in some feature flag permutations.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
50d80328e5 notify: don't make tests require pve-context
Tests now have their own context, so requiring pve-context is not
necessary any more.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
d61e3fc759 notify: convert Option<Vec<T>> -> Vec<T> in config structs
Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
a4d5594721 notify: make api methods take config struct ownership
This saves us from some of the awkward cloning steps when updating.

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
1516cc26d2 notify: switch to file-based templating system
Instead of passing the template strings for subject and body when
constructing a notification, we pass only the name of a template.
When rendering the template, the name of the template is used to find
corresponding template files. For PVE, they are located at
/usr/share/proxmox-ve/templates/default. The `default` part is
the 'template namespace', which is a preparation for user-customizable
and/or translatable notifications.

Previously, the same template string was used to render HTML and
plaintext notifications. This was achieved by providing some template
helpers that 'abstract away' HTML/plaintext formatting. However,
in hindsight this turned out to be pretty finicky. Since the
current changes lay the foundations for user-customizable notification
templates, I ripped these abstractions out. Now there are simply two
templates, one for plaintext, one for HTML.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
2024-04-23 23:06:52 +02:00
730f4e58ff GC: flatten existing status into job status
to avoid drifting definitions and reduce duplication. with the next major
release, the 'upid' field could then be renamed and aliased to be in line with
the other jobs, which all use 'last-run-upid'. doing it now would break
existing callers of the GC status endpoint (or consumers of the on-disk status
file).

the main difference is that the GC status fields are now not optional (except
for the UPID) in the job status, since flattening an optional value is not
possible. this only affects datastores that were never GCed at all, and only
direct API consumers, since the UI handles those fields correctly.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-04-22 13:58:08 +02:00
163732177d api: garbage collect job status
Adds an api endpoint on the datastore that reports the gc job status
such as:
 - Schedule
 - State (of last run)
 - Duration (of last run)
 - Last Run
 - Next Run (if scheduled)
 - Pending Chunks (of last run)
 - Pending Bytes (of last run)
 - Removed Chunks (of last run)
 - Removed Bytes (of last run)

Adds a dedicated endpoint admin/gc that reports gc job status for all
datastores including the onces without a gc-schedule.

Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Originally-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewd-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-04-22 13:58:08 +02:00
bec18b8e60 api: assert that maintenance mode transitions are valid
Maintenance mode Delete locks the datastore. It must not be possible to go
back to normal modes, because the datastore may be in undefined state.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-04-22 12:19:22 +02:00
15c013f758 maintenance: derive Copy for maintenance type and make maintenance mode fields public
Because it is a public api type.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-04-22 12:19:22 +02:00
73bf2b1994 pbs-api-types: use SchemaDeserializer for maintenance mode
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2024-04-22 12:19:22 +02:00
42fb9ed26b fix #5249: apt: allow parsing Packages without Priority field
it seems there are repositories out there that don't (always) include
it, and while it is required for the .deb packages themselves in Debian,
the repository "spec" doesn't make it mandatory.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-04-19 08:47:34 +02:00
f03f16d643 fix #5249: apt: allow parsing Packages without Priority field
it seems there are repositories out there that don't (always) include
it, and while it is required for the .deb packages themselves in Debian,
the repository "spec" doesn't make it mandatory.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-04-19 08:43:54 +02:00
6b393ac0ce notify: fix #5274: also set 'X-Gotify-Key' header for authentication
Versions of Gotify < 2.2.0 only supported the 'X-Gotify-Key' header
for passing the API token. This comment sets this header in addition
to the regular 'Authorization' header in order to be compatible with
older Gotify servers.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-04-04 16:51:55 +02:00
6858672642 bump proxmox-http to 0.9.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-28 13:18:21 +01:00
9be9d4b6ab http: support ALPN negotiated http2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-28 11:19:56 +01:00
d73eb3dcf1 tree-wide: run cargo fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-03-25 17:04:17 +01:00
0475421498 auth-api: implement Display for Realm{, Ref}
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
2024-03-25 17:03:27 +01:00
72afba8b5b ldap: add method for retrieving root DSE attributes
The root DSE holds common attributes about the LDAP server itself.
Needed to e.g. support Active Directory-based LDAP servers to retrieve
the base DN from the server itself, based on an valid bind.

See also RFC 4512, Section 5.1 [0] for more information about this
special object.

[0] https://www.rfc-editor.org/rfc/rfc4512#section-5.1

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
2024-03-25 17:03:27 +01:00
84fbfb22ec ldap: avoid superfluous allocation when calling .search()
The `attrs` parameter of `Ldap::search()` is an `impl AsRef<[impl
AsRef<str>]>` anyway, so replace `vec![..]` with `&[..]`.

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
2024-03-25 17:03:27 +01:00
78bf05a458 fix: use fragmented block size for space calculation
We currently calculate the size of a datastore using `statfs64`, which
returns the number of blocks in the fs and the two block sizes:
fragemented block size(f_frsize) and block size (f_bsize). To calculate
eg the total space in a datastore we use total_blocks * f_bsize, which
is not always correct.

`f_frsize` is the minimum unit of allocation on the filesystem (in
bytes) and in 99% of the cases equal to `f_bsize`, but in some cases
it differs. For example some filesystems allow smaller blocks for small
files, in case f_frsize < f_bsize. In that case, f_frsize * total_blocks
returns (mostly) the correct result (ceph also did some weird stuff, which is
now being fixed though [0][1]). `statvfs` also documents this as the
recommended way ('fsblkcnt_t f_blocks;   /* Size of fs in f_frsize units */')[2].

This patch aligns the the behavior with the libc utilities (also used by
`df`) [3].

Motivation: [4] (Forum post)

[0]: https://tracker.ceph.com/issues/3793
[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=92a49fb0f79f3300e6e50ddf56238e70678e4202
[2]: https://www.man7.org/linux/man-pages/man3/statvfs.3.html
[3]: https://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/fsusage.c#n147
[4]: https://forum.proxmox.com/threads/pbs-3-1-2-wrong-datastore-information-sshfs.139875/#post-626959

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-03-25 16:59:17 +01:00
71ff7c3344 datastore: remove datastore from internal cache based on maintenance mode
We keep a DataStore cache, so ChunkStore's and lock files are kept by
the proxy process and don't have to be reopened every time. However,
for specific maintenance modes, e.g. 'offline', our process should not
keep file in that datastore open. This clears the cache entry of a
datastore if it is in a specific maintanance mode and the last task
finished, which also drops any files still open by the process.

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
2024-03-25 16:12:41 +01:00
158f98fe72 cargo fmt (import reordering)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-20 11:13:13 +01:00
b8f2582bd9 pbs-api-types: use const_format and new api-types from proxmox-schema
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-20 11:09:26 +01:00
55f4d532c7 sys: d/copyright: update years
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-03-19 11:23:43 +01:00
e32081ea5f bump proxmox-notify to 0.3.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-19 11:11:02 +01:00
2c2783451f bump proxmox-auth-api to 0.3.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-19 11:09:49 +01:00
d653ac343b bump proxmox-schema to 3.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-19 11:08:28 +01:00
6f1d439f09 notify: adapt to proxmox_schema changes, use const_format
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-19 10:29:27 +01:00
686453a28c notify: sort and group dependencies
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-19 10:27:25 +01:00
1ac8b7f652 proxmox-schema: moved common api types from pbs-api-types
We want to use those types in all of our products.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-18 10:19:52 +01:00
d74fa06253 proxmox-schema: add IP address regex/api-types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-18 10:19:50 +01:00
a6f1b36fa6 proxmox-auth-api: use const_format to define static strings
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-18 10:19:48 +01:00
0c5e2640d0 proxmox-schema: use const_format to define static strings.
Macro rules are not hygienic, and current rust macro visibility rules
are a nightmare. Using const_format::concatcp!() is a much cleaner
solution.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-18 10:19:46 +01:00
c67a13f1d7 bump proxmox-acme to 0.5.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-07 13:27:47 +01:00
fbb3049768 acme: formatting fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-07 13:24:42 +01:00
b5255f1868 acme: drop api-types feature from schema dependency
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-03-07 13:24:37 +01:00
0370723261 proxmox-acme: derive PartialEq for API types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-07 13:21:54 +01:00
619414d4f1 proxmox-acme: add api-types feature
Because AccountData is exposed via our API (currently as type Object).

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2024-03-07 13:21:52 +01:00
279142df30 Fix warning
Variable does not need to be mutable
2024-03-02 02:59:35 +03:00
724c3dda6f rrd: fixup examples with the renamed types
Some types were recently renamed but the examples not updated
accordingly.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Fixes: 2f94283367 "rrd: spell out hard to understand abbreviations in public types"
2024-02-21 12:28:10 +01:00
05ff6b545a bump proxmox-schema to 3.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-21 12:28:02 +01:00
49b2bdf9c6 schema: drop periods after errors
lower case start + period = wrong

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-21 12:05:44 +01:00
9c40144214 schema: add regression tests for additional_properties in AllOf
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-21 12:02:00 +01:00
bae2cf75de schema: AllOf/OneOf: actually perform additional_properties() check
rather than just always allowing additional properties, only return
true if any of the available schemas allows it

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-21 11:43:20 +01:00
3f92e6286b router: Use safe wrapper for libc::isatty
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-02-16 16:56:02 +01:00
430df21720 sys: Use safe wrapper for libc::isatty
Use the `std::io::IsTerminal` trait introduced in Rust 1.70.

Internally it calls `libc::isatty`, see [1, 2]. Note that it switches
the comparison from `== 1` to `!= 0` which shouldn't make a difference
assuming that libc::isatty upholds the promises made in its man page.

The MSRV was set on the workspace to reflect this change.

[1] https://doc.rust-lang.org/src/std/io/stdio.rs.html#1079
[2] https://doc.rust-lang.org/src/std/sys/unix/io.rs.html#79

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2024-02-16 16:55:58 +01:00
8fabade99d fix #5190: api: OIDC: accept generic URIs for the ACR value
Allow more complex strings for the acr-value when using openid. The
openid documentation only specifies the acr-value *should* be an URI
[0].  Implemented a regex that loosely disallows some of the reserved
URI characters specified in the RFC [1].

Currently values like:
- "urn:mace:incommon:iap:silver"
- "urn:comsolve.nl:idp:contract:rba:location"
do NOT work, although they are correct URI's and common acr tokens.

For Proxmox VE we had to actually make this more strict to align with
each other, as there we accepted any string.

[0]: https://openid.net/specs/openid-connect-core-1_0.html
[1]: https://www.rfc-editor.org/rfc/rfc2396.txt

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2024-02-08 18:14:30 +01:00
7126249102 bump proxmox-subscription to 0.4.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:24:54 +01:00
304e1c544f bump proxmox-rrd to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:22:45 +01:00
364b21f3d2 bump proxmox-notify to 0.3.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:21:14 +01:00
77672b1253 bump proxmox-client to 0.3.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:06:08 +01:00
b5b563e215 bump proxmox-section-config to 2.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:03:51 +01:00
d9b783f1a8 bump proxmox-auth-api to 0.3.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:01:57 +01:00
9da7b3ad49 bump proxmox-tfa to 4.1.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 14:00:49 +01:00
890d9e58f7 bump proxmox-rest-server to 0.5.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 13:58:05 +01:00
f67ea142bc bump proxmox-router to 2.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 13:56:34 +01:00
d143efd01a bump proxmox-human-byte to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 13:56:01 +01:00
d7876b1837 bump proxmox-apt to 0.10.8-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 13:56:01 +01:00
ca6ca904ad bump proxmox-api-macro to 1.0.8-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 13:56:01 +01:00
245524d0d8 bump proxmox-schema to 3.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-02 13:53:12 +01:00
5df9da2af4 schema: implement split_list iterator
and reuse splitting code in no_schema's SeqAccess as well

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-02 12:06:46 +01:00
ae7454b05e router: OneOfSchema support
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-02 12:06:28 +01:00
d48a150835 schema: implement oneOf schema support
A 'oneOf' schema is basically exactly what a rust `enum` is.
Exactly one of the possible values must match the data.

This should ultimately be the base to allow using the
`#[api]` macro on a newtype style enum as well as using this
schema as a configuration for our section config parser.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-02 12:06:28 +01:00
1c0edfb518 router: cli: option to specify args explicitly
so CLI tools can pre-parse out non-api parameters before
passing the remaining stuff to the router

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-02 12:05:34 +01:00
2bba40f604 rest-server: support configuring the privileged connection
Adds a privileged_addr to ApiConfig, and some helpers for
hyper (both server and client)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 14:00:22 +01:00
aad01f7a90 rest-server: support unix sockets in create_daemon
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 13:59:48 +01:00
440c7e3361 bump proxmox-rrd to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-02-01 13:46:28 +01:00
2f94283367 rrd: spell out hard to understand abbreviations in public types
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 10:32:19 +01:00
f9e8ebfdc8 rrd: fix a few typos
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 10:32:19 +01:00
6eed8ed992 rrd: feature-gate support for the v1 format
new users of this crate might not really need support for the v1
format.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 10:32:19 +01:00
4d150d35c7 add debian packaging for proxmox-rrd
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 10:32:19 +01:00
5cbc8a4b66 add proxmox-rrd to workspace
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 10:30:57 +01:00
b8c56e7b6c Merge branch proxmox-rrd
The proxmox-backup repo was filtered using `git filter-repo` using the
following paths:

proxmox-rrd
proxmox-rrd-api-types
src/rrd

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-02-01 10:30:23 +01:00
2b56eba35c bump proxmox-login to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-24 12:49:06 +01:00
de8fd435fb Makefile: enforce the use packaged cargo
Nightly currently produces a different output format so this command
doesn't work right now when +system is not the default cargo.
Let's hope this is just a temporary hiccup in nightly, given that
there is an explicit `--format-version=1` parameter...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-24 09:26:03 +01:00
6ee541d5f2 login: parse helpers for floats
Of course PVE also stringifies those in the API, duh...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-24 09:20:49 +01:00
2fa645af2e schema: cli: simplify can_default check
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-24 09:20:42 +01:00
fe8b11eeec http: concat! user agent instead of format!
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-15 11:17:57 +01:00
3c453f7468 notify: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 14:15:48 +01:00
1484676ac4 bump proxmox-notify to 0.3.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 14:04:36 +01:00
ead4190e7b notify: matcher: support lists of values for 'exact' match-field mode
For example, one can now use:
  match-field exact:type=vzdump,replication
to match on vzdump AND replication events.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:38:32 +01:00
39c4d7d85e notify: include 'type' metadata field for forwarded mails
Seems like this was forgotten in the initial version. Without it,
it's not really possible to create matchers for forwarded mails.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:38:32 +01:00
870d9c2739 notify: include 'hostname' metadata field for forwarded mails
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:38:32 +01:00
f0bf95f53b notify: add separate context for unit-tests
... as using PVEContext for tests is brittle and annoying for some
tests.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:29:26 +01:00
50fa98e241 notify: api: allow resetting built-in targets if used by a matcher
In the 'delete'-handler targets, we check if a
target is still referenced by a matcher - if it is, we return an
error. For built-in targets, this is actually not necessary, since
'deleting' a built-in only resets it to its default settings - it will
continue to exist after that.
The user could easily trigger this if 'mail-to-root', which is
referenced by 'default-matcher' is modified and then reset to its
defaults: An error is shown, the built-in target is not reset.

This commit disables this check if it is a built-in target.

Renamed the helper 'ensure_unused' to 'ensure_safe_to_delete' in the
process.

Also fixed the tests in api::test - they were never executed due to a
faulty #[cfg] directive.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:29:26 +01:00
efa607f1b5 notify: smtp: add Auto-Submitted header to email body
`Auto-Submitted` is defined in the rfc 5436 [1] and describes how
an automatic response (f.e. ooo replies, etc.) should behave on the
emails. When using `Auto-Submitted: auto-generated` (or any value
other than `none`) automatic replies won't be triggered.

[1]: https://www.rfc-editor.org/rfc/rfc3834.html

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:20:41 +01:00
b03c394039 notify: smtp: forward original message instead nesting
For mails forwarded by `proxmox-mail-forward` to an SMTP target, the
original message was nested as a 'message/rfc822' message part.
Originally this approach was chosen to avoid having to rewrite
message headers.
Good email-clients, such as Thunderbird can display these inline.
Other, more limited clients will show these messages as an attached
.eml file, which is not really a good user experience.

This patch changes the approach for message forwarding to be more like
forwarding mails in a mail client. We create a new message and
add the original message body as a body. Additionally, we also copy
over all message headers that are relevant to correctly display the
original message body (e.g. Content-Type, Content-Transfer-Encoding)

Tested with a couple of different email messages (varying in
structure, body parts, encoding, etc.) against the following SMTP
relays:
  - gmail
  - outlook
  - our own webmail service

Originally reported in our community forum:
https://forum.proxmox.com/threads/proxmox-mail-forward-sends-mails-as-eml.137710/

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-10 12:20:41 +01:00
c2545b6540 move api-types tests to api-types and drop vec![] macro
we don't need to allocate here

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 10:13:45 +01:00
c5714ff06f api-types: doc improvements
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 10:13:45 +01:00
01618ea991 api-types: impl Display for FilterType
as the previous commit: simply keep the previous Display impl and call
it from out of the new GroupFilter impl

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 10:13:45 +01:00
e98fb9d5b1 api-types: factor out FilterType parsing
simply keep the previous FromStr implementation and call it the new
GroupFilter impl

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 10:13:45 +01:00
601098729a fixup import grouping
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-10 10:13:45 +01:00
c7b80d5e04 fix #4315: jobs: modify GroupFilter so include/exclude is tracked
After some discussion I canged the include/exclude behavior to first run
all include filter and after that all exclude filter (rather then
allowing to alternate inbetween). This is done by splitting them into 2
lists, running include first.

A lot of discussion happened how edge cases should be handled and we
came to following conclusion:

no include filter + no exclude filter => include all
some include filter + no exclude filter => filter as always
no include filter +  some exclude filter => include all then exclude

Since a GroupFilter now also features an behavior, the Struct has been
renamed To GroupType (since simply type is a keyword). The new
GroupFilter now has a behaviour as a flag 'is_exclude'.

I considered calling it 'is_include' but a reader later then might not
know what the opposite of 'include' is (do not include?  deactivate?). I
also considered making a new enum 'behaviour' but since there are only 2
values I considered it over engeneered.

Signed-off-by: Philipp Hufnagl <p.hufnagl@proxmox.com>
2024-01-10 10:13:45 +01:00
674ab33a43 bump proxmox-sys to 0.5.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-08 12:48:57 +01:00
8fdf696eed bump proxmox-time dependency to 1.1.6
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-08 12:18:12 +01:00
ea05268cde bump proxmox-time to 1.1.6
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2024-01-08 12:18:12 +01:00
dc72878d37 sys: email: use epoch_to_rfc2822 from proxmox_time
`strftime`'s formatting is locale-dependent. If the system locale was
set to e.g. de_DE.UTF-8, the `Date` header became invalid
(e.g Mo instead of Mon for 'Monday'), tripping up some mail clients
(e.g. KMail).

This commit should fix this by using the new `epoch_to_rfc2822`
function from proxmox_time. Under the hood, this function uses
`strftime_l` with a fixed locale (C).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-08 12:04:47 +01:00
1384bd9161 time: posix: add epoch_to_rfc2822
This is the format used in the 'Date' header in mails.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-08 12:04:11 +01:00
795b3a57a7 time: posix: add bindings for strftime_l
This variant of strftime can be provided with a locale_t, which
determines the locale used for time formatting.

A struct `Locale` was also introduced as a safe wrapper around
locale_t.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-08 11:59:26 +01:00
5b25e7cc90 time: posix: inline vars in string formatting
No functional changes.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-08 11:57:25 +01:00
7033c497a0 time: posix: use strftime from the libc crate.
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-08 11:57:15 +01:00
228ce9d69c client: do a POST instead of PUT in post_without_body
Probably a copy-paste mistake.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2024-01-04 08:15:01 +01:00
904bef0231 tape: move 'eject-before-unload' to a plain changer config option
instead of having it in a property string. For now this should be fine,
and if we need many more such options, we can still move them into a
property string if we want.

Also update the cli command in the docs on how to set it now.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-12-14 10:23:49 +01:00
2cfa31a218 tape: fix 'eject-before-unload' api type
by converting the bool into an option, otherwise having the options not
set at all will fail the unload while deserializing with
'eject-before-unload is not optional'

Also if we can automatically decide this in the future, we can now
detect if the option was explicitely set or not.

Fixes: 99f24b20 ("fix #4904: tape changer: add option to eject before unload")
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-12-13 12:14:33 +01:00
99f24b2079 fix #4904: tape changer: add option to eject before unload
some tape libraries need the tape being ejected from the drive before
doing an unload. Since we cannot easily detect if that's the case,
introduce an 'eject_before_unload' option.

Instead of just adding a bool flag to the config, add a new 'options'
property string where we can put such niche options similar to how we
handle the datastore tuning options.

Extend the LtoTapeHandle with 'medium_present' which just uses a
TEST UNIT READY command to check for present medium, so we don't
try to eject an already ejected tape.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-12-12 14:36:19 +01:00
a82bcf8ad1 tape: changer: save whole LtoTapeDrive config in MtxMediaChanger
we'll need more info from there in the future, so derive clone for it
and save the whole config instead of adding an additional field.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-12-12 14:36:08 +01:00
e9283e93e7 tape: derive PartialEq and PartialOrd for TapeDensity
so that we can compare more easily

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-12-12 13:24:58 +01:00
4abc2ec487 status: use Option on avail/used datastore attrs
Instead of returning -1 if we can't get the attributes, we use an
Option which will not be serialized on `None`.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2023-12-11 13:09:13 +01:00
1f377da07c bump api-macro to 1.0.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 16:02:42 +01:00
3c1103e7d5 bump proxmox-tfa to 4.1.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 16:01:13 +01:00
01e68eb40e bump proxmox-router to 2.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 16:00:10 +01:00
1d6174d36d bump proxmox-apt to 0.10.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 15:59:59 +01:00
dc53be1b9a api-types: add a missing serde(default)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 15:53:57 +01:00
5d9e33d1d9 tfa: fix deserialize-default in TfaUser
Note that this was currently not deserialized anywhere, so this was
not an issue, but the api-macro now treats this as an error.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 14:54:47 +01:00
2435ab29e2 api-macro: make skip_serializing_if without default an error
except for Option types, since this causes deserialization issues

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 14:39:47 +01:00
89b29415a4 api-macro: add VariantAttrib
separated out of FieldAttrib without the `flatten` attribute, since we
don't support this on enum variants

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 14:39:47 +01:00
fa9a50a0b7 api-macro: rename SerdeAttrib to FieldAttrib
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 14:39:47 +01:00
5b9bac09da router: fix warning
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-06 14:39:47 +01:00
bd3013690f Merge branch 'proxmox-acme-merge' 2023-12-04 11:52:39 +01:00
54784e591e bump proxmox-acme to 0.5.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-04 11:47:28 +01:00
e703725049 add proxmox-acme to workspace
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-04 11:45:00 +01:00
83df0d3903 Merge branch 'proxmox-acme' 2023-12-04 11:43:15 +01:00
b212febefc drop rustfmt.toml
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-04 11:42:54 +01:00
6773460d89 drop -rs suffix
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-04 11:42:42 +01:00
a1b59c8a23 move to proxmox-acme
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-04 11:41:59 +01:00
6e1e835739 expand helper function by eab credentials
Signed-off-by: Folke Gleumes <f.gleumes@proxmox.com>
2023-12-04 10:14:44 +01:00
d07e4fdb9a Option<Vec<>> -> Vec<>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-12-04 10:13:46 +01:00
b57e1fb347 proxmox-apt: fix digest api type in APTRepositoryFile
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-12-04 09:39:54 +01:00
53416e358f add meta fields returned by the directory
According to the rfc, the meta field contains additional fields that
weren't covered by the Meta struct. Of the additional fields, only
external_account_required will be used in the near future, but others
were added for completeness and the case that they might be used in the
future.

Signed-off-by: Folke Gleumes <f.gleumes@proxmox.com>
2023-12-04 09:37:42 +01:00
88f7e190ec add external account binding
Functionality was added as a additional setter function, which hopefully
prevents any breakages. Since a placeholder Option an the AccountData
was already present, but has never been used, replacing the field with
an Option of a fully defined type should also be minimally intrusive.

Signed-off-by: Folke Gleumes <f.gleumes@proxmox.com>
2023-12-04 09:37:41 +01:00
1859be3588 proxmox-apt: fix serde attributes for API types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-12-02 14:45:57 +01:00
109902fbf0 tree-wide: fix various typos
found with codespell

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-29 18:32:07 +01:00
ea95d57759 tree-wide: fix various typos
found with codespell

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-29 18:32:07 +01:00
325dbbc97f node: status: declutter kernel-version
Return a struct with all the components of the kernel version like it
has been done in pve. Also return the legacy `kversion` to keep
backwards compat.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-29 15:31:35 +01:00
5d77ea0cd2 node: status: added bootmode
Added field that shows the bootmode of the node. The bootmode is either
Legacy Bios, EFI, or EFI (Secure Boot). To detect the mode we use the
exact same method as in pve: We check if the `/sys/firmware/efi` folder
exists, then check if the `SecureBoot-xx...` file in the `efivars`
directory has the SecureBoot flag enabled.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-29 15:31:35 +01:00
50b79198f8 sys: bump to 0.5.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-29 15:26:21 +01:00
5517d6f839 sys: email: move Auto-Submitted header up
Move the Auto-Submitted header out of the multipart section.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2023-11-29 15:17:53 +01:00
8e5c164bf5 sys: cleanup assigned and immediately returned var
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-29 14:57:29 +01:00
12657f89b3 sys: add helper to get bootmode and secureboot status
Helper that return the current boot_mode and secureboot status.
Detection works the same as in pve, we use `/sys/firmware/efi` and
the `efivars/SecureBoot-xxx..` file.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2023-11-29 14:54:29 +01:00
25d26d83b1 api types: add regex, format & schema for partition names
The new regex is similar to BLOCKDEVICE_NAME_REGEX but also allows
numbers at the end of the device name (also allows partitions names).
For nvme partitions it also allows the letter p and a number.

Signed-off-by: Markus Frank <m.frank@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Max Carrara <m.carrara@proxmox.com>
2023-11-28 18:24:12 +01:00
a815fc4f56 bump rest-server to 0.5.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-28 11:34:54 +01:00
804b7e82ff proxmox-rest-server: do not use formatter for AuthErr
We want to get a 401 error at HTTP level.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-28 11:29:39 +01:00
6b59158aaf proxmox-rest-server: return status code with ExtJsFormatter
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-28 11:29:35 +01:00
d10394fc66 tree-wide: run cargo fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-27 15:37:35 +01:00
b01c0f572b fix-3211: manager: Document --notify argument
Signed-off-by: Maximiliano Sandoval R <m.sandoval@proxmox.com>
2023-11-27 15:33:51 +01:00
3932e5bedf bump proxmox-auth-api to 0.3.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-27 14:46:59 +01:00
4a8cadc7e0 bump proxmox-rest-server to 0.5.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-27 14:39:22 +01:00
7d29269986 rest-server: Add Redirector
The `Redirector` is a simple `Service` that redirects HTTP requests
to HTTPS and can be served by a `hyper::Server`.

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-27 14:28:18 +01:00
57b4c4624b rest-server: Refactor AcceptBuilder, provide support for optional TLS
The new public function `accept_tls_optional()` is added, which
accepts both plain TCP streams and TCP streams running TLS. Plain TCP
streams are sent along via a separate channel in order to clearly
distinguish between "secure" and "insecure" connections.

Furthermore, instead of `AcceptBuilder` itself holding a reference to
an `SslAcceptor`, its public functions now take the acceptor as an
argument. The public functions' names are changed to distinguish
between their functionality in a more explicit manner:

  * `accept()` --> `accept_tls()`
  *        NEW --> `accept_tls_optional()`

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-27 14:28:15 +01:00
8eff15b0b0 subscription: update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-11-27 14:08:18 +01:00
eadf2e06c3 bump proxmox-subscription to 0.4.2-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-11-27 13:59:40 +01:00
ee7a908ebc subscription: expose the next_due_date as an i64
internally `SubscriptionInfo` already uses the `parse_next_due` helper
to parse the next due date to an epoch. this exposes a function that
allows us to use the epoch outside of this crate too. for example, a
user of pom may have multiple subscription for the same system. in
that case we want to apply the one with the due date that is furthest
in the future.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-11-27 13:59:40 +01:00
81cdba6181 subscription: let ProductType derive Hash
for usage in HashMap keys

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-11-27 13:59:40 +01:00
82e742f8f5 type: move ProductType type to proxmox-subscription from pom
previously this type lived inside of pom. this made it harder to
access the product type from a `SubscriptionInfo` trait in other
products. move the type here so we can check product types more
consistently across products (e. g. in pom and pbs)

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-11-27 13:31:59 +01:00
bf9b3e7ac0 api: make Remote for SyncJob optional
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Tested-by: Gabriel Goller <g.goller@proxmox.com>
2023-11-25 17:07:42 +01:00
56575dfc62 pbs-api-types: derive Clone and PartialEq for BackupContent, SnapshotVerifyState, SnapshotListItem and GroupListItem
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-11-23 09:53:30 +01:00
4da2fee6fa bump tfa to 4.1.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-23 09:21:38 +01:00
74c3943a89 bump schema to 2.0.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-11-23 09:16:46 +01:00
578f994e6b proxmox-schema: derive PartialEq for UPID (for GUI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-11-20 11:10:22 +01:00
eb6df88120 notify: bump version to 0.3.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-17 13:36:55 +01:00
af660f1fee sys: bump version to 0.5.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-17 10:42:38 +01:00
ca76122b16 notify: update d/control
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-17 08:37:36 +01:00
a3fbe14f44 sys: purge pty module
it was only used in the terminal proxy and got moved there.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-11-17 08:35:18 +01:00
9bea76c6b9 notify: add built-in config and 'origin' parameter
This allows us to define a (modifiable) builtin-config, which is
at the moment hardcoded in PVEContext

The 'origin' parameter indicates whether a config entry was created by
a user, builtin or a modified builtin.

These changes require context to be set for tests, so we set
PVEContext by default if in a test context. There might be a nicer
solution for that, but for now this should work.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
306f4005a1 notify: add 'disable' parameter for matchers and targets.
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
20b290893a notify: add api for smtp endpoints
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
53627a1952 notify: add 'smtp' endpoint
This commit adds a new endpoint type, namely 'smtp'. This endpoint
uses the `lettre` crate to directly send emails to SMTP relays.

The `lettre` crate was chosen since it is by far the most popular SMTP
implementation for Rust that looks like it is well maintained.
Also, it includes async support (for when we want to extend
proxmox-notify to be async).

For this new endpoint type, a new section-config type was introduced
(smtp). It has the same fields as the type for `sendmail`, with the
addition of some new options (smtp server, authentication, tls mode,
etc.).

Some of the behavior that is shared between sendmail and smtp
endpoints has been moved to a new `endpoints::common::mail` module.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
c1a3505e51 notify: add PVE/PBS context
This commit moves PVEContext from `proxmox-perl-rs` into the
`proxmox-notify` crate, since we now also need to access it from
`promxox-mail-forward`. The context is now hidden behind a feature
flag `pve-context`, ensuring that we only compile it when needed.

This commit adds PBSContext, since we now require it for
`proxmox-mail-forward`. Some of the code for PBSContext comes
from `proxmox-mail-forward`.

This commit also changes the global context from being stored in a
`once_cell` to a regular `Mutex`, since we now need to set/reset
the context in `proxmox-mail-forward`.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
5f7ac875f6 notify: add mechanisms for email message forwarding
As preparation for the integration of `proxmox-mail-foward` into the
notification system, this commit makes a few changes that allow us to
forward raw email messages (as passed from postfix).

For mail-based notification targets, the email will be forwarded
as-is, including all headers. The only thing that changes is the
message envelope.
For other notification targets, the mail is parsed using the
`mail-parser` crate, which allows us to extract a subject and a body.
As a body we use the plain-text version of the mail. If an email is
HTML-only, the `mail-parser` crate will automatically attempt to
transform the HTML into readable plain text.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
d20d9ec1aa sys: email: add forward
This new function forwards an email to new recipients.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
80c9069389 notify: let a matcher always match if it has no matching directives
This should be a bit more intuitive to users than the current
behavior, which is 'always match' for mode==all and 'never match' for
mode==any. The current behavior originates in the neutral element of
the underlying logical operation (and, or).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
190d483b2d notify: matcher: introduce common trait for match directives
This allows us to make the match-checking code a bit shorter.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
bdbd55ccff notify: add calendar matcher
This allows matching by a notification's timestamp:

matcher: foo
  match-calendar mon..fri 8-12

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
b421a7ca24 notify: replace filters and groups with matcher-based system
This shifts notification routing into the matcher-system. Every
notification has associated metadata (key-value fields, severity -
to be extended) that can be match with match directives in
notification matchers. Right now, there are 2 matching directives,
match-field and match-severity. The first one allows one to do a
regex match/exact match on a metadata field, the other one allows one
to match one or more severites.
Every matcher also allows 'target' directives, these decide which
target(s) will be notified if a matcher matches a notification.

Since routing now happens in matchers, the API for sending is
simplified, since we do not need to specify a target any more.

The API routes for filters and groups have been removed completely.
The parser for the configuration file will still accept filter/group
entries, but will delete them once the config is saved again. This is
needed to allow a smooth transition from the old system to the new
system, since the old system was already available on pvetest.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
df4858e989 notify: factor out notification content into its own type
This will be useful later for system mail forwarding, where
the content of the mail should be forwarded unchanged.

This moves notification properties into this new type and calls them
'data'. They will exclusively used for template rendering.
`Notification` will receive a separate field for metadata, which
will be useful for notification filtering. This decouples
template rendering and filtering, which enables us to be very precise
about which metadata fields we allow to be used in filters.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
b2089c37c5 notify: introduce Error::Generic
... as leaf error-type for anything for which we do not necessarily
want a separate enum variant.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-17 08:31:36 +01:00
486415f517 PruneJobConfig: remove stale optional flag from the API macro.
The property is not optional - it is defined as "String".

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-11-16 17:47:38 +01:00
dca6c270a0 pbs-api-types: derive Clone and PartialEq for job config/status types (for GUI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-11-15 16:37:44 +01:00
3fca8ef10d apt: use apt changelog for changelog fetching
support for it got added to Proxmox repositories, so there is no need to use
custom logic and manual fetching for this anymore.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-11-14 08:43:47 +01:00
7cb2d72b97 DatastoreTuning: fix serde attributes
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-11-12 12:02:19 +01:00
ecca38b94b DatastoteNotify: fix serde attributes
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-11-12 11:25:09 +01:00
3ac6f2d9c0 http: rate limited stream: fix typo in rustdoc comment
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-11-08 09:56:26 +01:00
f844271990 apt: repositories: document status property for standard repository
Suggested-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2023-11-06 19:48:30 +01:00
69edfdf985 rest-server: parse upid: improve on comments and variables
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-10-26 10:41:15 +02:00
06fed255cb rest-server: extend documentation for reading status of a UPID
Including some possible pitfalls when using this.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-10-26 10:26:11 +02:00
ba59b84d46 rest-server: code complexity clean-up in task-log rotation
avoid using the negative in the if check and extract the value to be
passed upfront.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-10-26 10:13:45 +02:00
4197e94e25 rest-server: better document task-log archive rotation
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-10-26 10:13:45 +02:00
c8a0ba04ca sys: depreacate PTY module, moves to termproxy directly
As termproxy only used this module from the huge proxmox-sys crate,
and this module was also only used here, it makes sense to move it
over there (and dropping proxmox-sys halved build-time from 8.5 s to
4.2 s).

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-10-24 13:52:16 +02:00
c1c062bf56 schema: beautify parameter error output
If there is only one error, output in a single line, without any
excessive newlines at the end. If there are multiple errors, show them
in a bulleted list.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2023-10-23 17:40:34 +02:00
34e86078c2 proxmox-tfa: derive Copy, Clone and PartialEq on TfaType (for GUI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-23 11:44:45 +02:00
c1819c2549 DailyDuration: derive PartialEq (for GUI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-20 12:01:27 +02:00
1e64feeaad rest-server: factor out task-log directory and creation
We had two call sites deriving the directory "shard" where the task
log file is actually saved to, this can lead to ugly bugs and is
better done in a central single-source-of-truth way.

While at it factor out the creation of the log file (and it's shard
directory) to avoid crowding the WorkerTask new fn to much.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-10-18 19:36:37 +02:00
45152c5e3e bump proxmox-client to 0.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-16 13:27:13 +02:00
2c9c43ca4c human-byte: d/control bump
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-16 13:04:48 +02:00
f47ad56812 bump proxmox-human-byte to 0.1.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-16 13:03:12 +02:00
83af1cdce4 HumanByte: make fields public
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-16 12:27:54 +02:00
9e8f90e667 tfa: more optional dependency cleanup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-16 09:00:18 +02:00
6940908a8a tfa: make totp a feature and mark all optional deps as optional
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-16 08:58:09 +02:00
1a6f1efe63 workspace: set resolver to 2 to silence a warning
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-16 08:53:35 +02:00
a5f67f200d TrafficControlCurrentRate: derive Clone and PartalEq (for GUI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-15 11:09:42 +02:00
a2234de54a move TrafficControlCurrentRate to pbs-api-types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-15 11:07:58 +02:00
d6dab3cf24 TrafficControlRule: derive Clone and PartialEq (for GUI) 2023-10-15 09:25:26 +02:00
235cde7f03 Interface: add missing serde skip_serializing_if to bond_xmit_hash_policy
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-12 12:39:18 +02:00
9404f0ff9f Interface: fix deserialize (add default)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-11 11:38:01 +02:00
1fd995c54c Interface: derive Clone + PartialEq (for GUI)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-11 11:37:15 +02:00
3b42bca410 move MetricServerInfo definition to pbs-api-types
And derive Clone, Eq and Ord so that we can sort the list in the GUI.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-10-09 08:19:08 +02:00
7e6aa2733a replace deprecated X509Extension::new_nid
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-10-03 13:45:10 +02:00
04e2d0e5c3 bump proxmox-api-macro to 1.0.6-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-10-03 09:37:01 +02:00
c83627b1a6 bump proxmox-sortable-macro to 0.1.3-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-10-03 09:37:01 +02:00
4fd7359677 bump proxmox-router to 2.1.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-10-02 09:25:10 +02:00
b232b580a0 update to syn 2
This mostly affected attribute parsing (due to the syn::Meta changes).
Also creating `DelimSpan`s for custom-built `syn::Attribute`s is a
bit... ugly.
Upshot: turns out we can drop some helpers in util.rs with the new
`syn::Meta` changes.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-09-29 13:52:21 +02:00
eb1abe45b6 router: bump env_logger to 0.10 and move to workspace
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-09-28 09:59:48 +02:00
ee4d9a5567 pbs-api-types: move node status types from src/api2/types/mod.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-09-28 08:13:30 +02:00
9fa4185196 client: fix optional data for errors
previously we changed the internal type of the 'data' property
from Option<T> to T in the assumption the api always returns
'data:null'.

this is actually only the case when the api call succeeds. in an error
case there is no data property at all.

to fix this issue while behaving the same for 'data:null' we have to
revert to Option<T> for RawApiResponse but instead of always throwing an
error for 'data:null' in 'check' we now try there to deserialize from
Value::Null for T if there was no data. This will succeed for the Type
'()' which was the motivation for the original change.

The only downside is that the RawApiResponse now has a trait bound that
T is deserializeable, but was a requirement for using it anyway
(as there was no other way of constructing it)

Fixes: 271a55f ("client: remove option from inner RawApiResponse")
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-09-27 11:27:47 +02:00
dc9ee73751 schema: bump version to 2.0.1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-20 11:46:02 +02:00
bcdcb181ee fix #4162: sys: added auto-submitted header to email body
`Auto-Submitted` is defined in the rfc 5436 [1] and describes how
an automatic response (f.e. ooo replies, etc.) should behave on the
emails. When using `Auto-Submitted: auto-generated` (or any value
other than `none`) automatic replies won't be triggered.

[1]: https://www.rfc-editor.org/rfc/rfc3834.html

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
2023-09-18 16:41:06 +02:00
359da67e9b rest-server: accept empty body as valid parameters
technically an empty string is not valid json, but when sending an api
request without any parameters, treating the empty body as an empty
parameter hash instead of an error, makes the the api more robust for
clients

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-09-07 18:12:51 +02:00
92be86d776 schema: serialize enum unit variants
... since deserializing them already works

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2023-09-07 17:29:36 +02:00
498341ec0d apt: bump version to 0.10.6-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-05 15:29:49 +02:00
fb90d53caf apt: use modern format string variables and small style cleanups
note: not complete, there's other code to check and rework, but I had
this already done so commit it, better than nothing.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-05 15:19:38 +02:00
4e2cc6fd53 tests: factor out directory cleanup+creation
This moves the clean-up to happen up-front. That way one can still
inspect the test data after, e.g., a failed test.

Originally done almost like this in a patch from Fiona [0] that I just
overlooked, but now also factored out, avoid crowding the test code to
much with duplicate code.

[0]: https://lists.proxmox.com/pipermail/pve-devel/2023-June/057136.html

Suggested-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-05 15:15:46 +02:00
195e9e1cdf apt: fixup description for Reef repo
the "main" repo only exists for Quincy to allow an easier transition
from Proxmox VE 7 to Proxmox VE 8, for when the enterprise repo got
added for ceph too.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-05 10:09:00 +02:00
1e47036528 apt: add Ceph Reef to standard repo list
For now just duplicate the Ceph Quincy entries, as I want to avoid
using macros and we do not yet have support for enums inside enums
with the api macro.

Adapt and expand the tests slightly to have at least some simple
coverage there too.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-04 17:58:45 +02:00
767582a2eb apt: unify match-arm for ceph sources.list location
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-04 17:58:45 +02:00
ae3c04a76b apt: tests: allow re-running digest test without clean-up
Files inside CARGO_TARGET_TMPDIR are only cleaned on `cargo clean`, so
tests that expect files to not exist need to cleanup themselves.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-09-04 17:58:45 +02:00
c879704337 AclListItem: derive Clone and PartialEq
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-09-02 16:24:45 +02:00
bcd134a349 sys: fs: move tests to a sub-module
This ensures that test code is not compiled in regular builds

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-30 17:37:31 +02:00
51bae22b4d TaskListItem: derive Clone and PartialEq 2023-08-30 13:34:51 +02:00
299a478f15 proxmox-time: implement epoch_to_rfc3339 for wasm
we just printed out the UTC version, this implements a localized version

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-08-30 09:20:38 +02:00
151e2cfdfd time: make RFC3339 format in wasm conform to usual format
on other targets we print the timestamp without fractional seconds
('.xxxZ'), so we should remove that too on wasm

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-08-30 09:18:44 +02:00
271a55f187 client: remove option from inner RawApiResponse
when using the client for an api call that does not return any data
(it returns '{"data":null}'), we would always get an error 'api returned
no data'. The message is technically correct, but it should not be an
error when we expect no data (e.g. most of our CRUD PUT/POST calls)

instead of having the Option<T> in the RawApiResponse type itself, move
it into to the 'nodata' function intended for api calls where we don't
expect any data.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-08-29 14:14:52 +02:00
022fdacb25 proxmox-client: add post_without_body
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-08-27 16:19:31 +02:00
d49fc1aa2f notify: make template rendering helpers more robust
This commit has the aim of making template rendering a bit more
robust. It does so by a.) Accepting also strings for helpers that
expect a number, parsing the number if needed, and b.) Ignoring errors
if a template helper fails to render a value and showing an error in
the logs, instead of failing to render the whole template (leading
to no notification being sent).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-25 15:32:34 +02:00
c81068097b bump proxmox-client to 0.2.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-25 09:03:00 +02:00
8617442560 client: fixup checks for api calls not returning data
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-25 09:03:00 +02:00
f20f9bb9f7 client: set content type header on requests
this got lost with the recent refactoring

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-25 08:56:41 +02:00
6286ff4eeb bump proxmox-client to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-25 08:37:25 +02:00
d7f6fc4db5 client: fix content type parsing with included charset
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-25 08:36:19 +02:00
1f351625a5 async: runtime: Modernise module and update docs
This commit updates all helper functions, taking into account recent
developments regarding `tokio`.

In particular, the `block_in_place()` and `block_on()` functions now
don't panic anymore if used within the single-threaded `tokio` runtime
and instead behave as expected in both runtime flavours.

Furthermore, because `tokio` may add more runtime flavours in the
future, all helpers will now panic if used within an unsupported
runtime. This is to prevent unforeseen behavioural quirks and
interactions with `tokio` internals.

The above changes make `BlockingGuard` redundant; it is consequently
removed.

The documentation is also updated, describing the behaviour of the
helper functions and the purpose of the `runtime.rs` module in more
detail.

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
2023-08-23 09:51:52 +02:00
ede73a6561 client/login: clippy fixes
Signed-off-by: Max Carrara <m.carrara@proxmox.com>
2023-08-22 10:04:16 +02:00
68fe8baf95 bump proxmox-client to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-16 14:33:01 +02:00
97025d4143 package proxmox-login 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-16 14:30:38 +02:00
8e3e83318b fix: api-types: add support for lto 9 tape density
lto 9 tapes have a new density code which leads to these tapes not
being recognized properly. add the new density code and TapeDensity to
improve lto 9 support. since the documentation states that we support
lto 5 and above this constitutes a bug fix for lto 9 support.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-08-11 08:52:50 +02:00
a909d5789c client: convenience helper to get a serialized ticket
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-10 14:33:55 +02:00
490008d596 client: expose AuthenticationKind
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-10 14:27:31 +02:00
bea97ccce1 client: add Client::set_authentication method
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-10 14:14:06 +02:00
2fd5502321 bump proxmox-client to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-10 11:15:36 +02:00
604e467684 client: add TlsOptions::parse_fingerprint
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-10 10:44:04 +02:00
dff830ba04 client: impl HttpApiClient for refs, Arcs and Rcs
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 15:29:38 +02:00
a3322e49b9 client: put requests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 15:29:38 +02:00
ffe908f636 client: handle response data
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 13:21:02 +02:00
1c96afd0ec client: turn Client inside out
Since the WASM client cannot actually use a `http::Request` the way we
expect it to, that is, it cannot manually along cookies, we turn the
client bit inside out:

This crate mainly defines the `HttpApiClient` trait which expects the
http client to perform *authenticated* API calls, that is, the
handling of API tokens and tickets should happen at the *implementor*
side.

The product clients will require *this* trait to be implemented, and
will not themselves offer a way to login.

As for the `Client` struct, this will now instead *implement* this
trait and will *not* be used in the `wasm` ecosystem. Rather, this is
the ticket handling http client that already exists in the PWT based
ui code.

The PVE client in `pve-api-types` will not *contain* a `Client`
anymore, but rather, it will provide PVE api call implementations for
something implementing `HttpApiClient`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 13:21:02 +02:00
0f19f2125f client: drop environment and login methods
The environment trait was useful on the CLI, but does not really
translate well to eg. the wasm ui (or pdm for that matter), so drop it
and instead have `.login` and `.login_tfa` just take the
`proxmox_login` type and handle the updating of authentication data.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 13:21:02 +02:00
a9a267f04f client: replace Error trait with a type
Because we ultimately also want to drop the `Environment` trait since
it is not suitable for all use cases (eg. wasm ui)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 13:21:02 +02:00
e0b102d932 client: prepare to get rid of Error trait
First rename it so it's clear what "Error" refers to in the following
patches.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 13:21:02 +02:00
0c45d51406 login: add userid and api_url getters
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-09 13:21:02 +02:00
8841b389e3 proxmox-io: fix sparse_copy not copying sparsely on irregular read operations
In the uncommon circumstance that calls to `read()` end up reading any number of
bytes other than 4096, the subsequently read bytes become misaligned, causing
blocks of zeroes to be written unnecessarily.

To illustrate, imagine you have a 12KiB file:

  [x][x][x][x][ ][ ][ ][ ][x][x][x][x]
   └──4096──┘  └──4096──┘  └──4096──┘

The first and last block are filled with some data, whereas the middle block is
empty and will therefore result in only zeroes being read.

In order for the empty block to be skipped with `seek()`, the entire buffer has
to be filled with zeroes.

If, for example, the first `read()` ends up putting only 3KiB into the buffer,
the empty block in the middle won't be detected properly, as the buffer will
now always contain some data. What results are four misaligned reads:

  [x][x][x][x][ ][ ][ ][ ][x][x][x][x]
   ├─────┘  ├────────┘  ├────────┘  │
   1        2           3           4

This is fixed by ensuring chunks of 4KiB are always read into the buffer,
except when the last block is truncated. In order to prevent frequent small
reads, the incoming reader is also buffered via `io::BufReader`.

Signed-off-by: Max Carrara <m.carrara@proxmox.com>
2023-08-09 12:48:04 +02:00
e9499bbcf2 bump proxmox-ldap to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-08 14:09:19 +02:00
ade1d19b0a bump proxmox-apt to 0.10.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-08 14:05:51 +02:00
c74167f528 ldap: only search base of base_dn when checking connection
this should avoid most common size limitations. the search should also
complete quicker as fewer results need to be computed. note that this
way a configuration may be accepted, but the related sync job can
fail due to and exceeded size limit warning for some ldap servers
(such as 2.5.14+dfsg-0ubuntu0.22.04.2).

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-08-08 14:01:27 +02:00
92e02f6e33 ldap: add an integration test for check_connection
Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-08-08 14:01:25 +02:00
445e032eee fix #4868: map missing section field to 'unknown'
needed for supporting some third-party repositories.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-08-08 13:59:17 +02:00
9fc23c1335 api-types: drop unused leftover file
Commit 5720ba2d ("use new auth api crate") moved all auth-related code
into it's own crate inside the `proxmox` repo, including this file. Thus
drop it here, it's not even included in the compile.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
2023-08-08 12:00:09 +02:00
5ea70421b3 clippy fix: casting to the same type is unnecessary
See: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:48:01 +02:00
41b2e49123 clippy fix: deref on an immutable reference
See:
https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
a36769b11a clippy fix: complex type definitions
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
3c7c9fc55d clippy fix: the following explicit lifetimes could be elided
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
6c38a997af clippy fix: useless use of format!
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
58b29dfbcf clippy fix: warning: this let-binding has unit value
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
b6abb52a78 clippy fix: binary comparison to literal Option::None
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
8f7566209c clippy fix: unnecessary use of to_string
See:
https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
b272279ef1 clippy fix: you should consider adding a Default implementation
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
268fcfb43a clippy fix: this (Default) impl can be derived
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
6f9cc14b9e clippy fix: redundant closure
See:
https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
82164203bd clippy fix: unneeded return statement
See:
https://rust-lang.github.io/rust-clippy/master/index.html#needless_return

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
81ca4ae6a1 clippy fix: needless borrow
See:
https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
b4b186c544 clippy fix: calls to drop with a value that implements Copy
Dropping a copy leaves the original intact

See:
https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy

I assume the `drop` was used to silence a 'unused variable' warning,
so I silenced it by other means.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:29:36 +02:00
de6337ae6d clippy fix: the borrowed expression implements the required traits
See: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-08-08 11:05:20 +02:00
b7a64cd4a2 proxmox-login: depend on js_sys on wasm32
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-08-07 12:03:49 +02:00
ffa64bead7 add special impl for epoch_i64() on target_arch="wasm32" 2023-08-07 11:56:15 +02:00
17f5eac57a client: drop Send for non-wasm as well on response future
To see if it is even still necessary given that it's not a trait
object type where auto traits would need to be explicit...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-07 11:06:10 +02:00
1531a619ab client: rename response future to ResponseFuture
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-07 10:59:07 +02:00
a7435e757b client: drop retry logic
This should be moved to where we actually need it, not be part of the
generic product client.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-07 10:57:39 +02:00
2b212cf4e3 proxmox-client: do not require Send for wasm32 target
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-08-05 09:10:28 +02:00
748588f81c proxmox-login: add 'source' impls for errors
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-03 13:03:30 +02:00
34a79cdd0c bump proxmox-api-macro to 1.0.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-03 08:25:13 +02:00
ba6a628601 client: getters for the inner client
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-03 08:22:45 +02:00
9599cb6fd6 proxmox-login: fix ticket userid check for PMG quarantine tickets
We simply strip the "@quarantine" at the end.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-08-02 15:10:44 +02:00
25024fa687 import proxmox-client crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-01 15:49:43 +02:00
a9191c2253 update README
include repository.workspace=true for the [package] base section

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-01 15:48:19 +02:00
d6a550b71c login: improve response handling
we have use cases where we have bytes, and serde_json has a from_slice
method, doing the utf-8 check unnecessarily is pointless, while going
from &str to &[u8] is free...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-01 15:21:09 +02:00
2226107cc6 api-types: set serde defaults for UserWithTokens
since `totp_locked` is not wrapped in an `Option` we need to
explicitly tell serde about its default

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-08-01 09:12:43 +02:00
5859017061 bump proxmox-notify to 0.2.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-28 11:56:56 +02:00
17cf5f9593 more import cleanups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-28 11:47:39 +02:00
7cb339dfa3 notify: cleanup all the imports sections
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-28 11:47:39 +02:00
985717d948 bump proxmox-router to 2.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-28 11:26:01 +02:00
1ce7294e06 http-error: add debian packaging and bump as 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-28 11:06:48 +02:00
1a75668dc9 notify: use HttpError from proxmox-http-error
Also improve API documentation in terms of which HttpError is
returned when.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-28 10:47:07 +02:00
add38769f8 router: re-export HttpError from proxmox-http-error
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-28 10:47:05 +02:00
779c45eaad http-error: add new http-error crate
Break out proxmox-router's HttpError into it's own crate so that it can
be used without pulling in proxmox-router.

This commit also implements `Serialize` for `HttpError` so that it can
be returned from perlmod bindings, allowing Perl code to access the
status code as well as the message.

Also add some smoke-tests to make sure that the `http_bail` and
`http_err` macros actually produce valid code.

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-28 10:47:03 +02:00
a1cbaea766 notify: fix tests if not all features are enabled
Some tests are now disabled if not all required features are enabled.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-28 10:43:57 +02:00
f6fa851d1f notify: fix build warnings if not all features are enabled
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-28 10:43:55 +02:00
1e095e5ab8 bump proxmox-notify to 0.1.0-1, initial release
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 11:05:45 +02:00
d951b3329f bump proxmox-auth-api to 0.3.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 11:03:49 +02:00
7ea502b351 bump proxmox-section-config to 2.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 11:03:15 +02:00
9c5579d755 bump proxmox-subscription to 0.4.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:57:50 +02:00
1a8a3668a5 bump proxmox-tfa to 4.0.5
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:56:14 +02:00
b86a0883ea bump proxmox-rest-server to 0.4.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:54:18 +02:00
33d9ea78ef bump proxmox-apt to 0.10.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:53:44 +02:00
d0bce77e50 bump proxmox-human-byte to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:53:44 +02:00
93fc5b503a bump proxmox-router to 2.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:53:44 +02:00
ac6a52dfd1 bump proxmox-schema to 2.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:42:14 +02:00
b46d333d98 notify: add debian packaging
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
[w.bumiller@proxmox.com: set d/changelog to UNRELEASED]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-24 10:34:52 +02:00
9b726bb072 notify: additional logging when sending a notification
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:26:08 +02:00
2756c11c2f notify: ensure that filter/group/endpoint names are unique
Otherwise, a filter with the same name as an already existing
endpoint or group can overwrite it.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:26:06 +02:00
62ae1cf959 notify: on deletion, check if a filter/endp. is still used by anything
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:26:04 +02:00
3c9584296f notify: api: allow to query entities referenced by filter/target
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:26:02 +02:00
d44ce2c70d notify: gotify: add proxy support
The proxy configuration will be read from datacenter.cfg via
a new method of the `Context` trait.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:26:01 +02:00
da2e7b8da9 notify: sendmail: query default author/mailfrom from context
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:59 +02:00
c5f91aa1c8 notify: sendmail: allow users as recipients
This introduces a new configuration parameter `mailto-user`.
A user's email address will be looked up in the product-specific
user database.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:57 +02:00
d6c1f181d6 notify: add context
Since `proxmox-notify` is intended to be used by multiple products,
there needs to be a way to inject product-specific behavior.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:51 +02:00
97dac11823 notify: add example for template rendering
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:49 +02:00
4865711339 notify: add template rendering
This commit adds template rendering to the `proxmox-notify` crate, based
on the `handlebars` crate.

Title and body of a notification are rendered using any `properties`
passed along with the notification. There are also a few helpers,
allowing to render tables from `serde_json::Value`.

'Value' renderers. These can also be used in table cells using the
'renderer' property in a table schema:
  - {{human-bytes val}}
    Render bytes with human-readable units (base 2)
  - {{duration val}}
    Render a duration (based on seconds)
  - {{timestamp val}}
    Render a unix-epoch (based on seconds)

There are also a few 'block-level' helpers.
  - {{table val}}
    Render a table from given val (containing a schema for the columns,
    as well as the table data)
  - {{object val}}
    Render a value as a pretty-printed json
  - {{heading_1 val}}
    Render a top-level heading
  - {{heading_2 val}}
    Render a not-so-top-level heading
  - {{verbatim val}} or {{/verbatim}}<content>{{#verbatim}}
    Do not reflow text. NOP for plain text, but for HTML output the text
    will be contained in a <pre> with a regular font.
  - {{verbatim-monospaced val}} or
      {{/verbatim-monospaced}}<content>{{#verbatim-monospaced}}
    Do not reflow text. NOP for plain text, but for HTML output the text
    will be contained in a <pre> with a monospaced font.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:48 +02:00
109a936b6b notify: api: add API for filters
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:46 +02:00
ee0ab52b9b notify: add notification filter mechanism
This commit adds a way to filter notifications based on severity. The
filter module also has the necessary foundation work for more complex
filters, e.g. matching on properties or for creating arbitarily complex
filter structures using nested sub-filters.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:43 +02:00
ee44fdca04 notify: api: add API for groups
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:41 +02:00
ed5d27ba24 notify: add notification groups
When notifying via a group, all endpoints contained in that group
will send out the notification.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:39 +02:00
055db2d107 notify: api: add API for gotify endpoints
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:37 +02:00
990fc8efd2 notify: add gotify endpoint
Add an endpoint for Gotify [1], showing the how easy it is to add new
endpoint implementations.

[1] https://gotify.net/

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:35 +02:00
21c5c9a0c7 notify: api: add API for sendmail endpoints
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:33 +02:00
7c42752690 notify: add sendmail plugin
This plugin uses the 'sendmail' command to send an email
to one or more recipients.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:31 +02:00
714ef27786 notify: api: add API for sending notifications/testing endpoints
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:30 +02:00
ad3f78a315 notify: preparation for the API
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:28 +02:00
2726e68afe notify: preparation for the first endpoint plugin
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:27 +02:00
b8040a23cb add proxmox-notify crate
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:25 +02:00
82be261447 schema: add schema/format for comments
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:22 +02:00
8013a80b41 section-config: derive Clone for SectionConfigData
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-07-24 10:25:20 +02:00
f6e089555e schema: verify property strings w/ new serde code
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:19 +02:00
12da5121ff schema: predictable order of errors for tests
Otherwise we'd have to "search" & match the errors...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:17 +02:00
c702638bd8 schema: fixup empty error list handling
Some(<empty list of errors>) does not actually signal an error...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:16 +02:00
aa10025366 schema: guard property string constraint checking
StringSchema::check_constraint runs `parse_property_string` for
property strings, but when we deserialize a `PropertyString` we
immediately follow that up with deserializing it using the schema, so
there's no need to check it beforehand.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:13 +02:00
1a46283b78 schema: get rid of some unsafe code
the borrow tracking won't hurt...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:11 +02:00
73a20c7f5f schema: use schema when serializing property strings
Adds a Schema to the `PropertyString` type and uses it for better
serialization.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:09 +02:00
413d631fa6 schema: convenience accessors to schema subtypes
Adds `const fn <type>(&self) -> Option<&<Type>Schema>` methods to
`Schema`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:19:05 +02:00
5a64f3258a schema: serde based property string de- and serialization
This provides `proxmox_schema::property_string::PropertyString<T>` for
a typed property-string.

To facilitate this, this introduces
`proxmox_schema:🇩🇪:SchemaDeserializer` which is a serde deserializer
for property strings given a schema.

This basically maps to one of `de::SeqAccess` (for array schemas) or
`de::MapAccess` (for object schemas).

Additionally, a `de::NoSchemaDeserializer` is added, since properties
within the strings may have string schemas with no format to it, while
the type we serialize to may ask for an array (a simple "list") via
serde.

The deserializers support borrowing, for which a helper `Cow3` needed
to be added, since property strings support quoting with escape
sequences where an intermediate string would be allocated and with an
intermediate lifetime distinct from the `'de` lifetime.

A `de::verify` module is added which uses serde infrastructure to
validate schemas without first having to deserialize a complete
`serde_json::Value`.

For serialization, `proxmox_schema::ser::PropertyStringSerializer` is
added split into similar parts `ser::SerializeStruct` and
`ser::SerializeSeq` at the top level, and the same prefixed with
`Element` for inside the actual string. This should also properly
quote the contents if required.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-13 16:18:58 +02:00
4ca8dbf74f auth-api: fixup examples
These were missing the new client-ip parameter in the auth
function calls which was introduced to support `PAM_RHOST`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-10 09:06:35 +02:00
ec5e2a5d90 proxmox-apt: bump to 0.10.3-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-07-05 12:38:08 +02:00
50136f1817 bump proxmox-tfa to 4.0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-05 10:46:12 +02:00
8547ee31da tfa: also reset counters when unlocking tfa
Since this requires access to the user data, we need to add
a generic parameter to the unlock methods.
To avoid having to create another major API bump affecting
all our products this short after release, we keep the old
version around with the old behavior.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-07-04 14:45:18 +02:00
415d60daf9 release file: extend component fixup to bookworm
else mirroring bookworm-security will skip *all* components..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-06-29 13:11:17 +02:00
2a070da065 proxmox-rest-server: bump to 0.4.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-06-27 12:44:52 +02:00
880abd859b proxmox-apt: bump version to 0.10.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-06-27 12:36:05 +02:00
902a0e8cb5 api: include tfa lock status in user list
Like in PVE.
This means that /access/users is now a 'protected' call to
get access to 'tfa.cfg'.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-26 19:47:16 +02:00
f486f8485c access: ldap check connection on creation and change
this commit makes the ldap realm endpoints check whether a new or
updated configuration works correctly. it uses the new
`check_connection` function to make sure that a configuration can be
successfully used to connect to and query an ldap directory.

doing so allows us to remove the ldap domain regex. instead of relying
on a regex to make sure that a given distinguished name (dn) could be
correct, we simply let the ldap directory tell us whether it accepts
it. this should also aid with usability as a dn that looks correct
could still be invalid.

this also implicitly removes unauthenticated binds, since the new
`check_connection` function does not support those. it will simply
bail out of the check if a `bind_dn` but no password is configured.
therefore, this is a breaking change.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-06-26 16:08:24 +02:00
54cb9be8ed bump proxmox-ldap to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-26 14:22:50 +02:00
7f135263a9 ldap: add check_connection function
this function checks if a given connection could work. it uses the
current config to connect to an ldap directory and perform a search
with the provided base_dn. this enables us to verify a connection
before storing it in a more meaningful way than with a regex.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-06-26 14:15:35 +02:00
599a6a49da ldap: remove support for unauthenticated binds
by using the default empty string if no password was provided,
unauthenticated binds were possible. to bring pbs in-line with pve,
switch to throwing an error in this case instead. however, this will
break any pre-existing setup that relied on this behavior.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2023-06-26 14:15:33 +02:00
053e83c3c7 api-types: client: datastore: tools: use proxmox-human-bytes crate
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-06-26 13:56:45 +02:00
962ce920a0 bump proxmox-human-byte to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-26 13:37:29 +02:00
c611afcf0d human-byte: update copyright format
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-26 13:37:29 +02:00
77dbc2fe18 human-byte: move tests to their sub module
The `#[cfg(test)]` directive ensures that the tests are not compiled
for non-test builds.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-06-26 13:24:51 +02:00
c3545d6644 add proxmox-human-byte crate
The module previously lived in `pbs-api-types`, however turned out to
be useful in other places as well (POM, proxmox-notify), so it is moved
here as its own micro-crate.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-06-26 13:24:48 +02:00
d5b9f166a2 cargo fmt
bigger changes are only in the new crates
rest are minor ones

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-23 11:50:39 +02:00
b687edc1a0 rest: remove full static file path from error messages
this triggers certain security scanners, and having the requested path instead
gives basically the same information anyhow.

reported on the forum: https://forum.proxmox.com/threads/404-path-disclosure-vulnerability.129187/

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-06-23 11:46:52 +02:00
5791af8ff4 ldap: rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-23 11:20:25 +02:00
378e2380b7 ldap: surround user filter expression in parenthesis if not already
In PVE, the `filter` attribute is surrounded in () if it is not already,
allowing "uid=test" as well as "(uid=test)" [1].

A forum user [2] just ran into this inconsistency, so I decided to adjust
the behavior.

[1] https://git.proxmox.com/?p=pve-common.git;a=blob;f=src/PVE/LDAP.pm;h=ff98e367e63265bf76c0f302847c3749eea095a6;hb=HEAD#l115
[2] https://forum.proxmox.com/threads/ldap-query-for-security-group-members.127882/

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-06-23 11:19:04 +02:00
84edb20eb8 tfa: Improve TOTP algorithm parsing
It is very common for TOTP URIs to contain the algorithm in lowercase,
hence we convert to lowercase when doing From<&str> for Algorithm.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
2023-06-20 12:39:46 +02:00
16d512b4d9 bump proxmox-compression to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-15 11:01:19 +02:00
5b55ea797a compression: match style fixup
if the match arms are this far away from the actual `match`
keyword, this needs to be split up...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-15 10:37:59 +02:00
af46b655d4 compression: zip_directory: improve error handling
when zipping a directory, our intention was to skip over files that
cannot be zipped (e.g. the file can't be read/vanished/etc.), so we
ignored errors and simply logged it.

but when 'add_entry' fails, we will never actually restore, since every
error there is fatal to the point that the zip cannot be finished thats
because we take the 'target' sink out of self, and only insert it again
after all writes succeeded. so if an error occurs in between 'target' is
not put into self again (and never will be) and the zip cannot be
finished (even if we would catch all those intermediate errors and
restore 'target', we don't know in which state the output was, so we're
unable to finish a valid zip)

to fix that, split the actual 'add_entry' part there out of the async
move block and treat its errors always as fatal

without this, we generate heaps of log lines even after an error
occurred, and can never recover

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-06-15 10:28:50 +02:00
39a486f574 bump proxmox-tfa to 4.0.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-14 13:52:20 +02:00
5f1a0bc0c9 tfa: make TfaUser fields public
So we can print them in the proxmox-backup-manager CLI text
output.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-14 13:50:38 +02:00
ba39c5f990 auth-api: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-14 13:50:29 +02:00
cc091c4fe5 bump proxmox-auth-api to 0.3.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-14 09:55:31 +02:00
a228a22918 auth-api: set PAM_RHOST during pam authentication
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-14 09:32:03 +02:00
8f08039e7e auth-api: drop pam crate
it's too limited

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-14 08:51:43 +02:00
c8433e3219 fix #4653: (In)Release file: improve handling of special suites
APT doesn't mind a repository with either "/" or "./" as suite/distribution,
such as

 deb https://example.com/debian ./

in that case, the 'dists' part of the URL and the trailing slash (which would
be encoded as '_') is dropped in the file name in '/var/lib/apt/lists/'.

Other suite values with a trailing or leading '/' are rejected with an error by APT:

 E: Malformed entry 1 in sources file /etc/apt/sources.list.d/test.list (absolute Suite Component)
 E: The list of sources could not be read.

so this should be the only special case requiring handling.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-06-06 18:24:39 +02:00
27cd025053 fallback to Release file for Origin retrieval
APT will not store the InRelease file in some cases, and some repositories
might not even have one in the first place.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-06-06 18:24:39 +02:00
35dc1b0b8d bump proxmox-tfa to 4.0.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-05 08:59:55 +02:00
0fb7ec0c32 buildsys: also cleanup *.build files for convenience
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-06-04 18:27:46 +02:00
45432b689d apt: bump version to 0.10.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-06-03 18:29:04 +02:00
a367f6a0d8 build.sh: split overly long line
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-06-03 16:48:10 +02:00
ce6a87d513 buildsys: don't try to resolve dependency metadata when assembling crate list
A build-system that needs all build-dependencies even for a simple
make clean invocation is a PITA..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-06-03 16:39:39 +02:00
cc17861f54 apt: tests: add tests for Ceph Quincy repository detection on Bookworm
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2023-06-03 14:39:05 +02:00
06ad528c94 apt: tests: code cleanup to avoid useless vector
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2023-06-03 14:39:05 +02:00
9b7c5339d7 apt: tests: create temporary test directories in CARGO_TARGET_TMPDIR
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2023-06-03 14:39:05 +02:00
bae15e2408 apt: split Ceph main repository into no-subscription and enterprise
The old 'main' component stays valid, pointing to no-subscription,
which means the is_referenced_repository() check needs a special case
for it. It will eventually go away, together with the handles for
Quincy.

Alternatively, the standard repository's info() could've been changed
to return multiple possible components, similar to URLs, but as
opposed to URLs, there could be a standard repository that wants to
have multiple components and it feels a bit unnatural, because
multiple components are usually not aliases of the same. And adapting
is_referenced_repository() would be needed here too. So overall, the
above alternative just felt better.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2023-06-03 14:39:05 +02:00
21b58c3384 apt: drop older Ceph standard repositories
On Proxmox VE 8, only Quincy and newer will be supported.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2023-06-03 14:39:05 +02:00
4508c8b23c add some missing d/source/format files
For crates which already used (native) versioning.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-02 08:44:50 +02:00
abd2558b01 tfa: reduce default lockout time to an hour
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-01 15:10:53 +02:00
adb868ee08 tfa: include lockout status in the tfa user list
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-01 12:22:18 +02:00
c5a9fa8595 bump proxmox-tfa to 4.0.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-31 12:54:47 +02:00
f3666afd22 tfa: add d/source/format
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-31 12:54:47 +02:00
a26ec45d74 tfa: add api::methods::unlock_tfa
This mostly serves as documentation for the API call to be
implemented across our products. It's otherwise already just
a oneliner on the TfaConfig.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-31 12:54:47 +02:00
46c15171e6 buildsys: remove format.debcarg.hint file
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-31 12:54:47 +02:00
656ec1e7db bump proxmox-openid to 0.10.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-24 09:50:33 +02:00
c25c1cf4cd bump proxmox-apt to 0.10.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-24 09:50:33 +02:00
3c85df6830 proxmox-apt: update tests to expect bookworm
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-24 09:50:33 +02:00
077a83f401 add proxmox-apt and proxmox-openid to workspace
and fixup d/copyright

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-24 09:24:27 +02:00
6253f263ce Merge branch 'proxmox-openid-merge' 2023-05-24 09:22:09 +02:00
68ebe9ec8a Merge branch 'proxmox-apt-merge' 2023-05-24 09:22:04 +02:00
0ff81719ad move to proxmox-apt/
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-24 09:21:55 +02:00
88d1783a65 move to proxmox-openid/
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-24 09:20:44 +02:00
6c8191471e bump proxmox-subscription to 0.4.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
e5ff0dc40b bump proxmox-auth-api to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
c531c314c6 bump proxmox-rest-server to 0.4.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
ca56a67251 bump proxmox-metrics to 0.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
af2d4c6c86 bump proxmox-http to 0.9.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
644852296d bump proxmox-shared-memory to 0.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
4f2a7d971b bump proxmox-compression to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
dd87a120bd bump proxmox-sys to 0.5.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
bd63af3c3b make upload: bump dist to bookworm
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
818ac8e708 update zstd 0.6 -> 0.12 for bookworm
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:51 +02:00
8f8d52f148 update d/copyright files to debian copyright-format 1.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 13:02:39 +02:00
392290ec6c buildsys: improve clean target
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 10:50:33 +02:00
77e8db8649 buildsys: add dsc and %-dsc targets
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-23 10:50:27 +02:00
76ac1a3903 bump proxmox-tfa to 4.0.0-1, auth-api to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-10 10:43:21 +02:00
4324aea004 auth-api: update to new tfa crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-10 10:43:21 +02:00
39017fa334 tfa: add functions to unlock totp and tfa
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-10 10:35:54 +02:00
a3448feb1a tfa: log all tfa verify errors and treat as failure, count
Use a custom result type to return success/failure and the
need to save the user data to the caller, while having
logged the error messages rather than returning them.

We count general TFA failures and also TOTP specifically,
and lock the user out of their 2nd factors on too many
failures.

To this end, all errors are now treated as failures.
While technically we can have crypto errors the user might
not be able to cause, we can't always know, and not all
errors are guaranteed to be a host side configuration issue,
so instead, all errors (since they are rare) now now counted
as a regular TFA error.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-10 10:35:54 +02:00
50b793db8d tfa: add data for rate limiting and blocking
TfaUserData uses `#[serde(deny_unknown_fields)]`, so we add
this now, but using it will require explicitly enabling it.

If the TOTP count is high, the user should be locked out of
TOTP entirely until they use a recovery key to reset the
count.

If a user's TFA try count is too high, they should get rate
limited.

In both cases they should receive some kind of notification.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-10 10:26:51 +02:00
8d968274f1 tfa: make 'anyhow' optional, enable with the 'api' feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
3224f42ff5 tfa: fix warning with types feature w/o api feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
5c39559cad tfa: drop anyhow from totp module
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
c45620b447 tfa: drop anyhow from u2f module
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
0d942e81a3 tfa: add a 'types' feature to get TfaInfo and TfaType
without adding the entire API as well, so API clients can
actually use the types used by the api methods without
requiring the backend implementation being built in as
well...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
b6840e95ad tfa: make failing to generate a webauthn challenge non-fatal
If WA or U2F fail to produce a challenge, the user may still
log in with other factors and the challenge will be
considered to not be empty.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
4b3d171b2d tfa: don't return a challenge if all 2nd factors are disabled
Instead, this should allow the user to login without them.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
ea1d023a61 tfa: don't automatically drop empty recovery
This should only ever be explicitly removed.

Similarly, include an empty array of recovery keys in the
tfa challenge, so that clients know about empty recoveries
rather than getting an empty challenge when there are no
other factors available.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-08 10:32:26 +02:00
b66ceaede0 proxmox-longin: allow access to RecoveryState keys (make it pub)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-05-08 10:26:54 +02:00
a41c8481e2 proxmox-login: pass body as &str to response()
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2023-05-08 08:23:10 +02:00
be169e25ae add new proxmox-login to workspace members 2023-05-05 09:29:50 +02:00
26f586d5eb new proxmox-login package
Author: Wofgang Bumiller <w.bumiller@proxmox.com>
2023-05-04 09:09:08 +02:00
f971b8c1e6 partial fix #3701: sync job: pull: add transfer-last parameter
Specifying the transfer-last parameter limits the amount of backups
that get synced via the pull command/sync job. The parameter specifies
how many of the N latest backups should get pulled/synced. All other
backups will get skipped.

This is particularly useful in situations where the sync target has
less disk space than the source. Syncing all backups from the source
is not possible if there is not enough disk space on the target.
Additionally this can be used for limiting the amount of data
transferred, reducing load on the network.

The newest backup will always get re-synced, regardless of the setting
of the transfer-last parameter.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
2023-04-24 15:18:51 +02:00
175a8c6d7e api types: fix non-capturing group syntax
a non capturing group is '(?:)' not '(:?)' so fix that.
None of these regexes are used where would use capturing groups.
DATASTORE_MAP_REGEX and TAPE_RESTORE_SNAPSHOT_REGEX are only used
as api types and BLOCKDEVICE_NAME_REGEX is only used once outside of the
api and there we also don't look at the capturing groups.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-04-06 07:28:58 +02:00
12674a37e0 api-macro: support non-idents in serde(rename)
For PVE we'll have enum variants like /dev/urandom...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-04-03 10:01:44 +02:00
cd0d1cbc62 api-types: anchor datastore-map regex
Fixes: 4c4e5c2b ("api2/tape/restore: enable restore mapping of datastores")
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-03-29 11:58:59 +02:00
92cca750d9 api-types: ldap: properly anchor DN regex
Otherwise, a substring match is enough to fulfill the constraint.

Fixes: 3aba0d9a ("api-types: ldap: add verification regex for LDAP DNs")
Reported-by: Friedrich Weber <f.weber@proxmox.com>
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-03-29 11:29:10 +02:00
3aba0d9aa6 api-types: ldap: add verification regex for LDAP DNs
Regex was taken from the LDAP implementation in PVE.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-03-28 16:37:14 +02:00
39453abb8f http: sync: drop unused &self parameter
these are just internal helpers, changing their signature is fine.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-03-07 09:30:13 +01:00
6a1be173a6 http: sync: derive default user-agent from crate version
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-03-07 09:30:13 +01:00
5ba9d9b2c2 http: sync: remove redundant calls for setting User-Agent
the requests are all created via the agent that already contains the user
agent, so this internal helper isn't needed anymore.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-03-07 09:30:13 +01:00
d69fee254a http: sync: set user-agent via ureq agent
this allows us to slim down our code, and once
https://github.com/algesten/ureq/pull/597 is merged upstream (and/or we update
to a version containing the fix) it also means the custom user agent is used
for requests to the proxy host, if one is configured.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-03-07 09:30:13 +01:00
5720ba2dce use new auth api crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-03-02 17:01:35 +01:00
5df815f660 proxmox-tfa: update generated d/control
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-03-02 16:54:59 +01:00
32e7d3ccdf bump proxmox-auth-api to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:44:35 +01:00
1bccff7e68 auth-api: make example require pam-authenticator
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:44:35 +01:00
82e212e33a bump schema dependency to 1.3.7 for auth-api
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:44:35 +01:00
2f5b1f26cc bump proxmox-schema to 1.3.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:44:35 +01:00
bca9c6dbaf bump proxmox-tfa to 3.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:44:35 +01:00
5349ae208b add proxmox-auth-api crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:44:35 +01:00
a8bd8fca15 schema: add basic api types feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
f813e8d866 sort workspace members
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
8a90efba68 bump proxmox-metrics to 0.2.2
to update proxmox-http dep to 0.8

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
71794901c7 bump proxmox-subscription to 0.3.1
to update proxmox-http dependency

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
89eaf83755 bump proxmox-rest-server to 0.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
d422852f51 bump proxmox-http to 0.8.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
dcd6e85ab2 rest-server: update example to new ApiConfig
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
1f373b9276 rest-server: add wasm content type
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
b4bb3feef3 rest-server: tls-acceptor: allow setting cipher suite and list
just pass the strings to openssl

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
6873926dea rest-server: generic certificate path types
to not require a PathBuf on the caller side

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
2f2f5cfcd8 rest-server: more convenient alias-list for ApiConfig
To the existing `.alias(item)`, add a
`.aliases(into-item-iter)` similar to how `Extend` works.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
310310c650 rest-server: make all ApiConfig methods builder-style
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
e2ac53e3de rest-server: add AcceptorBuilder
The connection submodule now allows building an "acceptor"
for hyper connections which can either take an explicit ssl
acceptor, or builds a default one with a self signed
certificate.

The rate-limited-stream feature enables a method to
lookup/update rate limiters for connections.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
666f920291 rest-server: impl PeerAddress for RateLimitedStream via feature
rest-server can now optionally provide a PeerAddress
implementation for RateLimitedStream by activating its
'rate-limited-stream' feature

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
d7ed04f8e5 http: add RateLimitedStream::inner, drop peer_addr
instead of implementing 'peer_addr' specifically for
RateLimitedStream<tokio::net::TcpStream>, just provide
.inner() and .inner_mut() so the user can reach the inner
stream directly.

This way we can drop the tokio/net feature as well

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
b2c26f74a6 http: lower hyper feature requirements for client feature
instead of 'full', we only need 'tcp+http1+http2'

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
10a3ab222b http: move rate-limiting out of client feature
this can now be used separately

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
b62d76e80c http: start 0.8.0 refactoring
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
726bf413f5 rest-handler: more convenient auth/index handler setters
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
4639542fce rest-server: PeerAddress for Pin<Box<T>>
since this is how tokio-openssl's SslStream is used in
practice

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
515cc729d0 rest-server: drop ServerAdapter, move AuthError
Instead of a ServerAdapter for the index page and
authentication checking (which don't relate to each other),
provide a `.with_auth_handler` and `.with_index_handler`
builder for ApiConfig separately.

Both are optional. Without an index handler, it'll produce a
404. Without an auth handler, an `AuthError::NoData` is
returned.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
6904dcf4e6 rest-server: make adapter optional
when no user information or index needs to be defined

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:14:04 +01:00
4a5360aef4 rest-server: drop Router from ApiConfig
instead, allow attaching routers to path prefixes and also
add an optional non-formatting router

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:13:55 +01:00
258e2399a6 rest-server: make handlebars optional as 'templates' feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 16:07:50 +01:00
28ba2016e5 rest-server: cleanup unreadable code
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
a1119a3e63 rest-server: use BAD_REQUEST for non-GET on file-paths
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
93c027f5cc rest-server: make handle_request a method of ApiConfig
This is what actually defines the API server after all.
The ApiService trait in between is a hyper impl detail.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
5fe0777318 rest-server: drop allocation in Service impl
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
e377909bee rest-server: PeerAddr trait, drop proxmox-http dep
We pulled in proxmox-http with the client feature solely to
implement the `Service` trait on
`SslStream<RateLimitedStream<TcpStream>>`.

All those `Service` impls are the same: provide a peer
address and return an `ApiService`.
Let's put the `peer_addr()` call into a trait and build from
there.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
01436ae30f rest-server: make socketpair private
`proxmox_rest_server::socketpair` doesn't make sense as an
external API

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
ccc70bc95f rest-server: start 0.3 api refactoring
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:35 +01:00
1a14696a5c ldap: test fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-02 15:44:11 +01:00
7e12788c60 sys: drop sortable and identity macros
We should not use the sys crate to pull in the sortable
macro, just depend on its crate instead...
And the identity macro used to be required by the sortable
macro, but is not anymore and has been deprecated for a
while, so we can now drop it.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-03-01 13:40:40 +01:00
2cf54dcf2e router: make format&print generic
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-28 14:57:35 +01:00
8b3d568beb server: add LDAP realm sync job
This commit adds sync jobs for LDAP user sync. As of now, they
can only be started manually.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-10 12:46:19 +01:00
b6b18f65bc api-types: add config options for LDAP user sync
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-10 12:46:19 +01:00
dd67737942 api-types: add LDAP configuration type
The properties are mainly based on the ones from PVE, except:
  * consistent use of kebab-cases
  * `mode` replaces deprecated `secure`

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-10 12:46:19 +01:00
46e803256e release proxmox-ldap to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-10 10:42:40 +01:00
6dcdbd2bd1 bump proxmox-rest-server to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-10 10:42:40 +01:00
d696ad5bd1 rest-server: add handle_worker from backup debug cli
The function has now multiple users, so it is moved
here.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-10 10:23:41 +01:00
e8e8f83723 ldap: fixup d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-08 14:30:52 +01:00
870be885ed ldap: drop Ldap prefix from types that have it
for a bit more consistency and since we tend to repeat stuff
too much

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-08 14:29:12 +01:00
4ff5c59559 fix 'default-features = false' for ldap3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-08 14:26:34 +01:00
cd61c8741c ldap: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-02-08 14:15:44 +01:00
1db057e189 ldap: add debian packaging
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-08 14:11:24 +01:00
582e994cca ldap: tests: add LDAP integration tests
This commit adds integration tests to ensure that the crate works as intended.
The tests are executed against a real LDAP server, namely `glauth`. `glauth` was
chosen because it ships as a single, statically compiled binary and can
be configured with a single configuration file.

The tests are written as off-the-shelf unit tests. However, they are
 #[ignored] by default, as they have some special requirements:
   * They required the GLAUTH_BIN environment variable to be set,
     pointing to the location of the `glauth` binary. `glauth` will be
     started and stopped automatically by the test suite.
   * Tests have to be executed sequentially (`--test-threads 1`),
     otherwise multiple instances of the glauth server might bind to the
     same port.

The `run_integration_tests.sh` checks whether GLAUTH_BIN is set, or if
not, attempts to find `glauth` on PATH. The script also ensures that the
tests are run sequentially.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-08 14:11:21 +01:00
4488256cb1 ldap: allow searching for LDAP entities
This commit adds the search_entities function, which allows to search for
LDAP entities given certain provided criteria.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-08 14:11:18 +01:00
b9ab0ba4fa ldap: add helpers for constructing LDAP filters
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-08 14:11:15 +01:00
6fd77c9a5e ldap: add basic user auth functionality
In the LDAP world, authentication is done using the bind operation, where
users are authenticated with the tuple (dn, password). Since we only know
the user's username, it is first necessary to look up the user's
domain (dn).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-08 14:11:12 +01:00
0e2f88ccf3 ldap: create new proxmox-ldap crate
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
2023-02-08 14:11:08 +01:00
fbac2f0a0c sys: fixup error types handling
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-02-02 16:32:37 +01:00
ce389914ff sys: cope with unavailable KSM sharing info
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-02-02 16:12:20 +01:00
2cebe420c1 bump proxmox-time to 1.1.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-01-12 14:23:11 +01:00
fadf7f7bd8 re-add proxmox-uuid d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-01-12 14:22:52 +01:00
78d9b156a8 bump proxmox-uuid to 1.0.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-01-12 13:49:18 +01:00
ecf59cbb74 bump version to 0.9.9-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-01-11 18:42:20 +01:00
2f4254b414 cargo: update openidconnect to 2.4
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-01-11 18:41:06 +01:00
ac1f71eddb update d/control
after debcargo update

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 15:07:21 +01:00
093afb985f bump version to 0.9.8-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:26:47 +01:00
ae6bf664dd update nix to 0.26
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:24:51 +01:00
773400829a update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:24:46 +01:00
9c44e9b410 update d/control files
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:17:00 +01:00
3046e2f285 bump proxmox-rest-server to 0.2.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:15:14 +01:00
30ae33a31d bump proxmox-shared-memory to 0.2.3-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:13:53 +01:00
40cb468bef bump proxmox-router to 1.3.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:10:00 +01:00
d0c1958f86 bump proxmox-schema to 1.3.6-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:08:58 +01:00
01e9b3affc bump proxmox-sys to 0.4.2-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:07:45 +01:00
ff9aa2012e update nix to 0.26
it's the version currently shipped by bookworm, so let's unify this widely-used
dependency to make bootstrapping easier.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 12:07:16 +01:00
6953154254 update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-01-05 11:49:03 +01:00
61c3ac5b1b fix non-camel-case enums
This should have never been started to begin with...
2023-01-05 11:13:46 +01:00
915f6ab5d0 derive Clone and PartialEq for some API types
This is useful for react-lik GUI toolkits which need to do VDOM diffs.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-12-15 17:34:13 +01:00
78e86f3261 re-add epoch_to_rfc3339_utc on wasm target
This was lost in commit 980d6b26df.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-12-15 13:35:53 +01:00
acaf55c437 clippy fix
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-13 14:56:42 +01:00
6eb638c806 section-config: silence clippy
these two functions don't actually use the `type_name` parameter, but the
interface including custom formatters require it.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-13 14:53:28 +01:00
77ac0bd5fe section-config: make ReST dump reproducible
HashMaps are not ordered, so each package build containing a section config
dump would have the documentation ordered randomly.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-13 14:51:50 +01:00
cb2646c7b4 section config: fix handling array schema in unknown sections
Mostly relevant when the config is written out again after parsing it
with unknown sections. Previously, with duplicate keys, only the last
value would be saved. Now, duplicate keys are assumed to be part of
an array schema and handled as such.

Because the unknown section parsing does not know if a certain
property does actually have an array schema, it's not possible to
detect duplicate keys for non-array-schema properties, and if a
property with array-schema shows up only once, it will not be saved as
a Value::Array, but a Value::String.

Writing, or to be precise the format_section_content methods, already
handle Value::Array, so don't need to be adapted.

Fixes: 0cd0d16 ("section config: support allowing unknown section types")
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-12-12 14:03:26 +01:00
e97f41e290 section config: add test for array schema
where duplicate keys are allowed.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-12-12 14:03:23 +01:00
aaf4b72839 deps: bump api-macro to current version
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-12 11:51:08 +01:00
7bc85c05c9 bump proxmox-api-macro to 1.0.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-12 11:34:18 +01:00
38a60d3acb api: support #[default] attribute
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-12 11:34:18 +01:00
0719e1db1c update/extend README.rst
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 11:05:30 +01:00
ee8419cf2d workspace: switch remaining dependencies
while these are (currently) only used by a single member each, having *all*
dependency versions specified in the top level Cargo.toml only makes the whole
process of managing them less error-prone.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 11:05:30 +01:00
1380182538 update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 11:05:30 +01:00
2610208794 io: add boxed module for boxed bytes like vec::zeroed...
- proxmox_io::boxed::uninitialized(len) -> Box<[u8]>
  same as vec::uninitialized, but as a box

- proxmox_io::boxed::zeroed(len) -> Box<[u8]>
  same as vec::zeroed, but as a box

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-12 11:00:22 +01:00
a7d84effc5 io: deny unsafe_op_in_unsafe_fn
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-12 11:00:22 +01:00
8316fd3899 more workspace dependencies
regex was missed in the first pass, and two intra-workspace dev-dependencies as
well.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:30:30 +01:00
10f56e9358 sort dependencies
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:08:56 +01:00
89052a009d switch remaining member dependencies to workspace
these are only used by a single member at the moment, but we can move them to
the workspace to have a single location for version + base feature set
specification.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:08:36 +01:00
32504b78db switch remaining member dependencies to workspace
these are only used by a single member at the moment, but we can move them to
the workspace to have a single location for version + base feature set
specification.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:08:36 +01:00
8dbc2d7311 switch regular dependencies to workspace ones
where applicable.

notable changes:
- serde now uses 'derive' feature across the board
- serde removed from pbs-tools (not used)
- openssl bumped to 0.40 (and patched comment removed)
- removed invalid zstd comment

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:07:12 +01:00
b659deb529 switch regular dependencies to workspace ones
where applicable.

notable changes:
- serde now uses 'derive' feature across the board
- serde removed from pbs-tools (not used)
- openssl bumped to 0.40 (and patched comment removed)
- removed invalid zstd comment

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:07:12 +01:00
580399b26c switch proxmox dependencies to workspace
besides harmonizing versions, the only global change is that the tokio-io
feature of pxar is now implied since its default anyway, instead of being
spelled out.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:05:27 +01:00
d75e305162 switch proxmox dependencies to workspace
besides harmonizing versions, the only global change is that the tokio-io
feature of pxar is now implied since its default anyway, instead of being
spelled out.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:05:27 +01:00
6623ebdf2a workspace: inherit metadata
pbs-buildcfg is the only one that needs to inherit the version as well, since
it stores it in the compiled crate.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:05:27 +01:00
219af02796 workspace: inherit metadata
pbs-buildcfg is the only one that needs to inherit the version as well, since
it stores it in the compiled crate.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-12 09:05:27 +01:00
485ed1a2a2 switch exclude to workspace in README
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-09 13:31:51 +01:00
d3f2a86f80 buildsys: get crate list via cargo metadata in Makefile
so we don't have to keep this in sync manually

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-09 13:18:23 +01:00
e6d1e6440d add bump.sh
for bumping crates in this workspace (it requires cargo-edit being installed).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 10:36:27 +01:00
de6a59289a proxmox-time: drop TryFrom use statement
no longer needed with edition 2021

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:47 +01:00
46a675830d update d/control files
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:47 +01:00
48abc5afa3 update outdated workspace dependencies
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:47 +01:00
bdca6de588 update d/control files
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:47 +01:00
e5abc0590e define workspace dependencies in workspace
so that we no longer have to (or forget to) bump the version in multiple places.

notable changes:
- outdated versions have been unified
- proxmox-metrics -> proxmox-async no longer uses explicit empty features
  (proxmox-async doesn't provide any anyway)
- proxmox-subscription -> proxmox-http no longer uses explicit default_features
  = false (proxmox-http has an empty default feature anyway)
- missing path dependencies added (mainly proxmox-rest-server)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:38 +01:00
6c161bd5ab update d/control files
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:38 +01:00
4189221470 inherit shared, external dependencies
noteworthy changes:
- proxmox-http had a default_features_false dep on hyper, which is dropped (the
  default feature is empty anyway)
- hyper, libc, nix, tokio and url versions are unified
- missing (cosmetic) bindgen feature on zstd enabled everywhere

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:38 +01:00
64959d9ae0 move common metadata to workspace
and switch all crates to 2021 edition as well as a unified "authors" value.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-07 09:48:25 +01:00
378b763408 tree-wide: bump edition to 2021
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-06 13:31:01 +01:00
5acca01947 tree-wide: bump edition to 2021
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-06 13:31:01 +01:00
5ec765f842 update d/control files
debcargo 2.6 changed some minor details

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-06 11:21:43 +01:00
6a3a3f0413 use statement fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-12-05 14:11:16 +01:00
1aa6f0ea22 clippy 1.65 fixes
and rustfmt

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-05 11:40:02 +01:00
538578c558 clippy 1.65 fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-05 11:17:37 +01:00
50aa62b764 proxmox-schema: bump to 1.3.5
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-01 11:10:01 +01:00
30388b7256 schema: update to textwrap 0.16
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-12-01 11:06:41 +01:00
d70ae30a43 use derive 'Default' for ChunkOrder
instead of hardcoding the default deep inside the code. This makes it
much easier to see what is the actual default

the first instance of ChunkOrder::None was only for the test case, were
the ordering doe not matter

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-11-28 15:59:55 +01:00
854fb5c08f api-types: add MaintenanceType::Delete
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-28 14:45:39 +01:00
cf10742842 api-types: derive Display and FromStr for MaintenanceType
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-28 14:45:39 +01:00
5196e0bbee api-types: make Operation Eq
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-28 14:45:39 +01:00
017a0652cd datastore: make 'filesystem' the default sync-level
rationale is that it makes the backup much safer than 'none', but does not
incur a big of a performance hit as 'file'.

here some benchmark:

data to be backed up:
~14GiB semi-random test images between 12kiB and 4GiB
that results in ~11GiB chunks (more than ram available on the target)

PBS setup:
virtualized (on an idle machine), PBS itself was also idle
8 cores (kvm64 on Intel 12700k) and 8 GiB memory

all virtual disks are on LVM with discard and iothread on
the HDD is a 4TB Seagate ST4000DM000 drive, and the NVME is a 2TB
Crucial CT2000P5PSSD8

i tested each disk with ext4/xfs/zfs (default created with the gui)
with 5 runs each, inbetween the caches are flushed and the filesystem synced
i removed the biggest and smallest result and from the remaining 3
results built the average (percentage is relative to the 'none' result)

result:

test         none     filesystem         file
hdd - ext4   125.67s  140.39s (+11.71%)  358.10s (+184.95%)
hdd - xfs    92.18s   102.64s (+11.35%)  351.58s (+281.41%)
hdd - zfs    94.82s   104.00s (+9.68%)   309.13s (+226.02%)
nvme - ext4  60.44s   60.26s (-0.30%)    60.47s (+0.05%)
nvme - xfs   60.11s   60.47s (+0.60%)    60.49s (+0.63%)
nvme - zfs   60.83s   60.85s (+0.03%)    60.80s (-0.05%)

So all in all, it does not seem to make a difference for nvme drives,
for hdds 'filesystem' increases backup time by ~10%, while
for 'file' it largely depends on the filesystem, but always
in the range of factor ~3 - ~4

Note that this does not take into account parallel actions, such as gc,
verify or other backups.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-11-28 09:49:55 +01:00
d513ef7836 bump proxmox-section-config to 1.0.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-28 09:30:03 +01:00
c39d04c33e minor doc fixup
an 'ignore' block assumes rust syntax, a 'text' block should
just be plain text

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-28 09:27:28 +01:00
0cd0d16c1e section config: support allowing unknown section types
Similar to commit c9ede1c ("support unknown types in section config")
in pve-common.

Unknown sections are parsed as String-JSON String key-value pairs
without any additional checks and also written as-is.

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-11-28 09:19:05 +01:00
bb7519af3b subscription: recognize 'Suspended' status
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-11-25 09:53:58 +01:00
3193237afd rrd: add Entry::get() to access the data
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-24 13:53:49 +01:00
b5708459d3 api-types: derive Ord for BackupDir
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-11-15 10:16:54 +01:00
7a98c5d50c datastore: improve sync level code a bit
fixups for DatastoreFSyncLevel:
* use derive for Default
* add some more derives (Clone, Copy)

chunk store:
* drop to_owned for chunk_dir_path

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-10-28 13:04:22 +02:00
208d4ffac6 bump proxmox-section-config to 1.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-24 14:07:30 +02:00
c8f26ee0ea section-config: expose order
we already expose the raw sections which are sometimes
easier to use, but we don't expose the order at all this way
otherwise

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-24 13:45:58 +02:00
b4a798f0a7 section-config: use Vec for section order
We use none of the additional functionality provided by
VecDeque.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-24 13:44:23 +02:00
3b2b1214b3 section config: parse additional properties when schema allows it
Additional properties will be parsed according to the default string
schema.

This is relevant for use cases when the full schema is not known for
some reason or another. In particular this allows support for parsing
older/newer versions of configuration files. One example of this is
the proposed proxmox-mail-forward helper binary, which currently
doesn't have access to the PBS API types for dependency reasons and
is only interested in the email field for the root user. If it can
only use a minimal schema with additional_properties set to true, it
will be robust against changes.

Writing already works, because the ObjectSchema's verify_json()
already handles additional_properties correctly and
format_section_content() handles them like all other properties
(method doesn't depend on the schema).

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-10-24 13:35:50 +02:00
495da87f80 clippy fixes
the dropped .into() is guarded by the bumped build-dependency on
proxmox-sys 0.4.1, the missing Eq is a new clippy lint.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-24 12:10:19 +02:00
e0fb53e41d datastore: implement sync-level tuning for datastores
currently, we don't (f)sync on chunk insertion (or at any point after
that), which can lead to broken chunks in case of e.g. an unexpected
powerloss. To fix that, offer a tuning option for datastores that
controls the level of syncs it does:

* None (default): same as current state, no (f)syncs done at any point
* Filesystem: at the end of a backup, the datastore issues
  a syncfs(2) to the filesystem of the datastore
* File: issues an fsync on each chunk as they get inserted
  (using our 'replace_file' helper) and a fsync on the directory handle

a small benchmark showed the following (times in mm:ss):
setup: virtual pbs, 4 cores, 8GiB memory, ext4 on spinner

size                none    filesystem  file
2GiB (fits in ram)   00:13   0:41        01:00
33GiB                05:21   05:31       13:45

so if the backup fits in memory, there is a large difference between all
of the modes (expected), but as soon as it exceeds the memory size,
the difference between not syncing and syncing the fs at the end becomes
much smaller.

i also tested on an nvme, but there the syncs basically made no difference

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-10-20 14:59:15 +02:00
8a7a719aec bump version to 0.9.3-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-10-19 16:17:47 +02:00
b522722d9c deb822: source index support
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
 [ T: commit Sources for test & fix white space errors ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-10-19 15:55:54 +02:00
cfa77e0e88 sys: impl AsFd for PTY
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 14:36:45 +02:00
34688a6d74 sys: impl AsFd for PidFd
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 14:35:07 +02:00
3bbdcf23e0 bump sys dep to 0.4.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 14:26:00 +02:00
34f47339d5 bump sys to 0.4.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 14:21:08 +02:00
7b350d3e43 proxmox-http: fix last changelog entry
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-19 14:17:42 +02:00
ab17fdb4d9 sys: deprecate RawFdNum
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 13:55:47 +02:00
88677d8955 sys: add From<OwnedFd/Fd> for Fd/OwnedFd temporarily
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 13:50:17 +02:00
8bd961acdc rest-server: update to OwnedFd
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 13:25:40 +02:00
8420c266af sys: deprecate Fd, add its methods as module functions
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 13:25:40 +02:00
85c260f6b5 sys: deprecate BorrowedFd
std has this now, stable since 1.63

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-19 10:55:16 +02:00
ab6c2c7493 packages file: add section field
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-19 07:51:35 +02:00
b039bf011b bump edition in rustfmt.toml
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-10-13 15:00:28 +02:00
008a771f3e cargo fmt
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-12 10:42:50 +02:00
661b8837f3 clippy fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-12 10:42:24 +02:00
d97a86c15b cargo: rrd: set license in subcrate too
in preparation of moving this out

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-10-11 15:46:30 +02:00
7c7e2f886c rest-server: add packaging and bump to 0.2.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-10-11 15:09:50 +02:00
bd00e2f317 cargo: rest-server: set license property
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-10-11 15:09:44 +02:00
a7122eb99e Merge remote-tracking branch 'proxmox-rest-merge/master'
split out from proxmox-backup using `git filter-repo` including
history with the following --paths-from-file:

```
proxmox-rest-server
src/api/server.rs
src/server/command_socket.rs
src/server/config.rs
src/server/environment.rs
src/server/formatter.rs
src/server/h2service.rs
src/server/rest.rs
src/server/state.rs
src/tools/compression.rs
src/tools/daemon.rs
src/tools/file_logger.rs
src/worker_task.rs
```
2022-10-11 15:09:28 +02:00
95f7232188 cargo fmt
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-11 09:48:11 +02:00
28e30719e8 clippy fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-11 09:48:04 +02:00
4a13373c4b clippy fixes
and one additional API fn "allow many parameters" addition.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-10-11 09:36:12 +02:00
dbd5906402 fix #4274: implement prune notifications
we converted the prune settings of datastores to prune-jobs, but did
not actually implement the notifications for them, even though
we had the notification options in the gui (they did not work).

implement the basic ok/error notification for prune jobs

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-10-07 08:35:56 +02:00
49e553935f pbs-api-types: add FileRestoreFormat type
intended for passing the format to the file-restore client/daemon

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-10-05 18:40:49 +02:00
5450421f03 d/control: update
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-09-16 14:19:07 +02:00
6c0c48b97a bump version to 0.9.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-09-16 14:17:34 +02:00
7e8eab45dd release: fix typo in 'Acquire-By-Hash'
to allow detection of repositories that support downloading indices via
their hash instead of their filename.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-16 14:09:29 +02:00
f54534cc37 release: add 'architecture' helper
which returns if a file reference is architecture specific, and for
which architecture it is relevant.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-16 14:09:29 +02:00
566981077c release: add Commands file reference type
used by command-not-found to lookup which package ships which command.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-16 14:09:29 +02:00
2ae95b5f4e metrics: bump to 0.2.1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 14:49:35 +02:00
2502d691b6 subscription: bump to 0.3.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 14:49:35 +02:00
916aa8a2db update proxmox-router to 1.3.0
no real change for PBS usage - the ApiHandler enum is marked
non_exhaustive now because it has extra values if the new (enabled by
default) "server" feature is enabled.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 14:17:12 +02:00
e8d199d51c update to proxmox-http 0.7
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 14:17:00 +02:00
a6e03dfe42 subscription: properly forward verification error
when verifying the server response used for offline mirror keys.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 13:06:02 +02:00
d55816e9dd subscription: use lowercase for Display-ing status 2022-09-07 13:05:42 +02:00
31f1bbbf40 subscription: properly alias 'notfound'
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 13:05:42 +02:00
f908f216ae subscription: conditionalize checks
signed subscription info files should always be checked to catch
attempts of invalid signatures, but the age and serverid checks only
need to apply to "active" files, else the status might switch from a
more meaningful one to "invalid" by accident.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 13:05:42 +02:00
4beac11b34 subscription: add Expired status
this can be returned by the shop when checking an online subscription.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 13:05:42 +02:00
5b90667d05 http: bump to 0.7.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:35:51 +02:00
08a6d56eae http: client_trait: make request body generic
like the response body, instead of hard-coding Read.
2022-09-07 09:25:47 +02:00
891dcfda2f http: add extra_headers to post
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
ec77785578 http: sync: add HttpClient for Box<dyn Read>
for use cases where the full request body is not available from the
start, or the response doesn't need to be fully read in one go.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
00f5eca155 http: make post() take Read, not &str
for more flexibility.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
7863eff2a5 http: fix typo
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
da49b98d15 http: rename SimpleHttp to Client
so we have proxmox_http::client::Client for the async, hyper-based
client and proxmox_http::client::sync::Client for the sync, ureq-based
one.

this is a breaking change.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
7ffb895062 http: add "raw" sync client
and switch String one over to use it.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
9c444d7a94 http: add ureq-based sync client
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
f429fcb592 http: extend HttpClient trait
to allow get requests with extra headers (such as `Authorization`) and a
generic `request` fn to increase flexibility even more.

this is a breaking change.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
ab5d5b39f6 http: move SimpleHttpOptions to http-helpers feature
and rename it to HttpOptions, since it's not specific to the "Simple"
client at all.

this is a breaking change.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
e8d02db689 router: bump to 1.3.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
66ace63618 router: make hyper/http optional
but enable it by default.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-07 09:17:45 +02:00
0a9685ed4a bump version to 0.9.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-06 10:36:55 +02:00
64d7956788 add ceph quincy repositories
Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
2022-09-06 09:37:46 +02:00
0376c3b50b build: more missing features
these would cause failures when building the sub-crates directly from
their sub-directory.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-09-05 12:55:33 +02:00
f92c8f92cc api-macro: track d/control
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-08-26 12:57:57 +02:00
056a5eb581 git: ignore top level *-deb make target files
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-08-26 12:18:44 +02:00
52a8eb6ace d/control: tree wide update after switch to weak/namespaced dependencies
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-08-26 12:07:36 +02:00
289d297c7d build: use weak and namespaced features
to reduce the creep of optional dependencies being exposed as features.

this requires cargo 0.63 (and debcargo built against at least that
version), but greatly reduces the number of binary packages and provides
generated, while still allowing sensible selection of optional
dependencies via the explicit feature meant for pulling them in.

diff stat for running `make  deb` after this change:
 proxmox-http/debian/control         | 226 ++++--------------------------------
 proxmox-router/debian/control       |  74 +-----------
 proxmox-schema/debian/control       |  53 ++-------
 proxmox-subscription/debian/control |  17 +--
 proxmox-sys/debian/control          |  51 +++-----
 proxmox-tfa/debian/control          | 110 ++----------------
 6 files changed, 72 insertions(+), 459 deletions(-)

the 'dep:' prefix marks something on the RHS inside the features section
as dependency, it's only allowed if the string after it is an optional
dependency an no explicit feature of the same name exists. if all
pointers to the optional dependency in the features section are marked
as such, the optional dependency itself will not be exposed as a feature
(either on the cargo or debian/control level).

the '?' suffix marks dependencies as "weak", which only enables the
optional dependency + its feature(s) if the optional dependency itself
is also enabled. it has no effect on d/control since such a relationship
is not encodable in Debian package relations, but it does affect cargo
dependency resolution and allows skipping the build of unneeded optional
dependencies in some cases.

with no packages/crates depending on the no longer exposed automatically
generated features/packages, so these are safe to remove even though
it's technically a breaking change.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-26 10:35:00 +02:00
1cd6a842f7 subscription: add missing path dependencies
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-19 14:20:58 +02:00
d7082b037d make: add proxmox-metrics to crate list
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-19 14:20:02 +02:00
9478ae2bed fixup! time: update to nom 7 2022-08-19 14:19:39 +02:00
12da49b5ec schema: bump to 1.3.4
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-19 12:41:15 +02:00
1349f24d49 schema: update to textwrap 0.15
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-19 12:26:42 +02:00
1344ffdd94 time: bump to 1.1.4
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-19 12:22:45 +02:00
552f14e916 time: update to nom 7
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-19 12:22:27 +02:00
5ac4e0fcae more stable clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-08-17 09:22:32 +02:00
ff3fcebda7 jws: allocate exact capacity
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-08-17 09:07:41 +02:00
e499b084c8 replace deprecated 'affine_coordinates_gfp' call
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-08-17 09:03:27 +02:00
26e2bce4a7 release: add proper error message
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-05 11:28:58 +02:00
0e84522116 cleanup non-closure parse helpers
The TryFrom impl is already massive enough as it is.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-08-05 09:35:40 +02:00
b2f851da87 release-file: improve invalid file-reference handling
if we encounter a file reference pointing to a component that is not
contained in the componenents list, we can just ignore it as unknown.
only treat parsing errors for references pointing to known components as
actual errors.

this currently triggers with (In)Release files for debian-updates and
debian-security, which reference (empty) files for a "non-free-firmware"
component that is not listed in the `Components` field of the release
file.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-08-05 09:10:07 +02:00
5d61126f58 bump proxmox-sys dep to 0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:47:37 +02:00
505e28d8a3 bump proxmox-sys dep to 0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:47:37 +02:00
ce6def2192 bump version to 0.9.7-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:42:52 +02:00
40aae0593a bump proxmox-sys dep to 0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:41:59 +02:00
b3e2a1f574 bump d/control files
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:39:21 +02:00
d52a1b7889 bump proxmox-subscription to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:35:47 +02:00
7540ebd238 bump proxmox-shared-memory to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:35:47 +02:00
2eacdbe090 bump proxmox-http to 0.6.5-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:35:47 +02:00
1d3f4a4bbd http, shared-memory, subscription: bump proxmox-sys dependency to 0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:35:47 +02:00
6dc2393625 sys: drop comment from Cargo.toml
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:29:42 +02:00
6e857c6090 bump proxmox-sys to 0.4.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-28 13:28:21 +02:00
56b5c28930 rrd: Entry type and clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 14:54:44 +02:00
00f16b4e94 rest-server: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 14:54:44 +02:00
3d670c9e4e api-types: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 13:29:32 +02:00
c5382d1b20 sys: doc fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 10:55:10 +02:00
28ee8bc6d0 http: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 10:52:50 +02:00
31a569b425 api-macro: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 10:52:07 +02:00
c78c47cff2 uuid: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-27 10:51:22 +02:00
6cac8d5cbe sys: another Arc::clone
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 14:45:39 +02:00
51746a0d45 sys: explicit Arc::clone
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 14:44:49 +02:00
74092debc5 more clippy fixes and annotations
the remaining ones are:
- type complexity
- fns with many arguments
- new() without default()
- false positives for redundant closures (where closure returns a static
  value)
- expected vs actual length check without match/cmp

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-26 14:05:25 +02:00
6e247b9593 sys: use Iterator::min instead of a manual version
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 13:21:36 +02:00
36903bf2fe sys: bump edition to 2021
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 13:04:09 +02:00
4de145aaef lang: update c_str
drop the let binding, easier to use in const context since
CStr::from_bytes_with_nul_unchecked is const since 1.59

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 12:27:44 +02:00
34d2c91118 sys: drop proxmox-borrow dependency
nix now has an owning directory iterator, we don't need it
anymore

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 12:24:14 +02:00
36625fb92c tfa: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 12:24:01 +02:00
d0b4f0bf2f tfa: docs fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-26 12:23:50 +02:00
df0d30a106 bump proxmox-tfa to 2.1.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-25 13:38:04 +02:00
a7f808d43b tfa: bump edition to 2021
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-25 13:35:58 +02:00
d396c3ea31 tfa: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-25 13:35:57 +02:00
ea34292850 tfa: expose 'allow_subdomains' property
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-25 13:25:52 +02:00
b84446a030 sys: enable CreateOptions::group_root/root_only
nix now has the required const fns

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-25 10:31:49 +02:00
d3364e07fb sys: file locking depends on timer feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-22 11:58:18 +02:00
e2e7ea6d62 sys: introduce 'acl', 'crypt' and 'timer' features
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-21 15:42:11 +02:00
f6c7d46d04 bump proxmox-subscription to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-21 14:36:49 +02:00
1148b795f3 bump version to 0.9.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-07-21 13:32:53 +02:00
6ff1c96021 add default signing key path
for use in dependent modules. this file should be shipped via
proxmox-archive-keyring.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-21 13:30:11 +02:00
5391f5313b subscription: make key optional and support multiple keys
this is a breaking change requiring updates in proxmox-perl-rs and
proxmox-backup.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-21 13:29:15 +02:00
2e929cc386 bump proxmox-http dep to 0.6.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-21 13:01:14 +02:00
d228ef6e20 http: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-21 12:58:41 +02:00
dc57115703 bump proxmox-http to 0.6.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-21 12:58:11 +02:00
a8a838754d http: fix proxy authorization header to include type
and encode the username:password string as base64 [0]. This fixes the
error 407 issue when using proxy authentication [1].

[0] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Proxy-Authorization
[1] https://forum.proxmox.com/threads/checking-the-subscription-behind-a-proxy-fails.112063/

Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
2022-07-21 12:54:59 +02:00
760c49be6e subscription: check in d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-21 12:54:59 +02:00
72dc88fb71 clippy fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-21 12:44:13 +02:00
3e515dbc7d AptRepositoryFile: make path optional
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-21 12:41:36 +02:00
03f14b74dd bump version to 0.8.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-07-21 12:10:36 +02:00
5a3c084219 deb822 checksums: factor out hash equality check into macro
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-07-21 12:10:23 +02:00
77fd3c8bce add test files for deb822 module
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-07-21 12:10:23 +02:00
8cdd231153 add module for parsing Packages and Release files
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-07-21 12:10:23 +02:00
28aafe6e3c file: add pre-parsed content variant
to allow usage with in-memory contents instead of actual files.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-07-21 12:10:23 +02:00
6794989b2a proxmox-subscription: initial bump to 0.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-20 13:37:08 +02:00
757031ef33 sys: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-20 13:35:58 +02:00
94d388b988 http: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-20 13:31:58 +02:00
5e630472ec subscription: clippy fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-20 13:28:31 +02:00
ab17e16664 subscription: line-wrap test data
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-20 13:28:31 +02:00
0cd02a0d2b subscription: doc comment fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-20 13:28:31 +02:00
3f694b5481 subscription: clippy lints
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-20 13:28:31 +02:00
baf31dc2d8 subscription: properly case status enum values
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-20 13:28:31 +02:00
38492bde83 check signature when reading subscription
and handle signed keys differently w.r.t. age checks, since they will be
refreshed less frequently.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-19 15:20:36 +02:00
4ec9a8183d add new proxmox-subscription crate
taking over slighlty generified helpers and types:
- subscription info and status
- checking subscription status with shop
- reading/writing local subscription-related files

the perl-based code uses base64 with newlines for the data, and base64
without padding for the checksum. accordingly, calculate the checksum
with and without newlines, and compare the decoded checksum instead of
the encoded one.

furthermore, the perl-based code encodes the subscription status using
Capitalized values instead of lowercase, so alias those for the time
being.

PVE also stores the serverid as 'validdirectory', so add that as alias
as well.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-19 15:20:36 +02:00
1647fc93ff api-types: make BackupType::iter an actual iterator
Otherwise we have to use BackupType::iter().iter() whenever
we're not using a `for _ in iter()` construct.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-14 11:14:26 +02:00
6e4a43d683 bump proxmox-sys to 0.3.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-07 11:49:57 +02:00
67fbf2b2ee sys: make escape_unit() more flexible, add unescape_unit_path
This adds the ability to use these functions with non-utf8
strings as well.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-07 11:45:26 +02:00
c7224c5f67 bump proxmox-compression to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-05 13:46:38 +02:00
ff86aa5f8a compression: more cleanups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-05 13:23:14 +02:00
2d37cd92e0 compression: indentation cleanup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-05 12:17:04 +02:00
cc6e5d7372 compression: minor cleanup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-05 12:13:38 +02:00
6e989a1c29 proxmox-compression: add 'tar_directory'
similar to 'zip_directory', this is intended to tar a local directory,
e.g. when we're in a restore vm.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-07-05 12:12:02 +02:00
38db37dc5f proxmox-compression: make ZstdEncoder stream a bit more generic
by not requiring the 'anyhow::Error' type from the source, but only
that it implements 'Into<Error>'. This way, we can also accept a stream
that produces e.g. an io::Error

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-07-05 12:11:57 +02:00
b5accff750 bump proxmox-http to 0.6.3-1 2022-06-30 12:42:18 +02:00
bd1f9f103e http: implement HttpClient for SimpleHttp
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-30 12:42:17 +02:00
3c0486be50 http: add HttpClient trait
gated behind feature "client-trait"

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-30 12:42:12 +02:00
214317df93 api-types: doc: add crate to Display trait in comments
when creating the documentation (e.g. `cargo doc --open`), it would
warn that `Display` is not in scope.

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-30 10:15:22 +02:00
94456ee4b1 http: move TLS helper to client feature
it's only used there and pulls in hyper and tokio, which we don't
want/need in http-helpers.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-29 10:32:44 +02:00
210d4fdb68 http: take over json_object_to_query 2022-06-29 10:32:44 +02:00
4393217633 bump proxmox-serde to 0.1.1 2022-06-29 10:32:44 +02:00
b239b56999 serde: take over to/write_canonical_json
from PBS' tools module, and feature-guard via optional `serde_json`
dependency.
2022-06-29 10:32:44 +02:00
f505240065 cargo fmt
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-29 10:32:29 +02:00
0d30720907 http: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 10:14:33 +02:00
064791e565 section-config: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 10:11:28 +02:00
7b1aad429f api-macro: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 10:10:07 +02:00
30901c60f5 compression: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 10:08:49 +02:00
b06b4c7426 lang: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 10:06:57 +02:00
1fca7b715d time: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 10:06:20 +02:00
b1a5daef61 sys: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 09:58:34 +02:00
f3fd79be13 bump proxmox-sys dep to 0.3.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 09:45:11 +02:00
2f82a04734 bump proxmox-sys dep to 0.3.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 09:45:11 +02:00
8f769a3996 bump proxmox-sys to 0.3.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 09:41:36 +02:00
1e2aa3eea4 partially fix #2915: proxmox-sys: scandir: stat if file_type is unknown
when using readdir/getdents the file type might be 'DT_UNKNOWN'
(depending on the filesystem). Do a fstatat in that case to
get the file type. Since maybe the callback wants to do
a stat anyway, pass it there (if done)

adds two new helpers:
'file_type_from_file_stat': uses a FileStat struct to get the file type
'get_file_type': calls fstatat to determine the file_type

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-29 09:34:34 +02:00
f81e9b259a bump proxmox-router dep to 1.2.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-21 10:43:19 +02:00
8e06108d10 proxmox-rest-server: replace print with log macro
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-21 10:43:19 +02:00
a6f9cf3d73 bump proxmox-router dep to 1.2.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-21 10:43:19 +02:00
09032fb88c bump proxmox-router to 1.2.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-21 10:40:05 +02:00
d934c79d4c router: restrict 'env_logger' dep to 'cli' feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-21 10:38:17 +02:00
32b8ae982f router: add init_cli_logger helper function
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-21 10:37:07 +02:00
41d8747de3 metrics: check in d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-13 10:03:48 +02:00
57fa204064 pbs-api-types: add metrics api types
InfluxDbUdp and InfluxDbHttp for now

introduces schemas for host:port and https urls

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-13 09:57:16 +02:00
b446456aa5 bump proxmox-metrics to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-13 09:56:05 +02:00
e12230d543 proxmox-metrics: send_data_to_channels: change from slice to IntoIterator
which is a bit generic and allows us to use e.g. a map result to be
passed here

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-13 09:53:47 +02:00
d845736270 tree wide: typo fixes through codespell
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-07 14:08:09 +02:00
be8f24ff5d tree wide: typo fixes through codespell
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-07 14:08:09 +02:00
2ec6f86f63 tree wide: typo fixes through codespell
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-07 14:08:09 +02:00
917f5f73af tree wide: clippy lint fixes
most (not all) where done automatically

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-02 15:59:55 +02:00
551cd92fa8 api types: clippy lints
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-02 15:57:07 +02:00
8e042eb130 update to nix 0.24 / rustyline 9 / proxmox-sys 0.3
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:33 +02:00
4faf81dc69 update to nix 0.24 / rustyline 9 / proxmox-sys 0.3
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:33 +02:00
7667e549a5 bump proxmox-http to 0.6.2
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
05d3c3f412 http: bump proxmox-sys to 0.3.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
980d6b26df proxmox-time: add missing 1.1.3 change
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
29c051f5f8 bump proxmox-router to 1.2.3
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
8cb1a99934 bump proxmox-schema to 1.3.3
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
f7c9738ead bump proxmox-shared-memory to 0.2.1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
e3232f2fe3 shared-memory: bump proxmox-sys to 0.3.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
9dd4134052 bump proxmox-sys to 0.3.0
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:33:12 +02:00
0ec392e425 bump version to 0.9.6
for nix 0.24 rebuild

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:12:23 +02:00
97fd3a0a14 sys: feature-gate logrotate (and zstd)
it's not needed everywhere we pull in proxmox-sys.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 14:08:37 +02:00
a3efe0b3dc bump rustyline to 9
it works with nix 0.24

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 13:34:09 +02:00
f8b19c2e22 proxmox-shared-memory: fix nix 0.24 compat in test
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-02 13:34:09 +02:00
abf5aedc54 update proxmox-router to nix 0.24.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 13:34:09 +02:00
92069b224d update proxmox-schema to nix 0.24.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 13:34:09 +02:00
bd0a7cb223 update proxmox-shared-memory to nix 0.24.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 13:34:09 +02:00
376af82be7 update proxmox-sys to nix 0.24.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 13:34:09 +02:00
28c85a828e proxmox-serde: move serde_json to dev-dependencies
it's only used in doctests

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 13:18:03 +02:00
f7da6ee7d4 bump proxmox-schema to 1.3.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 10:14:23 +02:00
d68a755536 schema: drop some commented out lines
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 10:14:23 +02:00
3facb7b455 router: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-06-02 10:10:01 +02:00
1d5b46c21e api types: prune keep options: also check weekly in keeps_something
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-01 14:30:24 +02:00
532140de86 schema: bump api macro dep to 1.0.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-31 09:43:59 +02:00
9079afc831 schema: clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-31 09:42:25 +02:00
288074a018 manager: hidden command to move datastore prune opts into jobs
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-30 13:58:43 +02:00
705ee9c93f add prune jobs api
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-30 13:58:43 +02:00
27718f2a72 api-types: add PruneJobConfig
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-30 13:58:43 +02:00
79cf434a79 datastore status: impl empty-status constructor for item type
we can now use it for the error case and will further use it for the
can access namespace but not datastore case in a future patch

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-26 13:34:00 +02:00
f6a37f40f6 tree-wide: remove DatastoreWithNamespace
instead move the acl_path helper to BackupNamespace, and introduce a new
helper for printing a store+ns when logging/generating error messages.

Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-26 11:42:10 +02:00
bbfbd9297f api: add new priv to priv name helper
for usage in permission check error messages, to allow easily indicating
which privs are missing.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-25 17:18:56 +02:00
d09aadee84 verify_job: fix priv check
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-25 17:18:56 +02:00
bb0fdee898 sync job: don't require privs on datastore
syncing to a namespace only requires privileges on the namespace (and
potentially its children during execution).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-25 17:18:56 +02:00
5925004a7d sync job: fix worker ID parsing
the namespace is optional, but should be captured to allow ACL checks
for unprivileged non-job-owners.

also add FIXME for other job types and workers that (might) need
updating.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-25 17:18:56 +02:00
17805f9791 api-macro: doc update
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-23 10:34:57 +02:00
b49f3554c7 bump proxmox-api-macro to 1.0.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-19 12:04:03 +02:00
9086f422af api types: verify job: fix doc comment typo
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-18 15:45:55 +02:00
680d391490 api types: verify job: allow outdated-afer == 0 for backward compat
We can have those in existing verify jobs configs, and that'd break
stuff. So, even while the "bad" commit got released only recently
with `2.1.6-1` (14 April 2022), we still need to cope with those that
used it, and using some serde parser magic to transform on read only
is hard here due to section config (json-value and verify currently
happen before we can do anything about it)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-18 15:39:59 +02:00
d1fc9d87fa Revert "verify: allow '0' days for reverification"
This reverts commit c72fe7d77c.

We could already cause the behavior by simply setting ignore-verified
to false, aas that flag is basically an on/off switch for even
considering outdated-after or not.

So avoid the extra logic and just make the gui use the previously
existing way.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-18 12:53:08 +02:00
43b4440ef0 sys: bump log dependency
to ensure format strings with "{var}" work properly.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-16 15:50:50 +02:00
6614f8840f build: bump required log version
else logging using "{var}" in format strings doesn't work properly.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-16 15:02:07 +02:00
fbbab0d8e0 build: bump required log version
else logging using "{var}" in format strings doesn't work properly.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-16 15:02:07 +02:00
988e614129 api types: namespace: fix typo in error message
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-16 09:50:17 +02:00
b724d44153 api types: BackupNamespace: fix depth check on pushing subdir to ns
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-16 09:37:38 +02:00
1d7f4ad0aa api types: BackupNamespace: remove unused, commented out code
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-16 09:37:38 +02:00
e3ad1c7f15 api: tape/backup: fix namespace/max-depth parameters
by adding the 'default' serde hint and renaming 'recursion_depth' to
'max_depth' (to be in line with sync job config)

also add the logic to actually add/update the tape backup job config

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-05-13 14:09:53 +02:00
f6950c3ca9 api: tape/restore: add namespace mapping
by adding a new parameter 'namespaces', which contains a mapping
for a namespace like this:

store=datastore,source=foo,target=bar,max-depth=2

if source or target are omitted the root namespace is used for its value

this mapping can be given several times (on the cli) or as an array (via
api) to have mappings for multiple datastores

if a specific snapshot list is given simultaneously, the given snapshots
will be restored according to this mapping, or to the source namespace
if no mapping was found.

to do this, we reutilize the restore_list_worker, but change it so that
it does not hold a lock for the duration of the restore, but fails
if the snapshot does exist at the end. also the snapshot will now
be temporarily restored into the target datastore into the
'.tmp/<media-set-uuid>' folder.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-05-13 14:08:32 +02:00
cb71f94110 tape: add namespaces mapping type
and the relevant parser for it

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-05-13 14:08:32 +02:00
6cbbb57ca7 tape: add namespaces/recursion depth to tape backup jobs
and manual api via TapeBackupJobSetup

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-05-13 14:08:31 +02:00
559017748c pbs-api-types: add parse and print ns_and_snapshot
these are helpers for the few cases where we want to print and parse
from a format that has the namespace and snapshot combined, like for
the on-tape catalog and snapshot archive.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-05-13 13:52:50 +02:00
26604f31a5 datastore: inline some format variables
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-13 12:42:41 +02:00
f3b18f7233 api types: set NS_MAX_DEPTH schema default to MAX_NAMESPACE_DEPTH
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-13 12:32:25 +02:00
ed3dd6644e api: split max-depth schema/types
into the regular one (with default == MAX) and the one used for
pull/sync, where the default is 'None' which actually means the remote
end reduces the scope of sync automatically (or, if needed,
backwards-compat mode without any remote namespaces at all).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-13 12:07:22 +02:00
79dae5df9d namespaces: move max-depth check to api type
and use it when creating a sync job, and simplify the check on updating
(only check the final, resulting config instead of each intermediate
version).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-13 12:07:22 +02:00
37c6fdafd1 pull/sync: treat unset max-depth as full recursion
to be consistent with tape backup and verification jobs.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-05-12 17:00:38 +02:00
d51475123e rest: example: fix comment width
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 11:57:51 +02:00
92984a159d cargo fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 11:54:21 +02:00
457ccc9bb3 rest server: daemon: update PID file before sending MAINPID notification
There is a race upon reload, where it can happen that:
1. systemd forks off /bin/kill -HUP $MAINPID
2. Current instance forks off new one and notifies systemd with the
   new MAINPID.
3. systemd sets new MAINPID.
4. systemd receives SIGCHLD for the kill process (which is the current
   control process for the service) and reads the PID of the old
   instance from the PID file, resetting MAINPID to the PID of the old
   instance.
5. Old instance exits.
6. systemd receives SIGCHLD for the old instance, reads the PID of the
   old instance from the PID file once more. systemd sees that the
   MAINPID matches the child PID and considers the service exited.
7. systemd receivese notification from the new PID and is confused.
   The service won't get active, because the notification wasn't
   handled.

To fix it, update the PID file before sending the MAINPID
notification, similar to what a comment in systemd's
src/core/service.c suggests:
> /* Forking services may occasionally move to a new PID.
>  * As long as they update the PID file before exiting the old
>  * PID, they're fine. */
but for our Type=notify "before sending the notification" rather than
"before exiting", because otherwise, the mix-up in 4. could still
happen (although it might not actually be problematic without the
mix-up in 6., it still seems better to avoid).

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2022-05-12 11:53:54 +02:00
51e1d3c1fd datastore: add new Lookup for operations tracking
We sometimes need to do some in-memory only stuff, e.g., to check if
GC is already running for a datastore, which is a try_lock on a mutex
that is in-memory.

Actually the whole thing would be nicer if we could guarantee to hold
the correct contract statically, e.g., like
https://docs.rust-embedded.org/book/static-guarantees/design-contracts.html

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 11:36:56 +02:00
718504f164 sync/pull: make namespace aware
Allow pulling all groups from a certain source namespace, and
possibly sub namespaces until max-depth, into a target namespace.

If any sub-namespaces get pulled, they will be mapped relatively from
the source parent namespace to the target parent namespace.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
8f21c992a7 api-types: rework BackupNamespace::map_prefix
to use slice::strip_prefix() from std

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
e941396678 verify job: support max-depth config
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
c358973e1b api: verify: support namespaces
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
77d4a752b6 api: add DatastoreWithNamespace helper struct
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
21667bed72 api-types: allow empty namespace
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
586c9f468d api: namespace: return popped component
helpful for places where namespaces need to be (re)created

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
afcf4896ba split the namespace out of BackupGroup/Dir api types
We decided to go this route because it'll most likely be
safer in the API as we need to explicitly add namespaces
support to the various API endpoints this way.

For example, 'pull' should have 2 namespaces: local and
remote, and the GroupFilter (which would otherwise contain
exactly *one* namespace parameter) needs to be applied for
both sides (to decide what to pull from the remote, and what
to *remove* locally as cleanup).

The *datastore* types still contain the namespace and have a
`.backup_ns()` getter.

Note that the datastore's `Display` implementations are no
longer safe to use as a deserializable string.

Additionally, some datastore based methods now have been
exposed via the BackupGroup/BackupDir types to avoid a
"round trip" in code.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
1e0c87d48f api: namespace management endpoints
allow to list any namespace with privileges on it and allow to create
and delete namespaces if the user has modify permissions on the parent
namespace.

Creation is only allowed if the parent NS already exists.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
47d14e1aed api: add NS_MAX_DEPTH_SCHEMA
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
 [ T: renamed from NAMESPACE_RECURSION_DEPTH_SCHEMA & moved to from
   jobs to datastore ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
cf93fbb893 api: add prefix-mapping helper to BackupNamespace
given a namespace, a source prefix and a target prefix this helper
strips the source prefix and replaces it with the target one (erroring
out if the prefix doesn't match).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
1f35bbc4dc api: derive UpdaterType for BackupNamespace
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
1b3a49c595 BackupNamespace: fix deserialize of root NS
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
e2cf8920ea api-types: fixup backup-ns being optional
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
2c593cd38c api types: BackupNamespace add pop & parent helpers
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
731d4783ce api-types: more regex fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
0deda0cacf api-types: add missing slash in optional ns path regex
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
0cd80471d0 api types: namespace: add from_parent_ns helper
will be used in the (recursive) namespace iterator

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
1682d9ae0d api types: namespace: include problematic component in error
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
0af9b69146 api-types: add namespace to BackupGroup
Make it easier by adding an helper accepting either group or
directory

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
686c4cd250 ns: max depth: set constant to upper inclusive boundary
makes usage a bit simpler, e.g., the api maximum can use that 1:1
then.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
c38e22c93c api-types: add BackupNamespace type
The idea is to have namespaces in a datastore to allow grouping and
namespacing backups from different (but similar trusted) sources,
e.g., different PVE clusters, geo sites, use-cases or company
service-branches, without separating the underlying
deduplication domain and thus blowing up data and (GC/verify)
resource usage.

To avoid namespace ID clashes with anything existing or future
usecases use a intermediate `ns` level on *each* depth.

The current implementation treats that as internal and thus hides
that fact from the API, iow., the namespace path the users passes
along or gets returned won't include the `ns` level, they do not
matter there at all.

The max-depth of 8 is chosen with the following in mind:
- assume that end-users already are in a deeper level of a hierarchy,
  most often they'll start at level one or two, as the higher ones
  are used by the seller/admin to namespace different users/groups,
  so lower than four would be very limiting for a lot of target use
  cases

- all the more, a PBS could be used as huge second level archive in a
  big company, so one could imagine a namespace structure like:
  /<state>/<intra-state-location>/<datacenter>/<company-branch>/<workload-type>/<service-type>/
  e.g.: /us/east-coast/dc12345/financial/report-storage/cassandra/
  that's six levels that one can imagine for a reasonable use-case,
  leave some room for the ones harder to imagine ;-)

- on the other hand, we do not want to allow unlimited levels as we
  have request parameter limits and deep nesting can create other
  issues as well (e.g., stack exhaustion), so doubling the minimum
  level of 4 (1st point) we got room to breath even for the
  more odd (or huge) use cases (2nd point)

- a per-level length of 32 (-1 due to separator) is enough to use
  telling names, making lives of users and admin simpler, but not
  blowing up parameter total length with the max depth of 8

- 8 * 32 = 256 which is nice buffer size

Much thanks for Wolfgang for all the great work on the type
implementation and assisting greatly with the design.

Co-authored-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Co-authored-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
f64272b948 api types: BackupType: add iter for enum
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-12 09:33:50 +02:00
7fe84f8e15 api-macro: allow overriding field attributes in the updater
This allows fixing up things such as `skip_serialize_if`
calls like so:

    #[derive(Updater)]
    struct Foo {
        #[serde(skip_serializing_if = "MyType::is_special")]
        #[updater(serde(skip_serializing_if = "Option::is_none"))]
        field: MyType,
    }

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-05-11 16:00:42 +02:00
44735fe5d6 schema: doc comment format/slight-expansion
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-05 11:03:21 +02:00
42dd95aa6f http: bump version to 0.6.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-05 10:42:52 +02:00
9c0e9dca59 tree wide update of genereated control
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-05 10:22:50 +02:00
e48568a7f1 router: bump version to 1.2.2-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-05 09:32:46 +02:00
de5d5f7618 router: format doc comment, use full text width
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-05 08:54:14 +02:00
43b5f1ae3e router: permissions: allow to pass partial-collapsed acl path components
This would allow the following components:

* all in one : &["system/network"]
* mixed: &["system/network", "dns"]
* with templates: &["datastore/{store}"]
* with the value of template being a path, e,g, with ns = "foo/bar":
  &["/datastore/{store}/{ns}"]

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-05 08:54:14 +02:00
c2049bce7f api: status: return gc-status again
Returning the GC status was dropped by mistake in commit fdcb2694
("datastore status: factor out api type DataStoreStatusListItem")

As this is considered a breaking change which we also felt, due to
the gc-status being used in the web interface for the datastore
overview list (not the dashboard), re add it.

Fixes: fdcb2694
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
 [ T: add reference to breaking commit, reword message ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-02 10:11:01 +02:00
e98ca77777 permissions: fix doc comment text width
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-28 11:45:21 +02:00
a4f552f738 api2: DataStoreListItem add maintenance info
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2022-04-27 19:21:19 +02:00
a63b50f79a api types: datastore status: reword doc comment of estimated_full_date
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-25 11:48:25 +02:00
47acc8dc8f router: drop Index impls for references
these should not be required as the use cases should all be
covered by the non-reference impls

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-21 14:04:51 +02:00
16daad64c7 bump proxmox-router to 1.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-21 13:48:57 +02:00
39956b5d09 router: fix impl Index for dyn RpcEnvironment
implement Index and IndexMut on `dyn RpcEnvironment` rather
than on a reference to it

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-21 13:45:26 +02:00
f3d07e6f15 api-types: DataStoreConfig::new for testing
so our examples can more easily access a datastore without
going over a configuration & cache

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-20 15:31:04 +02:00
cc65272130 api-types: use BackupType for GroupFilter::BackupType
instead of a string

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-20 11:49:01 +02:00
c4c67bdcfb api-types: datastore type improvements
let BackupGroup implement Hash

let BackupGroup and BackupDir be AsRef<BackupGroup>
let BackupDir be AsRef<BackupDir>

the pbs-datastore types will implement these AsRefs as well

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-20 10:03:39 +02:00
b12dc1e501 AuthId: derive Ord and PartialOrd
So the we can sort...

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-04-20 09:58:52 +02:00
027033c17a RemoteWithoutPassword: new API type
To make it explicit that we do not return the password.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-04-20 09:42:46 +02:00
47c9bed30d impl epoch_to_rfc3339_utc on wasm target 2022-04-20 09:10:47 +02:00
32ea4b56a1 api-types: introduce BackupType enum and Group/Dir api types
The type is a real enum.

All are API types and implement Display and FromStr. The
ordering is the same as it is in pbs-datastore.

Also, they are now flattened into a few structs instead of
being copied manually.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-15 13:12:46 +02:00
b635dc3ee1 rust fmt for pbs src
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-14 14:03:46 +02:00
169a91c332 bump proxmox-compression dependency to 0.1.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:37:20 +02:00
c01b08fea9 bump proxmox-compression to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:35:31 +02:00
f8fe8f59a6 compression: limit ZstdEncoder constructors to usable ones
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:31:57 +02:00
79ac8d7344 compression: don't use tokio::main in doctest
because we have no rt feature enabled

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:31:26 +02:00
99add1733c compression: style changes
use where clauses where the parameter list is short enough
to become a single line

easier to read

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:31:10 +02:00
d4a09de520 compression: fmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:08:44 +02:00
fa5373c5c0 compression: clone_from_slice -> copy_from_slice
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:07:33 +02:00
e7e4411f44 proxmox-compression: add streaming zstd encoder
similar to our DeflateEncoder, takes a Stream and implements it itself,
so that we can use it as an adapter for async api calls

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:02:54 +02:00
d107d0b2eb proxmox-compression: add async tar builder
inspired by tar::Builder, but limited to the things we need and using
AsyncRead+AsyncWrite instead of the sync variants.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 09:02:51 +02:00
ac50b068de bump proxmox-schema to 1.3.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:20:35 +02:00
71a7566cd3 bump proxmox-schema dependency to 1.3.1 for streaming attribute
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:20:27 +02:00
e734143380 bump proxmox-schema dependency to 1.3.1 for streaming attribute
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:20:27 +02:00
8291d9ed81 schema: bump api macro to 1.0.2 for the streaming attribute
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:19:00 +02:00
56dd83740d bump proxmox-router dependency to 1.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:17:08 +02:00
97c5095486 bump proxmox-router dependency to 1.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:17:08 +02:00
922d61d276 adapt to the new ApiHandler variants
namely 'StreamingSync' and 'StreamingAsync'
in rest-server by using the new formatter function,
and in the debug binary by using 'to_value'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:13:40 +02:00
cd4e485600 proxmox-rest-server: OutputFormatter: add new format_data_streaming method
that takes the data in form of a `Box<dyn SerializableReturn + Send>`
instead of a Value.

Implement it in json and extjs formatter, by starting a thread and
stream the serialized data via a `BufWriter<SenderWriter>` and use
the Receiver side as a stream for the response body.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-13 08:13:36 +02:00
50fa7bad49 datastore: add check for maintenance in lookup
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2022-04-12 15:29:14 +02:00
35786fe37e api-types: add maintenance type
+ bump proxmox-schema dep to 1.2.1 (for quoted property string)

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2022-04-12 15:29:14 +02:00
bcf6abaa0d bump proxmox-api-macro to 1.0.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:27:11 +02:00
6ce3a73681 bump proxmox-router to 1.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:26:07 +02:00
930bb59d84 proxmox-router: depend on proxmox-async 0.4.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:24:27 +02:00
61d6541ce2 router: deduplicate some code
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:23:05 +02:00
ca3b25869c proxmox-api-macro: add 'streaming' option
to generate the `Streaming` variants of the ApiHandler

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:23:05 +02:00
f585722aad proxmox-router: add new ApiHandler variants for streaming serialization
they should behave like their normal variants, but return a
`Box<dyn SerializableReturn + Send>` instead of a value. This is useful
since we do not have to generate the `Value` in-memory, but can
stream the serialization to the client.

We cannot simply use a `Box<dyn serde::Serialize>`, because that trait
is not object-safe and thus cannot be used as a trait-object.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:23:05 +02:00
2c9272945e promxox-router: add SerializableReturn Trait
this will be useful as a generic return type for api calls which
must implement Serialize.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:23:05 +02:00
27c8106d7b bump proxmox-async to 0.4.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:22:59 +02:00
9471ba9969 proxmox-async: add SenderWriter helper
this wraps around a tokio Sender for Vec<u8>, but implements a blocking
write. We can use thas as an adapter for something that only takes a
writer, and can read from it asynchonously

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-04-12 14:05:14 +02:00
9661defb0f tree wide: some stylistic clippy fixes
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-11 08:14:28 +02:00
908908191e api types: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 18:00:18 +02:00
4cdeee64dc sys: make acl constants rustfmt safe
there's not much better one can do here..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 17:39:31 +02:00
0a651e00a9 sys: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 17:39:31 +02:00
3a3dd296cc schema: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:42:09 +02:00
ae9d6e255c lang: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:41:44 +02:00
0eeb0dd17c http: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:41:21 +02:00
05cad8926b router: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:40:39 +02:00
0ec1c684ae shared memory: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:37:38 +02:00
6f8173f67a tfa: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:34:41 +02:00
4554034d32 time: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:34:04 +02:00
800cf63a8a uuid: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:34:01 +02:00
d3b387f1a7 update gitignore
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-10 12:29:03 +02:00
6b06cc6839 rest server: log rotation: refactor and comment improvements
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-07 14:04:18 +02:00
5eaab7864a rest server: log rotation: fix off-by-one for max_days
The entries in a file go from oldest end-time in the first time to
newest end-time in the last line. So, just because the first line is
older than the cut-off time, the remaining one doesn't necessarily
have to be old enough too. What we can know for sure that older than
the current checked rotations of the task archive are definitively up
for deletion.

Another possibility would be to check the last line, but as scanning
backwards is more expensive/complex to do while only being an actual
improvement in a very specific edge case (it's more likely to have a
mixed time-cutoff vs. task-log-file boundary than that those are
aligned)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-07 12:58:32 +02:00
bd8fd62de4 rest-server: add option to rotate task logs by 'max_days' instead of 'max_files'
and use it with the configurable: 'task_log_max_days' of the node config

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-04-06 17:12:49 +02:00
6c856eed5e rest-server: cleanup_old_tasks: improve error handling
by not bubbling up most errors, and continuing on. this avoids that we
stop cleaning up because e.g. one directory was missing.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-04-06 17:10:02 +02:00
d5b9d1f482 rrd: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-06 16:56:33 +02:00
2bda552b55 rest server: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-06 16:55:39 +02:00
0afe853119 bump version to 0.9.5-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-04-01 15:57:00 +02:00
4aff0d7c95 fix Open ID with Azure as provider
Azure doesn't accept `Transfer-Encoding: chunked` on their token endpoint,
but with the switch to ureq we always send requests with this set.

Fix by switching to `Content-Length` in the header instead. ureq only
sets `Transfer-Encoding: chunked` when the body length is not known
beforehand, which is the case when using `send`. See
https://docs.rs/ureq/2.4.0/ureq/index.html#content-length-and-transfer-encoding

See https://forum.proxmox.com/threads/openid-401-with-azure-ad.105892/
for the issue.

Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
2022-04-01 15:53:44 +02:00
f8c7bc4fb4 fix #3067: api: add support for multi-line comments in node.cfg
add support for multi-line comments to node.cfg and the api, similar to
how pve handles multi-line comments

Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
Acked-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-23 10:43:43 +01:00
f04eb949d1 tfa: serde tools: improve variance and dropck
`FoldSeqVisitor` doesn't actually own a `T` and therefore
cannot drop a `T`, we only use it via the `Fn(&mut Out, T)`,
so use `fn(T)` in the `PhantomData` to keep `T`
contravariant.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-22 12:31:54 +01:00
1f47f7d3eb bump version to 0.9.4-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-03-22 11:37:59 +01:00
23e6c398a2 http client: rust format and whitespace cleanup
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-03-22 10:57:21 +01:00
e0535e56ad add http proxy support
ureq has support for a HTTP proxy, but no support for HTTPS proxy yet.
ureq doesn't query `all_proxy` and `ALL_PROXY` environment variables by
itself, the way curl does. So set the proxy in code if any of the above
environment variables are set.

Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
2022-03-22 10:42:57 +01:00
24a10d107a api: datastore_status: restore api/gui compatibility
the latest changes to this api call changed/removed some things that
were actually necessary for the gui. Readd those and document them this
time.

The change from u64 to i64 limits us to 8EiB of Datastore sizes (instead if
16EiB) but if we reach that, we must adapt most other parts to use 128bit
sizes anyway

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-03-22 10:31:25 +01:00
fdcb2694b4 datastore status: factor out api type DataStoreStatusListItem
And use the rust type instead of json::Value.
2022-03-20 09:38:50 +01:00
6221d86c64 schema: add another test case
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-09 14:29:53 +01:00
d7283d5aeb schema: don't accept unterminated quoted strings
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-09 13:41:09 +01:00
1a059f3ebe regex: bump to 1.5.5
to ensure CVE fix for DoS on untrusted RE is picked up where it matters

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-03-09 09:55:36 +01:00
35da5ff9fe Username schema: set min_length to 1
Just to get a better error message (the regex already requires min_length 1)

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-03-07 13:47:06 +01:00
346e422237 cleanup: move BasicRealmInfo to pbs-api-types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-03-07 08:06:55 +01:00
cdf4326e43 schema: factor out string verify fn and improve docs
We'll need to name the type when we add more perl bindings.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-04 14:51:16 +01:00
42cafaba32 bump proxmox-schema dep to 1.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-04 09:50:21 +01:00
c01fbafae0 bump proxmox-schema dep to 1.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-04 09:50:21 +01:00
6e4a912bea bump schema to 1.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-04 09:47:40 +01:00
1e0c04a4ba drop param_bail test
it's not actually testing anything useful

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-04 09:44:51 +01:00
7c7e99dca1 proxmox-schema: add convenience macros for ParameterError
with two variants:

(expr, expr) => assumes that the second is an 'Error'
(expr, (tt)+) => passes the tt through anyhow::format_err

also added tests

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-03-04 09:43:01 +01:00
ec5ff23d70 make property_string module public
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-03-01 14:57:45 +01:00
842a39af35 datastore: add tuning option for chunk order
currently, we sort chunks by inode when verifying or backing up to tape.
we get the inode# by stat'ing each chunk, which may be more expensive
than the gains of reading the chunks in order

Since that is highly dependent on the underlying storage of the datastore,
introduce a tuning option  so that the admin can tune that behaviour
for each datastore.

The default stays the same (sorting by inode)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-02-23 09:06:03 +01:00
fca5218749 schema: add const fn unwrap_*_schema/format methods
'unwrap_' because they will panic and as `const fn` since
panic in const fn is now possible

Note that const evaluation will only be triggered when
actually used in const context, so to ensure *compile time*
checks, use something like this:

    const FOO_SCHEMA: &AllOfSchema =
        SomeType::API_SCHEMA.unwrap_all_of_schema();
    then_use(FOO_SCHEMA);

or to use the list of enum values of an enum string type
with compile time checks:

    const LIST: &'static [EnumEntry] =
        AnEnumStringType::API_SCHEMA
            .unwrap_string_schema()
            .unwrap_format()
            .unwrap_enum_format();
    for values in LIST {
        ...
    }

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-22 09:35:06 +01:00
98811ba9f1 bump proxmox-metrics to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-22 09:00:39 +01:00
77ea32cc5a metrics: bump async dep to 0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-22 08:59:37 +01:00
53aa06e411 bump proxmox-async dep to 0.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:25:37 +01:00
6fe5357ce9 bump proxmox-lang dep to 1.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:24:24 +01:00
f1681d4b83 use io_format_err, io_bail, io_err_other from proxmox-lang
and move the comment from the local io_bail in pbs-client/src/pxar/fuse.rs
to the only use

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:24:13 +01:00
13408babad depend on new 'proxmox-compression' crate
the compression utilities live there now

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:23:43 +01:00
1e75baecb0 bump proxmox-async to 0.4.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:20:13 +01:00
04b3bdb6cd proxmox-compression: update d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:17:01 +01:00
b8bf6a5c81 split out compression code into new crate 'proxmox-compression'
this removes quite a bit of dependecies of proxmox-async

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
[set proxmox-lang dep to 1.1]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 14:10:53 +01:00
d663ff328a formatting fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 13:49:48 +01:00
57d052af36 workspace: set proxmox-lang dep version to 1.1
to ensure the error macros are available

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 13:45:46 +01:00
d8ecb87358 bump proxmox-lang to 1.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 13:45:10 +01:00
fc4e02a34f lang: remove io_assert
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 13:41:59 +01:00
d4b4115400 move io error helpers to proxmox-lang
this removes proxmox_sys as a dependecy for proxmox-async

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-21 13:35:14 +01:00
46580ee3e9 bump proxmox-schema to 1.2.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-17 12:06:15 +01:00
46541feecf support quoted strings in property strings
This allows free form text to exist within property strings,
quoted, like:
    key="A value with \"quotes, also commas",key2=value2
or also:
    "the value for a default_key",key2=value2

And drop ';' as a key=value separator since those are meant
for arrays inside property strings...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-17 09:36:43 +01:00
43b602248d rrd: extract data: avoid always calculating start-time fallback
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-15 07:59:55 +01:00
5740f36ef8 rrd: avoid intermediate index, directly loop over data
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-15 07:59:55 +01:00
6149c171ca rrd cache: code style, avoid useless intermediate mutable
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-15 07:59:12 +01:00
39eac6280f schema: FromIterator lifetime fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-14 11:20:48 +01:00
c43ac0a64c schema: impl FromIterator for ParameterError
for where we also have Extend impls

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-14 09:46:20 +01:00
706d966c87 schema: bump edition to 2021
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-14 09:45:38 +01:00
fb27e132e7 rest-server: bump schema to 1.2 and use convenience methods
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 14:09:45 +01:00
ae0ee80f43 bump proxmox-schema to 1.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 14:08:30 +01:00
8a60e6c8d0 schema: rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 14:05:05 +01:00
a5ba444a5b schema: ParameterError convenience
for ease of use implement these traits for ParameterError:
    * From<(&str, Error)>
    * From<(String, Error)>
    * Extend<(String, Error)>
    * Extend<(&str, Error)>
    * IntoIterator<Item = (String, Error)>

and add the following methods:
    * fn into_inner(self) -> Vec<(String, Error)>;

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 14:01:57 +01:00
68d22d4888 proxmox-rest-server: add missing 'derive' feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 13:57:48 +01:00
70142e607d proxmox-http: drop 'mut' on specialized request methods
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 08:56:34 +01:00
86f3c90763 proxmox-tfa: fully deserialize TfaChallenge
otherwise clients cannot use this...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 08:56:34 +01:00
e5a43afe10 proxmox-tfa: make TfaChallenge members public
rust based *clients* may want/need access to it

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-11 08:56:34 +01:00
90476cf118 misc clippy fixes
the trivial ones ;)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-02-08 14:57:16 +01:00
d80d195c26 misc clippy fixes
the trivial ones ;)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-02-08 14:57:16 +01:00
bb7018e183 misc clippy fixes
the trivial ones ;)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-02-08 14:57:16 +01:00
9ba2092d1b proxmox-async: rustfmt (again)
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-02-08 14:52:42 +01:00
09d1344d61 proxmox-async: another clippy fixup
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-02-08 14:33:50 +01:00
ca563a8cfd misc clippy fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-02-08 14:28:44 +01:00
9539fbde1c proxmox-async: clippy fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-08 14:26:46 +01:00
0b90f8d802 api-macro: fix "Forgerty" typo
Signed-off-by: Stefan Sterz <s.sterz@proxmox.com>
2022-02-07 15:38:39 +01:00
5cc4ce3b4d http: websocket: code cleanup
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-04 17:16:36 +01:00
1edb52411e http: websocket: drop Text frame auto-detection from docs
was forgotten in commit 232d87531e when
dropping the bogus frame auto detection

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-04 17:15:49 +01:00
42b6f4331f http: websocket: avoid modulo for power of 2
even for the small cases this can matter

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-04 17:12:07 +01:00
c70d98c90c tfa: fix hyperlink in doc comment
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-04 17:06:02 +01:00
425b52586e http: websocket: rustfmt and small cleanups
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-04 17:05:45 +01:00
170564dd77 http: websocket: doc wording cleanups
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-04 17:04:50 +01:00
4826ff99d8 proxmox-http: websocket: fix comment about callback
this was once a callback in an early version, but it changed to a
channel, but the comment was not updated

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-02-04 16:10:31 +01:00
138f32e360 metrics: cleanup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-03 13:40:07 +01:00
645b2ae89b rest: add cookie_from_header helper
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-03 13:12:02 +01:00
48e047cefc proxmox-metrics: re-bump version for first upload
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-03 13:11:38 +01:00
037ce3a0aa check suites: add special check for Debian security repository
since the suffix was changed with Debian Bullseye.

Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-03 08:57:04 +01:00
4bdd6a5148 clippy fixes
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-03 08:55:42 +01:00
9caae7d49e upgrade to edition 2021
std::convert::{TryFrom, TryInto} are now part of the prelude.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-03 08:55:08 +01:00
3745f36ab1 metrics: use builder pattern for adding tags
Rather than going from a list of `(&str, &str)` tuples to a
`HashMap<String, String>`, add a `.tag()` builder method
and use `Cow` behind the scenes to efficiently allow the
caller to choose between a static literal and a `String`
value.

Previously the methods forced `&str` slices and then
always-copied those into `String`s even if the caller could
just *move* it.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 15:23:22 +01:00
e325f4a0d8 metrics: more doc fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 14:30:14 +01:00
c609a58086 doc fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 14:14:09 +01:00
65f05daf7e doc fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 14:13:19 +01:00
41862eeb95 bump proxmox-async to 0.3.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 13:21:55 +01:00
8bf293bfc5 proxmox-metrics: implement metrics server client code
influxdb (udp + http(s)) only for now

general architecture looks as follows:

the helper functions influxdb_http/udp start a tokio task and return
a Metrics struct, that can be used to send data and wait for the tokio
task. if the struct is dropped, the task is canceled.

so it would look like this:
  let metrics = influxdb_http(..params..)?;
  metrics.send_data(...).await?;
  metrics.send_data(...).await?;
  metrics.join?;

on join, the sending part of the channel will be dropped and thus
flushing the remaining data to the server

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
[renamed proxmox_async::io::udp -> proxmox_async::net::udp]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 12:56:21 +01:00
de891b1f76 proxmox_async: rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 12:55:01 +01:00
807a70cecc proxmox_async: move io::udp to net::udp
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 12:54:39 +01:00
9ebf24b4f8 proxmox-async: add udp::connect() helper
so that we do not have to always check the target ipaddr family manually

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-02 12:43:48 +01:00
8b15ac202e d/control: update
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-01 13:18:22 +01:00
1ab70b8c21 bump version to 0.9.3-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-01 13:03:57 +01:00
e428920a15 proxmox-sys: add FileSystemInformation struct and helper
code mostly copied from proxmox-backups 'disk_usage'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-02-01 12:26:14 +01:00
131f3d9471 proxmox-sys: make some structs serializable
we already depend on serde anyway, and this makes gathering structs a
bit more comfortable

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-02-01 12:26:14 +01:00
4cab29c57a make upload: drop buster
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-01 11:10:00 +01:00
3531729921 include error messages in error display
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-01 10:39:23 +01:00
bd2bf045cc use native-tls for ureq
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-01 10:38:57 +01:00
abc0bdd09d bump version to 0.4.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-01 10:26:20 +01:00
fe6294cd3a bump edition to 2021
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-01 10:18:32 +01:00
7622380dd7 switch from curl to ureq
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-02-01 10:18:23 +01:00
ce9a84c54f enable gzip feature for ureq
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-02-01 09:13:50 +01:00
cf18776173 cargo: enable "accept-rfc3339-timestamps" feature for OIDC
It doesn't pull in any new dependency and we require it to be able to
work with the auth0 provider.
https://github.com/ramosbugs/openidconnect-rs/pull/55#issuecomment-1026567725

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-02-01 09:03:58 +01:00
f3ddce5297 use ureq (with native-tls) instead of curl
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2022-02-01 08:16:39 +01:00
c72fe7d77c verify: allow '0' days for reverification
and let it mean that we will always reverify

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-01-27 15:31:55 +01:00
ff132e93c6 rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-01-20 10:12:02 +01:00
175648763d bump proxmox-async to 0.3.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-01-20 10:10:16 +01:00
d0a3e38006 drop RawWaker usage
this was also leaking a refcount before, this is fixed now

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-01-20 10:08:05 +01:00
4e854d32f0 ciphers: simplify API schema
these need to be checked (and are) via libssl anyway before persisting,
and newer versions might contain new ciphers/variants/... (and things
like @STRENGTH or @SECLEVEL=n were missing).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-01-14 11:02:07 +01:00
a8d5bc32ca config: add tls ciphers to NodeConfig
for TLS 1.3 and for TLS <= 1.2

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
2022-01-14 11:02:07 +01:00
1d72829310 proxmox-async: bump version to 0.3.1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-01-12 15:54:06 +01:00
d069c91e73 api-types: relax NODENAME_SCHEMA
there isn't really a concept of 'nodes' in PBS (yet) anyway - and if
there ever is, it needs to be handled by the rest-server / specific API
endpoints (like in PVE), and not by the schema.

this allows dropping proxmox-sys from pbs-api-types (and thus nix and
some other transitive deps as well).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-01-12 15:42:58 +01:00
588001cf8d api-types: move RsaPubKeyInfo to pbs-client
it's the only thing requiring openssl in pbs-api-types, and it's only
used by the client to pretty-print the 'master' key, which is
client-specific.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-01-12 15:42:58 +01:00
e4974b9891 fix #3618: proxmox-async: zip: add conditional EFS flag to zip files
this flag marks the file names as 'UTF-8' encoded if they are valid UTF-8.

By default, encoding of file names in zips are defined as code page 437,
but we save the filenames as bytes (like in linux fs).

For linux systems this would not be a problem since most tools
simply use the filenames as bytes, but for the zip utility under
windows it's important since NTFS uses UTF-16 for file names.

For filenames that are valid UTF-8, they are decoded as UTF-8 everywhere
correctly (Linux as UTF-8 bytes, Windows as correct UTF-16 sequence) and
for other filenames with a high bit set, it depends on the OS/Software
what exactly happens. Some cases below:

* Windows + Built-in/7zip: decoded as CP437
* Debian + zip: Bytes taken as-is
* Debian + 7z: interpreted as Windows1252, decoded as UTF-8

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-01-11 06:31:58 +01:00
c8e73a225a rrd: drop redundant field names
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 15:02:07 +01:00
6ad9248cf3 tree-wide: drop redundant clones
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 15:02:07 +01:00
fb3fe5561e use schema verify methods
the old, deprecated ones only forward to these anyway.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 15:02:07 +01:00
872b5f41cd tree-wide: drop redundant clones
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 15:02:07 +01:00
647a0db882 tree-wide: fix needless borrows
found and fixed via clippy

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 13:55:33 +01:00
039d3374d7 tree-wide: fix needless borrows
found and fixed via clippy

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 13:55:33 +01:00
5b19368000 tree-wide: fix needless borrows
found and fixed via clippy

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 13:55:33 +01:00
1cc9b91c4f async: track d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
121af8a06f proxmox-serde: track d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
bbc635375e shared-memory: track d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
3ee175c798 tfa: ignore uncompilable doctest
the doctest code uses non-public `fold`, up for re-evaluation if this
gets moved to proxmox-serde and made public..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
2fa269fb05 shared-memory: make tests integration tests
same as with sys/xattr, these touch files, so should use a tmpdir
provided by cargo, which requires them being integration tests.

if the tmpdir doesn't support O_TMPFILE (like overlayfs), the test is
not run (unfortunately, there is no way to indicate this via the test
result like with other test frameworks).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
540fb905c2 sys: make xattr tests integration tests
these touch files, so should use the cargo-provided tmp dir, but that is
only available to benchmarks and integration tests, not unit tests.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
e8cb382442 add missing library dependencies
without these, the generated d/control files are incomplete and builds
fail on clean systems.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
d363fb2bee switch to new schema verify methods
the deprecated ones only forward to the new ones anyway..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-30 11:51:08 +01:00
2d9fbc02ab schema: fix deprecation warnings in tests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-17 08:07:02 +01:00
b28f0d820b time: fix tests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-17 08:04:12 +01:00
8393bcb268 bump regex dep to 1.5
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:25:02 +01:00
da2e372b19 cleanup schema function calls
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:25:02 +01:00
cb32acc703 cleanup schema function calls
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:25:02 +01:00
049972844e cleanup schema function calls
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:25:02 +01:00
e207a84d93 bump proxmox-schema to 1.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:25:02 +01:00
88b56894c7 bump proxmox-schema to 1.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:25:02 +01:00
241dbcff16 schema: bump regex dep to 1.5
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:16:00 +01:00
e865ac59f3 bump proxmox-schema to 1.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 11:07:30 +01:00
28c0ede638 schema: deny unsafe_op_in_unsafe_fn
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 10:17:41 +01:00
efe492034e schema: make verification functions methods
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-16 10:16:55 +01:00
4ca47bc325 fix #3794: api types: set backup time lower limit to 1
Some users want to import historical backups but they run into the
original lower backuo-time limit one can pass. That original limit
was derived from the initial PBS development start in 2019, it was
assumed that no older backup can exist with PBS before it existing,
but imports of older backups is a legitimate thing.

I pondered using 683071200 (1991-08-25), aka the first time Linux was
publicly announced by Linus Torvalds as new limit but at the end I
did not wanted to risk that and backup software is IMO to serious for
such easter eggs, so I went for 1, to differ between the bogus 0 some
tools fallback too if there's something off with time.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-12-15 14:13:49 +01:00
fbd82c81d1 proxmox-router: fix glob-import of anyhow
will break usage of the `Result::Ok()' with anyhow 1.0.49+ as that
added a new Ok helper, so a glob-import would make that shadow the
core one.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-12-13 08:13:13 +01:00
61cd0ac2ba proxmox-sys: fix glob-import of anyhow
will break usage of the `Result::Ok()' with anyhow 1.0.49+ as that
added a new Ok helper, so a glob-import would make that shadow the
core one.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-12-13 07:46:34 +01:00
5f75b37301 schema: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:53:18 +01:00
fd39f876dc shared-memory: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:52:58 +01:00
1185458719 serde: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:52:39 +01:00
dddfa1164b tfa: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:51:22 +01:00
a774958239 io: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:51:15 +01:00
d871d6849b api-macro: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:51:09 +01:00
5e490dd7a0 uuid: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:50:27 +01:00
d851078eae shared-memory: clippy fixes (docs)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:49:23 +01:00
179515c5b2 http: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:45:52 +01:00
59986f1195 sys: another minor clippy fix
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:43:28 +01:00
b213dbb7c8 sys: formatting
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:42:53 +01:00
c280f73793 sys: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-07 11:42:53 +01:00
e888fa5181 proxmox-uuid: fix wasm32 build
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-03 09:33:02 +01:00
b39e6ac669 bump proxmox-uuid to version 1.0.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-03 09:21:17 +01:00
0dc3bcd1a5 proxmox-uuid: implement uuid on target wasm 2021-12-03 09:17:20 +01:00
165fa05290 Allow to compile on wasm32 target
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-03 08:52:54 +01:00
259e4b1441 update proxmox-time to version 1.1.2-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-02 10:38:06 +01:00
6bdf5085dc proxmox-time: calendar-events: parse 'UTC' timezone into calendarevent
like we do in pve. this breaks the (newly added) compute_next_event
method of CalendarEvent, but we can keep compatibilty with the
deprecated function

adapt the tests so that they add a ' UTC' by default
(so they can run reliably on any machine)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-02 10:32:53 +01:00
50dc2daddb fixup changelog entry
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 09:12:30 +01:00
6bb932e604 use nix::unistd::fchown
instead of re-implementing it, now that we depend on >=0.19

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 09:04:55 +01:00
1db9a5bc0e clippy: allow manual_range_contains
we use it quite often in this module, and it's more readable when split.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 09:04:55 +01:00
9c9b5c02b4 clippy: collapse match/if let/..
best viewed with `-w` ;)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 09:01:52 +01:00
e303ad8605 clippy: misc fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 09:00:52 +01:00
b1c2250000 clippy: use matches!
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 08:59:27 +01:00
a81b2672d8 clippy: remove unnecessary reference taking
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-02 08:58:10 +01:00
0a0f3906b5 proxmox-router: bump to 1.1.1
for current anyhow compat

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-12-01 13:22:26 +01:00
8995e899c8 pbs-api-types: remove proxmox-sys dependency for target wasm
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-01 09:49:52 +01:00
4dac564215 pbs-api-types: remove openssl dependency for target wasm
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-01 09:28:47 +01:00
5d570c4b59 pbs-api-types: remove libc dependency
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-01 09:10:25 +01:00
a717b13733 pbs-api-types: removbe usused nix dependency
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-01 09:08:25 +01:00
7c04f07525 remove use of deprecated functions from proxmox-time
Depend on proxmox-time 1.1.1

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-01 07:23:18 +01:00
63cecf8a69 bump proxmox-time version to 1.1.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-12-01 07:18:44 +01:00
c77ab2c7e5 proxmox-time: time-span: implement FromStr
and deprecate 'parse_time_span'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:14:33 +01:00
42420d3a5e proxmox-time: calendar_events: implement FromStr
and deprecate 'parse_calendar_event'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:14:20 +01:00
a96e9fb724 proxmox-time: calendar-events: make compute_next_event a method
and deprecated the standalone function

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:14:06 +01:00
032787a6a3 proxmox-time: lib.rs: rustfmt
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:13:45 +01:00
676146fd90 proxmox-time: move tests from time.rs to test.rs
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:13:36 +01:00
22b3388500 proxmox-time: move TimeSpan into time_span.rs
and related parsing functions

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:13:23 +01:00
6a680aac55 proxmox-time: move CalendarEvent into calendar_events.rs
and all relevant parsing functions as well

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:13:08 +01:00
83cf350f04 proxmox-time: daily_duration.rs: rustfmt
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:12:51 +01:00
78e1e8ce09 proxmox-time: move parse_daily_duration to daily_duration.rs
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 07:12:35 +01:00
f61ee1372f proxmox-time: split DateTimeValue into own file
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 06:55:51 +01:00
07cc21bd5a proxmox-time: move WeekDays into own file
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 06:55:32 +01:00
a104c8fc41 proxmox-time: move common parse functions to parse_helpers
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 06:55:04 +01:00
8e0fc66dfe proxmox-time: calendar-events: make hour optional
to be compatible with our perl calendar events, we have to make hour optional
in that case we select every hour, so 'X' is the same as writing '*:X'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 06:54:32 +01:00
8480b7b4ff proxmox-time: calendar-events: implement repeated ranges
so that we can have e.g. '7..17/2:00' as timespec

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-12-01 06:53:55 +01:00
fc80f519f4 api-macro: add #[updater(type = "...")]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-30 17:21:20 +01:00
e461be1c9f api-macro: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-30 17:02:25 +01:00
60fa521095 time: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 14:56:31 +01:00
36e064d73a io: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 14:51:26 +01:00
eac7ebfc55 sys: add back d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 11:35:39 +01:00
30e99fef5b bump proxmox-sys to 0.2.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 11:33:42 +01:00
35ecf9a551 sys: deprecate the identity macro
to be removed with the next major version bump

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 11:31:39 +01:00
9dcc229491 sys: depend on sortable-macro 0.1.2
drops the requirement for the identity macro

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 11:30:06 +01:00
3891953724 proxmox-sys: fix a warning in io_bail_last macro
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-29 11:29:31 +01:00
6679005b4f bump proxmox-tfa to 2.0.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:00:29 +01:00
d85ebbb464 tfa: clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:00:29 +01:00
637188d4ba tfa: make configured webauthn origin optional
and add a webauthn origin override parameter to all methods
accessing it

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:00:29 +01:00
508c1e7c85 tfa: let OriginUrl deref to its inner Url, add FromStr impl
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:00:29 +01:00
df3e1c53d5 tfa: add WebauthnConfig::digest method
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:00:29 +01:00
21b56f0c79 tfa: fix typo in docs
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:00:29 +01:00
248e888ae7 cleanup: avoid use anyhow::*
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-28 12:50:59 +01:00
0bb298b262 bump proxmox-sortable-macro to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-26 14:11:30 +01:00
2ce2136744 sortable-macro: drop anyhow dependency
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-26 14:10:14 +01:00
be7b330d8f sortable-macro: remove need for 'identity' macro
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-26 14:10:14 +01:00
7df207b52c fix typo in comment
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-25 13:15:35 +01:00
cdf8220676 bump proxmox-io version to 1.0.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-25 12:16:03 +01:00
10ad340322 bump proxmox-sys version to 0.2.1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-25 12:12:35 +01:00
807e474398 move pbs-tools/src/percent_encoding.rs to pbs-api-types/src/percent_encoding.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-25 11:48:52 +01:00
41d3df8950 proxmox-io: imported pbs-tools/src/sync/std_channel_writer.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-25 11:21:31 +01:00
80df41a887 proxmox-sys: import pipe() function from pbs-toos/src/io.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-25 10:34:33 +01:00
a092ef9c32 update to proxmox-sys 0.2 crate
- imported pbs-api-types/src/common_regex.rs from old proxmox crate
- use hex crate to generate/parse hex digest
- remove all reference to proxmox crate (use proxmox-sys and
  proxmox-serde instead)

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:32:27 +01:00
93625e4f87 update to proxmox-sys 0.2 crate
- imported pbs-api-types/src/common_regex.rs from old proxmox crate
- use hex crate to generate/parse hex digest
- remove all reference to proxmox crate (use proxmox-sys and
  proxmox-serde instead)

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:32:27 +01:00
14f389d563 update to proxmox-sys 0.2 crate
- imported pbs-api-types/src/common_regex.rs from old proxmox crate
- use hex crate to generate/parse hex digest
- remove all reference to proxmox crate (use proxmox-sys and
  proxmox-serde instead)

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:32:27 +01:00
bdcecd3214 update debian/control 2021-11-24 10:16:37 +01:00
a92d77bf1f bump version to 0.9.2-1, depend on proxmox-sys 0.2
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:14:54 +01:00
d357ce2070 remove proxmox crate (no longer used)
Most functionality is now in proxmox-sys. The common-regex.rs is
moved back into proxmox-backup-server, because it is only used there.
Serde code is now in new proxmox-serde crate.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
82245339b8 use new proxmox-sys 0.2.0 for all crates
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
dace74a556 bump proxmox-sys version to 0.2.0
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
194b028789 proxmox-serde: add new crate with code from proxmox/src/tools/serde.rs 2021-11-24 10:00:38 +01:00
b06807532e proxmox-sys: add stortable-macro feature and remove it from proxmox
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
ee4e56e372 proxmox-sys: moved nodename from proxmox crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
ec3965fdad proxmox-sys: fix regression tests 2021-11-24 10:00:38 +01:00
21686c99e6 proxmox-sys: fixup worker task log macros 2021-11-24 10:00:38 +01:00
6efbe4e6e8 proxmox-sys: imported proxmox/src/tools/systemd.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
ba1f59c098 proxmox-sys: imported proxmox/src/tools/email.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
0806020ebf proxmox-sys: improve dev docs
And move WorkerTaskContext to top level.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
e011964f81 proxmox-sys: imported pbs-tools/src/command.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
4c7bd0ee50 proxmox-sys: imported pbs-tools/src/acl.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
66aea897b6 proxmox-sys: imported pbs-tool/src/xattr.rs 2021-11-24 10:00:38 +01:00
d98ed51fa8 proxmox-sys: move file_get_non_comment_lines to src/fs/file.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
92caf51634 proxmox-sys: split xattr code into extra file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
d0b7e1e299 proxmox-sys: imported pbs-tools/src/fs.rs to src/fs/read_dir.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
32b69176dd proxmox-sys: imported proxmox tools/sys
And split some files into smaller parts.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-24 10:00:38 +01:00
4b1cb9f9b3 bump proxmox-tfa to 1.3.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-22 13:30:51 +01:00
54e97d35c1 fix u2f context instantiation
don't use the appid for the origin if an origin was
specified

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-22 13:25:49 +01:00
4c66ea2789 d/control and Cargo.toml bumps
* pin-utils isn't used anymore
* proxmox-sys version should also be tracked in Cargo.toml

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-22 10:56:36 +01:00
c0312f3717 bum proxmox-sys version to 0.1.2
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-22 10:35:45 +01:00
4d158ec1b3 proxmox-sys: fix test for wrong logrotate path
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-22 10:35:45 +01:00
19c29ab9b2 clipy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-22 09:07:29 +01:00
9da1062f82 add Mmap::assume_init
to convert Mmap<MaybeUninit<T>> to Mmap<T>

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-22 09:06:46 +01:00
f6799e08af Fingerprint: add new signature method
commit 8c1ec5c802 introcuded a bug by
using fp.to_string(). Replace this with fp.signature() which correctly
returns the full fingerprint instead of the short version.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-22 08:29:43 +01:00
27e98af425 set default for 'protected' flag
otherwise we cannot properly parse the api return value from older
versions, since that field does not exist there.

fixes sync from older versions without the protected feature

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-11-22 08:28:37 +01:00
2984877c5e sync-job: add rate limit
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-22 07:49:41 +01:00
9815d90136 pbs-api-types: split out type RateLimitConfig 2021-11-22 07:49:41 +01:00
f2a761f9b1 pbs-api-types: fix HumanByte::auto_scale 2021-11-21 09:13:02 +01:00
a70a8ef32e use HumanByte for traffic-control config
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-20 19:35:24 +01:00
8dbc29bf7a human byte: make proper proxmox API type
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-20 19:35:24 +01:00
8ced698304 human byte: add from string parser
Adapted from Dietmar's v3 on pbs-devel but some changes:
- reworked with a strip_suffix fn that does matching, way shorter and
  even easier to read IMO
- make b/B byte symbol fully optional, not just for base-10
- also trim trailing whitespace for SizeUnit::Byte
- simplify the FromStr impl
- adapt parser unit tests such that we actually see the failed test's
  definition line, simplifies debugging a bit

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-20 19:35:24 +01:00
52bfb8e819 human byte: add proper unit type and support base-10
The new SizeUnit type takes over the auto scaling logic and could be
used on its own too.

Switch the internal type of HumanByte from u64 to f64, this results
in a slight reduce of usable sizes we can represent (there's no
unsigned float type after all) but we support pebibyte now with quite
the precision and ebibytes should be also work out ok, and that
really should us have covered for a while..

Partially adapted by Dietmar's version, but split up and change so:
* there's no None type, for a SizeUnit that does not makes much sense
* print the unit for byte too, better consistency and one can still
  use as_u64() or as_f64() if they do not want/need the unit rendered
* left the "From usize/u64" impls intact, just convenient to have and
  avoids all over the tree changes to adapt to loosing that
* move auto-scaling into SizeUnit, good fit there and I could see
  some re-use potential for non-human-byte users in the future
* impl Display for SizeUnit instead of the separate unit_str method,
  better usability as it can be used directly in format (with zero
  alloc/copy) and saw no real reason of not having that this way
* switch the place where we auto-scale in HumanByte's to the new_X
  helpers which allows for slightly reduced code usage and simplify
  implementation where possible
* use rounding for the precision limit algorithm. This is a stupid
  problem as in practices there are cases for requiring every variant:
  - flooring would be good for limits, better less than to much
  - ceiling would be good for file sizes, to less can mean ENOSPACE
    and user getting angry if their working value is messed with
  - rounding can be good for rendering benchmark, closer to reality
    and no real impact
  So going always for rounding is really not the best solution..

Some of those changes where naturally opinionated, if there's a good
practical reason we can switch back (or to something completely
different).

The single thing I kept and am not _that_ happy with is being able to
have fractional bytes (1.1 B or even 0.01 B), which just does not
makes much sense as most of those values cannot exist at all in
reality - I say most as multiple of 1/8 Byte can exists, those are
bits.o

Note, the precission also changed from fixed 2 to max 3 (trailing
zeros stripped), while that can be nice we should see if we get
a better precision limiting algorithm, e.g., directly in the printer.
Rust sadly does not supports "limit to precision of 3 but avoid
trailing zeros" so we'd need to adapt their Grisu based algorithm our
own - way to much complexity for this though..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-20 19:35:24 +01:00
adcf38948e move HumanByte to pbs-abi-types crate
Originally-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-20 19:35:24 +01:00
15819cdcfc depend on proxmox-async 0.2 2021-11-20 17:14:02 +01:00
ffbf58cad3 bump proxmox-async version to 0.2.0 2021-11-20 17:07:52 +01:00
cab125297b proxmox-async: improve dev docs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-20 17:07:52 +01:00
5bd54b4d9b proxmox-async: move AsyncChannelWriter to src/io
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-20 17:07:52 +01:00
7b7247fa80 proxmox-async: move TokioWriterAdapter to blocking
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-20 16:38:36 +01:00
6c4982bd7c proxmox-async: remove duplicate src/stream/wrapped_reader_stream.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-20 16:15:05 +01:00
781b5161bd proxmox-async: split stream.rs into separate files
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-20 16:12:55 +01:00
fa2032c7aa proxmox-async: split blocking.rs into separate files 2021-11-20 15:58:04 +01:00
4a07f14565 proxmox-rest-server: remove pbs-tools dependency 2021-11-19 18:06:54 +01:00
66b1f90f97 use new proxmox-async crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 18:03:22 +01:00
4413002f22 proxmox-async: add copyright file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:43:19 +01:00
b63229bf1d proxmox-async: imported pbs-tools/src/zip.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:42:11 +01:00
64dca3c869 proxmox-async: imported pbs-tools/src/compression.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:42:11 +01:00
112b239d50 proxmox-async: imported pbs-tools/src/tokio/tokio_writer_adapter.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:42:11 +01:00
743f7df2a5 proxmox-async: imported pbs-tools/src/stream.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:42:11 +01:00
a2d62a2555 proxmox-async: imported pbs-tools/src/broadcast_future.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:42:11 +01:00
e1f0eb4aec proxmox-async: start new crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 16:42:11 +01:00
45645d9aee proxmox-sys: add copyright file
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 15:48:18 +01:00
c08d4a173d tfa: remove unnecessary bound attribute
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-19 12:45:23 +01:00
5ecc7724e2 sys: update d/control
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 12:12:01 +01:00
f3872d0a69 bump proxmox-tfa to 1.3.1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 12:05:48 +01:00
91932da15c tfa: bump webauthn-rs to 0.3
switch WebauthnConfig to use Url for the origin field, via a wrapper
type to make Updater and ApiType happy.

the two new Credential fields `verified` and `registration_policy` are
always set to `false` and `Discouraged`, to get the same behaviour as
before.

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 12:05:48 +01:00
148950fd17 tfa: properly wrap webauthn credentials
this (external) struct gets new fields in webauthn-rs 0.3, so let's
properly wrap / convert it instead of just aliasing, else deserializing
will fail.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 12:05:48 +01:00
d49e6a362e bump proxmox-sys to 0.1.1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 12:05:48 +01:00
aedc5197f5 sys: add missing file
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 12:05:48 +01:00
6b2d0e7427 bump proxmox-http to 0.5.6
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 11:46:21 +01:00
57d31a8683 bump proxmox to 0.15.4
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 11:46:21 +01:00
dc14d03171 all crates: bump base64 dep to 0.13
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-19 11:46:21 +01:00
ef69d1aeb9 use new proxmox-sys crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 11:06:35 +01:00
20661f014d proxmox-sys: imported pbs-tools/src/task.rs (as worker_task_context.rs)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 10:05:06 +01:00
202641757e proxmox-sys: imported pbs-tools/src/logrotate.rs (with various cleanups)
- new: CreateOption s instead of owner string
- new: returns Result instead of Option
- new: add max_files  option
- remove new_with_user()

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 09:28:59 +01:00
8ac4c949bb proxmox-sys: imported pbs-tools/src/process_locker.rs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 09:28:52 +01:00
840154f61b proxmox-sys: add new crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-19 09:28:32 +01:00
246ce4e801 bump proxmox version to 0.15.3-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-18 16:54:44 +01:00
0fdc15a3f8 use proxmox::tools::fd::fd_change_cloexec from proxmox 0.15.3
Depend on proxmox 0.15.3

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-18 13:43:41 +01:00
e33f41c72c use proxmox::tools::fd::fd_change_cloexec from proxmox 0.15.3
Depend on proxmox 0.15.3

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-18 13:43:41 +01:00
a24b72c4de use proxmox::tools::fd::fd_change_cloexec from proxmox 0.15.3
Depend on proxmox 0.15.3

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-18 13:43:41 +01:00
9f4c20f3d2 proxmox: add fd_change_cloexec 2021-11-18 13:06:34 +01:00
d6e7e2599f bump version to 0.9.1-1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-18 12:56:21 +01:00
fb547f5935 bump version to 0.3.2
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-18 12:56:16 +01:00
a3592355a1 bump openidconnect dep to 2.1
for updated rand/base64 support

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-18 12:53:37 +01:00
cbc90fe9fc bump base64 dep to 0.13
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-18 12:48:40 +01:00
799df8f345 openid: allow to configure scopes, prompt, ACRs and arbitrary username-claim values
- no longer set prompt to 'login' (makes auto-login possible)
- new prompt configuration
- allow arbitrary username-claim values

Depend on proxmox-openid 0.9.0.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-18 11:20:55 +01:00
ac8709e97b group filter: rename CLI/API/Config "groups" option to "group-filter"
we even use that for basically all the related schema names, "groups"
allone is just rather not so telling, i.e., "groups" what?

While due to the additive nature of `group-filter` is not the best
possible name for passing multiple arguments on the CLI (the web-ui
can present this more UX-friendly anyway) due to possible confusion
about if the filter act like AND vs OR it can be documented and even
if a user is confused they still are safe on more being synced than
less. Also, the original param name wasn't really _that_ better in
that regards

Dietmar also suggested to use singular for the CLI option, while
there can be more they're passed over repeating the option, each with
a single filter.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-18 10:36:57 +01:00
f2b4af0322 tape backup jobs: add group filters to config/api
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-18 10:36:57 +01:00
c055cdb910 fix #sync.cfg/pull: don't remove by default
and convert existing (manually created/edited) jobs to the previous
default value of 'true'. the GUI has always set this value and defaults
to 'false'.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-18 10:36:57 +01:00
aea616987b sync: add group filtering
like for manual pulls, but persisted in the sync job config and visible
in the relevant GUI parts.

GUI is read-only for now (and defaults to no filtering on creation), as
this is a rather advanced feature that requires a complex GUI to be
user-friendly (regex-freeform, type-combobox, remote group scanning +
selector with additional freeform input).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-18 10:36:57 +01:00
506c106a50 api: add GroupFilter(List) type
at the API level, this is a simple (wrapped) Vec of Strings with a
verifier function. all users should use the provided helper to get the
actual GroupFilter enum values, which can't be directly used in the API
schema because of restrictions of the api macro.

validation of the schema + parsing into the proper type uses the same fn
intentionally to avoid running out of sync, even if it means compiling
the REs twice.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-18 10:36:57 +01:00
632ab24359 api-types: add schema for backup group
the regex was already there, and we need a simple type/schema for
passing in multiple groups as Vec/Array via the API.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-18 10:36:57 +01:00
06f5106145 bump version to 0.3.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-18 09:50:03 +01:00
467d567545 clippy and formatting fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-18 09:46:01 +01:00
ba2da9d125 client: add support for proxies
by storing the proxy url as string in the struct and setting it on
each invocation of `execute`, since execute calls reset on the
curl::easy::Easy object.

Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-18 09:40:31 +01:00
ea637eec28 bump version to 0.9.0-1 2021-11-18 09:38:47 +01:00
6454a54704 allow to configure acr values 2021-11-18 09:33:59 +01:00
897c5c7569 allow to configure prompt behaviour
And do not set it by default.
2021-11-18 09:33:11 +01:00
f53d242cb0 new helper verify_authorization_code_simple()
Simply return data as serde_json::Value.
2021-11-18 09:32:20 +01:00
cfecbee92c also return data from UserInfo endpoint 2021-11-18 09:31:44 +01:00
5937e44062 allow to configure used scopes 2021-11-18 09:23:24 +01:00
4d7cb99f4a proxmox-systemd: remove crate, use new proxmox-time 1.1.0 instead
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-17 13:07:51 +01:00
3a378a34bb bump proxmox-time version to 1.1.0 2021-11-17 13:03:36 +01:00
6871232791 proxmox-time: remove custom error type
None of the functions we call returns a resonable error number anyways.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-17 13:03:36 +01:00
4a5dbd2129 proxmox-time: added time related fuctions from proxmox-systemd crate.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-17 13:03:36 +01:00
5b2e8b4c66 Revert "lang: get offsetof const fn ready"
This reverts commit 8f89f9ad60.

generates broken code in release builds on current rustc
1.55
2021-11-17 10:07:08 +01:00
bbdfd8ede9 bump proxmox-tfa to 1.3.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-17 09:30:27 +01:00
313d0a6b88 proxmox-tfa: import tfa api from proxmox-perl-rs as api feature
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-17 08:39:56 +01:00
8c1ec5c802 move fingerprint helpers from pbs-tools to pbs-api-types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-17 07:07:40 +01:00
41d0cef377 bump proxmox-shared-memory to version 0.1.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 11:32:16 +01:00
ece92bde29 proxmox-shared-memory: depend on libc 0.2.107
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 11:29:53 +01:00
e3a14098f7 bump proxmox-http to 0.5.5-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 11:05:55 +01:00
81e959548b proxmox-http: impl RateLimiterVec
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 11:01:03 +01:00
c5d396cdb9 bump proxmox-shared-memory to version 0.1.0-2
And add missing debian files.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 07:29:13 +01:00
42002bb5c9 proxmox-shared-memory: avoid compiler warnings
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 07:29:13 +01:00
a1728a72c8 proxmox-shared-memory: remove debug println
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-15 07:14:49 +01:00
b8724f4952 bump proxmox-http version to 0.5.4-1 2021-11-14 08:19:15 +01:00
b9a1d62e47 proxmox-http: RateLimit - remove average_rate
Instead, add a method to return overall traffic.
2021-11-14 08:15:42 +01:00
100848de10 bump proxmox-http version to 0.5.3-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-13 17:38:41 +01:00
13276cc619 proxmox-http: use repr(C) for RateLimiter
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-13 17:38:18 +01:00
8734d0c2f9 proxmox-http: use SharedRateLimit trait object for RateLimitedStream
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-13 17:38:10 +01:00
937d1a6095 proxmox-http: define a RateLimit trait
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-13 17:38:00 +01:00
564703b195 proxmox-shared-memory: improve regression tests
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-12 18:20:44 +01:00
09dc3a4abc proxmox-shared-memory: create debian package
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-12 18:20:23 +01:00
0ef72957a6 proxmox-shared-memory: implement helper to init subtypes
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-12 18:20:06 +01:00
c8251d4d24 proxmox-shared-memory: add magic number test
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-12 18:19:49 +01:00
9828acd2ef proxmox-shared-memory: shared memory and shared mutex implementation 2021-11-12 18:19:36 +01:00
956e7041fe bump proxmox version to 0.15.2-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-12 17:39:38 +01:00
c4cff1278f rest: make successful-ticket auth log a debug one to avoid syslog
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-12 11:10:12 +01:00
8bcd8e5357 impl proxmox::tools::fs::CreateOptions::apply_to()
Split out code to apply CreateOptions.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-12 09:24:27 +01:00
28e1d4c342 bump proxmox-http version to 0.5.2-1 2021-11-12 09:24:17 +01:00
2b62255aca Add traffic control configuration config with API
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-10 10:15:40 +01:00
4b3e0e331c implement Servive for RateLimitedStream
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-10 10:15:40 +01:00
745c4f37dd bump proxmox-schema version to 1.0.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-10 09:58:32 +01:00
2f221df863 RateLimiter: add update_rate method 2021-11-10 09:51:08 +01:00
0c27d5da17 RateLimitedStream: implement peer_addr 2021-11-10 09:51:08 +01:00
e0a9982dd1 RateLimiter: avoid panic in time computations 2021-11-10 09:51:08 +01:00
e0305f724b RateLimitedStream: allow periodic limiter updates 2021-11-10 09:51:08 +01:00
00ca0b7fae HttpsConnector: use RateLimitedStream
So that we can limit used bandwidth.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-10 09:51:08 +01:00
ded24b3f4c RateLimitedStream: implement poll_write_vectored
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-10 09:51:08 +01:00
c94ad247b1 Implement a rate limiting stream (AsyncRead, AsyncWrite)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-11-10 09:51:08 +01:00
e848148f5c websocket: adapt for client connection
previously, this was only used for the server side handling of web
sockets. by making the mask part of the WebSocket struct and making some
of the fns associated, we can re-use this for client-side connections
such as in proxmox-websocket-tunnel.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-09 15:45:56 +01:00
e0df53e793 bump proxmox-tfa to 1.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-09 13:27:59 +01:00
0156b3fe03 proxmox-tfa: add version field to u2f::AuthChallenge
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-09 13:27:59 +01:00
83934e59e6 proxmox-tfa: make u2f::AuthChallenge Clone + Debug
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-09 13:27:59 +01:00
4128c5fdb5 updater: impl UpdaterType for Vec
by replacing the whole Vec.

if we ever want to support adding/removing/modifying elements of a Vec
via the Updater, we'd need to extend it anyway (or use a custom
updater).

Suggested-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-09 09:18:08 +01:00
bc38ff7878 bump proxmox-tfa to 1.1.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-29 15:07:09 +02:00
1554465d45 proxmox-tfa: add Totp::digits
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-29 14:31:39 +02:00
8e344d3d67 rrd: use saturating_sub to avoid underflow
Without this, the tests fail in debug mode.
Also having start (u64) underflow to a value greater than end does
not really make sense

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-28 12:54:54 +02:00
ee72e63fb9 add protected info of snapshots to api and task logs
adds the info that a snapshot is protected to:
* snapshot list
* manual pruning (also dry-run)
* prune jobs

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-28 11:30:51 +02:00
cb89d97df1 bump version to 0.3.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-21 13:14:10 +02:00
4c5d899c3a directory: make meta object optional
some custom ACME endpoints do not have a TOS, and thus do not return
a meta property at all

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-21 13:09:42 +02:00
4f4fa80f2f bump proxmox to 0.15.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-21 12:44:12 +02:00
bdc7e9d145 proxmox: cleanup files on fsync errors
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-21 12:42:23 +02:00
a0cfb9c20d bump proxmox-http version to 0.5.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:37:30 +02:00
e9bea7b7ed use new fsync parameter to replace_file and atomic_open_or_create
Depend on proxmox 0.15.0 and proxmox-openid 0.8.1

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:28:32 +02:00
75ca726c29 use new fsync parameter to replace_file and atomic_open_or_create
Depend on proxmox 0.15.0 and proxmox-openid 0.8.1

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:28:32 +02:00
d154224307 use new fsync parameter to replace_file and atomic_open_or_create
Depend on proxmox 0.15.0 and proxmox-openid 0.8.1

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:28:32 +02:00
8ae297c8d2 proxmox: bump d/control
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:27:43 +02:00
8471451a7b bump version to 0.8.1-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:15:11 +02:00
6aa28f0a08 add fsync parameter to replace_file
Depend on proxmox 0.15.0

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:12:27 +02:00
bb089965c9 bump proxmox vertsion to 0.15.0-1
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:08:34 +02:00
b960bc3a4a add fsync parameter to replace_file and atomic_open_or_create
The fsync is required for consistency after power failure, so it should
be set when writing config files or otherwise important data.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-21 07:06:04 +02:00
b87aa76b64 rest-server: use hashmap for parameter errors
our ui expects a map here with 'field: "error"'. This way it can mark
the relevant field as invalid and correctly shows the complete error
message

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-10-21 06:32:23 +02:00
ffd1d5f378 uuid: bump d/control
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-20 12:50:26 +02:00
1f03763c3b uuid: fixup debcargo.toml to include uuid-dev dependency
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-20 12:49:40 +02:00
412712029c proxmox-rrd: use fsync instead of syncfs
syncfs can sync unrelated data, and we do not want that.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-20 11:46:59 +02:00
a7ee3455da proxmox-rrd: fix regression tests 2021-10-19 18:41:03 +02:00
86b50e18ed proxmox-rrd: improve dev docs
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-19 11:17:09 +02:00
ed6a7f52e5 proxmox-rrd: cleanup - impl FromStr for JournalEntry
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-19 11:17:09 +02:00
75bb60e7b3 proxmox-rrd: add option to avoid page cache for load/save
use fadvice(.., POSIX_FADV_DONTNEED) for RRD files. We read those files only once,
and always rewrite them.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-19 11:17:09 +02:00
336e8f3e7f proxmox-rrd: use syncfs after writing rrd files
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
2021-10-19 11:17:09 +02:00
77c2e4668b proxmox-rrd: use fine grained locking in commit_journal_impl
Aquire the rrd_map lock for each file (else we block access for a long time)
2021-10-18 14:55:47 +02:00
8398620669 tfa: u2f: bytes_as_base64{,url} weren't meant to be public
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-18 14:31:04 +02:00
cc0bb59788 proxmox-rrd: log all errors from apply_and_commit_journal_thread (but only once) 2021-10-18 11:57:19 +02:00
e23f3ec774 proxmox-rrd: cleanup list_old_journals 2021-10-18 10:00:58 +02:00
a74384f725 proxmox-rrd: cleanup - use struct instead of tuple 2021-10-16 12:45:03 +02:00
4393b93a8b proxmox-rrd: move RRDMap into extra file 2021-10-16 12:45:03 +02:00
9dcc64b71a proxmox-rrd: move JournalState into extra file 2021-10-16 12:45:03 +02:00
30b4800f4f proxmox-rrd: implement non blocking journal
Do not block while applying the journal.
2021-10-16 12:45:03 +02:00
2c24c1dd22 proxmox-rrd: rename RRDCacheState to JournalState 2021-10-15 09:35:44 +02:00
2be07c2250 proxmox-rrd: avoild blocking readers while applying the journal
By using and extra RwLock<RRDMap> on the rrd data.
2021-10-15 09:22:07 +02:00
3275f1ac16 proxmox-rrd: log journal apply/flush times, split apply and flush
We need to apply the journal only once.
2021-10-15 07:16:41 +02:00
ec5d84f4d3 proxmox-rrd: cleanup - use slot_end_time() 2021-10-14 16:29:00 +02:00
2b9fb32de1 proxmox-rrd: cleanup - use staturating_add instead of if/else 2021-10-14 16:10:55 +02:00
26bd6a4f77 proxmox-rrd: improve dev docs 2021-10-14 11:53:54 +02:00
8619b21e4d proxmox-rrd: make rrd load callback configurable 2021-10-14 11:41:26 +02:00
52d5f340f2 proxmox-rrd: add more regression tests 2021-10-14 10:55:12 +02:00
f2e1ab2d44 proxmox-rrd: add regression tests and two minor fixes 2021-10-14 10:17:07 +02:00
a1eede6918 proxmox-rrd: pass time and value to update function 2021-10-14 08:12:56 +02:00
4231e2d5f5 proxmox-rrd: add some integration tests (file format tests) 2021-10-13 18:21:23 +02:00
0cf4129204 use complete_file_name from proxmox-router 1.1 2021-10-13 14:10:02 +02:00
432374d024 use complete_file_name from proxmox-router 1.1 2021-10-13 14:10:02 +02:00
6e30f3433f remove proxmox-rrd-api-types crate, s/RRDTimeFrameResolution/RRDTimeFrame/
Because the types used inside the RRD have other requirements
than the API types:

- other serialization format
- the API may not support all RRD features

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
919ccf713a proxmox-rrd: move unshipped cli tool to examples
it's a rather low-level tool mostly useful for debugging and some of
it is rather "dumb" (by design) anyway, e.g., it does not
transparently applies journal but really only operates on the DB
files as is (which can conflict with daemon operations).

In summary, not (yet) a tool meant for end user consumption.
Move it to examples folder to avoid compilation on packaging (we do
not ship it anyway) which allows us to move the rather expensive
proxmox-router (pulls in hyper) to the dev-dependencies section.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
392d646f7b proxmox-rrd: add more commands to the rrd cli tool
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
7edea7e08c proxmox-rrd: rename last_counter to last_value
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
66dfd1f08f proxmox-rrd: protect against negative update time
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
507f19dd33 proxmox-rrd: new helpers: slot, slot_start_time & slot_end_time
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
334eb9ce48 proxmox-rrd: avoid expensive modulo (%) inside loop
Modulo is very slow, so we try to avoid it inside loops.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
4cd28918e2 proxmox-rrd: add binary to create/manage rrd files
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
2c72c6a7ba proxmox-rrd: split out load_rrd (cleanup)
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
e928c24948 proxmox-rrd: support CF::Last
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
4bf2db8666 remove proxmox-rrd-api-types crate, s/RRDTimeFrameResolution/RRDTimeFrame/
Because the types used inside the RRD have other requirements
than the API types:

- other serialization format
- the API may not support all RRD features

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
ab567561b5 proxmox-rrd: extract_data: include values from current slot
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
cf097c5a89 proxmox-rrd: remove dependency to proxmox-rrd-api-types
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
bc68dee171 proxmox-rrd: implement new CBOR based format
Storing much more data points now got get better graphs.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
0355554905 proxmox-rrd: use a journal to reduce amount of bytes written
Append pending changes in a simple text based format that allows for
lockless appends as long as we stay below 4 KiB data per write.

Apply the journal every 30 minutes and on daemon startup.

Note that we do not ensure that the journal is synced, this is a
perfomance optimization we can make as the kernel defaults to
writeback in-flight data every 30s (sysctl vm/dirty_expire_centisecs)
anyway, so we lose at max half a minute of data on a crash, here one
should have in mind that we normally expose 1 minute as finest
granularity anyway, so not really much lost.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-13 13:36:02 +02:00
48ef839043 bump proxmox-router version to 1.1.0-1 2021-10-13 12:28:16 +02:00
417b7159d2 add filename completions helper (moved from pbs-tools)
Depend on 'nix' now.
2021-10-13 12:28:16 +02:00
17adc570db bump proxmox-borrow to 1.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-13 10:41:01 +02:00
087bf31567 borrow: update to ManuallyDrop::take
and fixup into_boxed_inner along the way

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-13 10:39:12 +02:00
d18292192d bump proxmox-api-macro to 1.0.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-12 14:51:35 +02:00
5988a1adf1 drop automatically_derived attribute for now
new rustc seems to *sometimes* complain about it

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-12 14:49:11 +02:00
f63ce12b66 lang: prepare c_str for const fns with 1.56
provides an api compatible const-fn-compatible c_str
alternative working on 1.56

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-12 14:29:03 +02:00
8f89f9ad60 lang: get offsetof const fn ready
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-12 14:14:25 +02:00
af33a97547 lang: deprecate ops::ControlFlow
as we now have rustc 1.55

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-12 14:13:59 +02:00
d65b2df750 update to proxmox split and bump version to 0.8.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 12:37:13 +02:00
4ccd6256a8 update proxmox-http to 0.5 for the split
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 12:34:14 +02:00
336dab0177 update proxmox crate to the current split
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 12:34:14 +02:00
c7b17de1b5 update to proxmox split and bump version to 0.8.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 12:00:19 +02:00
09046671ed update to first proxmox crate split
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 11:58:49 +02:00
d18f79dd4f update to first proxmox crate split
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 11:58:49 +02:00
e0ce41b03a update to first proxmox crate split
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 11:58:49 +02:00
1d24555b28 drop u2f-api file
used to be used by examples at some point

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 11:39:59 +02:00
f35dbbd651 add proxmox-section-config crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 11:39:59 +02:00
41f3fdfeb9 add proxmox-schema and proxmox-router crates
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 11:39:59 +02:00
01a8b6f1bf add proxmox-io crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-11 10:07:53 +02:00
91f59f9f59 add proxmox-lang crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 15:22:17 +02:00
9b6fe4aceb add proxmox-time crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 15:22:17 +02:00
7db0a3c6df add proxmox-borrow crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 15:22:17 +02:00
bd67ccc1b3 add proxmox-uuid crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 15:22:17 +02:00
77dc52c047 add proxmox-tfa crate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 15:22:17 +02:00
2859858f59 fix systemd::escape_unit's hex encoding
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 15:22:17 +02:00
6ad1bcaf89 bump proxmox dependency to 0.14.0 and proxmox-http to 0.5.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 11:18:22 +02:00
1aaac3f173 bump proxmox dependency to 0.14.0 and proxmox-http to 0.5.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 11:18:22 +02:00
fa9757e67f bump proxmox dependency to 0.14.0 and proxmox-http to 0.5.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-08 11:18:22 +02:00
fb6823b54b rest-server: add cleanup_old_tasks
this is a helper that removes task log files that are not referenced
by the task archive anymore

it gets the oldest task archive file, gets the first endtime (the
oldest) and removes all files in the taskdir where the mtime is older
than that

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-10-08 06:38:52 +02:00
071cb7aa8b proxmox-rrd: use correct directory options in create_rrdb_dir 2021-10-07 08:50:50 +02:00
9c64c09c92 proxmox-rrd: cleanup error handling 2021-10-07 08:01:12 +02:00
5165bed8c2 proxmox-rrd: use log crate instead of eprintln, avoid duplicate logs 2021-10-06 18:19:22 +02:00
9c7fd3c936 proxmox-rrd: fix update (do not update) when time is in the past 2021-10-06 18:01:48 +02:00
881d8f85ea proxmox-rrd: improve developer docs 2021-10-06 12:19:54 +02:00
54f7a80f97 proxmox-rrd: remove serde dependency 2021-10-06 10:55:46 +02:00
b89c56b96e start checklist for adding crates in README.rst
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-06 10:37:17 +02:00
5b3283c5d4 split out RRD api types into proxmox-rrd-api-types crate 2021-10-06 09:49:51 +02:00
538e6f66f3 split out RRD api types into proxmox-rrd-api-types crate 2021-10-06 09:49:51 +02:00
ac17698e4a proxmox-rrd: use create_path instead of std::fs::create_dir_all
To ensure correct file ownership.
2021-10-06 08:37:14 +02:00
9871af7ece move RRD code into proxmox-rrd crate 2021-10-06 08:13:28 +02:00
8d1a9d2ec6 move RRD code into proxmox-rrd crate 2021-10-06 08:13:28 +02:00
3ffe2ebc64 proxmox-rest-server: use new ServerAdapter trait instead of callbacks
Async callbacks are a PITA, so we now pass a single trait object which
implements check_auth and get_index.
2021-10-05 11:13:10 +02:00
2c09017045 proxmox-rest-server: pass owned RestEnvironment to get_index
This way we avoid pointers with lifetimes.
2021-10-05 11:12:53 +02:00
591a32ecd4 proxmox-rest-server: cleanup, access api_auth using a method 2021-10-05 11:12:53 +02:00
f189895cef fix deprecated use of std::u16 module
integer primitive type modules are deprecated, use
associated constants instead

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-04 15:03:50 +02:00
a5298b2a10 fix deprecated use of std::u64/... modules
integer primitive type modules are deprecated, use
associated constants instead

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-10-04 15:02:30 +02:00
4348c807f7 rest: daemon: group systemd FFI together
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-02 11:45:34 +02:00
62b226e9c4 rest: daemon: sd notify: code cleanup
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-02 11:45:34 +02:00
7fac98519c rest: daemon: sd notify barrier: avoid barging in between SystemdNotify enum and systemd_notify
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-02 11:45:34 +02:00
83f15413fd rest: daemon: sd notify barrier: allow caller to set timeout
else it's rather to subtle and not a nice interface considering that
we only want to have a thin wrapper for sd_notify_barrier..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-02 11:44:20 +02:00
947f4c78a7 rest: daemon: comment why using a systemd barrier is important for main PID handover
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-02 11:44:20 +02:00
5027727fc5 rest-server/daemon: use sd_notify_barrier for service reloading
until now, we manually polled the systemd service state during a reload
so that the sd_notify messages get processed in the correct order
(RELOAD(old) -> MAINPID(old) -> READY(new))

with systemd >= 246 there is now 'sd_notify_barrier' which
blocks until systemd processed all prior messages

with that change, the daemon does not need to know the service name anymore

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-10-02 11:44:20 +02:00
89766c4f95 proxmox-rest-server: make get_index async 2021-10-01 09:38:10 +02:00
58a6e5f512 proxmox-rest-server: add comment why ApiService needs to be 'pub' 2021-10-01 08:35:51 +02:00
2b023101f7 proxmox-rest-server: make check_auth async 2021-10-01 07:53:59 +02:00
a6c0ec35a3 proxmox-rest-server: fix spelling errors 2021-10-01 06:43:30 +02:00
be98d3156d proxmox-rest-server: improve ApiService docs 2021-09-30 17:18:47 +02:00
58eba821e6 proxmox-rest-server: start module docs 2021-09-30 13:49:29 +02:00
ad449a5780 rename CommandoSocket to CommandSocket 2021-09-30 12:52:35 +02:00
249aae1f05 drop fd_change_cloexec from proxmox-rest-server
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-09-30 12:43:22 +02:00
6d4e47fb09 proxmox-rest-server: improve docs
And rename enable_file_log to enable_access_log.
2021-09-30 12:29:15 +02:00
9cb2c97c77 proxmox-rest-server: improve docs
And renames abort_worker_async to abort_worker_nowait (avoid confusion,
because the function itself is not async).
2021-09-30 10:51:41 +02:00
50c62be82c proxmox-rest-server: cleanup FileLogger docs 2021-09-30 10:51:31 +02:00
f23aeff910 cleanup: move use clause to top 2021-09-30 08:42:37 +02:00
2ed2c0334c proxmox-rest-server: allow to catch SIGINT and SIGHUP separately
And make ServerState private.
2021-09-30 08:41:30 +02:00
93802ec2ef proxmox-rtest-server: make Reloader and Reloadable private 2021-09-30 07:44:19 +02:00
abfac6738c proxmox-rest-server: improve logging
And rename server_state_init() into catch_shutdown_and_reload_signals().
2021-09-29 14:48:46 +02:00
5b72478077 proxmox-rest-server: avoid useless call to request_shutdown
Also avoid unsafe code.
2021-09-29 14:37:07 +02:00
aedc1db9e2 daemon: simlify code (make it easier to use) 2021-09-29 12:04:48 +02:00
a8c75df695 cleanup: make BoxedStoreFunc private
There is no need to export this type.
2021-09-29 09:55:43 +02:00
15dcfbf162 examples: add example for a simple rest server with a small api
show how to generally start a daemon that serves a rest api + index page

api calls are (prefixed with either /api2/json or /api2/extjs):
/		GET	listing
/ping		GET	returns "pong"
/items		GET	lists existing items
		POST	lets user create new items
/items/{id}	GET	returns the content of a single item
		PUT	updates an item
		DELETE	deletes an item

Contains a small dummy user/authinfo

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-29 09:48:47 +02:00
2ce3e5fb78 rest-server: use hypers AddrIncoming for proxmox-backup-api
this has a 'from_listener' (tokio::net::TcpListener) since hyper 0.14.5 in
the 'tcp' feature (we use 'full', which includes that; since 0.14.13
it is not behind a feature flag anymore).

this makes it possible to create a hyper server without our
'StaticIncoming' wrapper and thus makes it unnecessary.

The only other thing we have to do is to change the Service impl from
tokio::net::TcpStream to hyper::server::conn::AddStream to fulfill the trait
requirements.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-29 09:38:40 +02:00
5bff2a1d4b add test for property string verification errors 2021-09-29 08:59:28 +02:00
d62d1e5707 move api schema tests into separate file 2021-09-29 08:25:23 +02:00
8e1d573844 add tests for schema verification errors 2021-09-29 08:17:53 +02:00
9ec9d1f9e6 ParameterError: construct XPath like string to identify nested properties 2021-09-28 12:34:08 +02:00
359b00675a ExtJsFormatter: use ParameterError to correctly compute 'errors'
By default, 'errors' is now empty.

Depend on proxmox 0.13.5.
2021-09-28 10:19:55 +02:00
48ba0a2dd5 ExtJsFormatter: use ParameterError to correctly compute 'errors'
By default, 'errors' is now empty.

Depend on proxmox 0.13.5.
2021-09-28 10:19:55 +02:00
b5ea4f9bb2 bump proxmox version to 0.13.5-1 2021-09-28 09:52:27 +02:00
51db0d0f12 ParameterError: record parameter names 2021-09-28 09:52:27 +02:00
5a88aaf074 cli/text_table: calculate correct column width for unicode characters
When printing unicode text, a glyph can take up more (or less) space than
a single column. To handle that, use the 'unicode-width' crate which
calculates the width by the unicode standard.

This makes the text tables correctly aligned when printing unicode
characters (e.g. in a datastore/user/syncjob comment).

'unicode-width' is used itself in the rust compiler to format errors
(see e.g. the Cargo.toml in /compiler/rustc_errors of the rust git)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-09-28 08:05:28 +02:00
962553d252 proxmox-rest-server: cleanup formatter, improve docs
Use trait for OutputFormatter. This is functionally equivalent,
but more rust-like...
2021-09-28 07:45:50 +02:00
6e50c7aac3 WorkerTaskContext: add shutdown_requested() and fail_on_shutdown() 2021-09-24 12:04:31 +02:00
42dae7e1fb cleanup WorkerTaskContext 2021-09-24 11:39:30 +02:00
5a37cfd4c0 upid: remove arbitrary 128 max length for UPID
we can easily go beyond that when having long datastore/remote names
also because we do 'systemd-encode' them, which means that every special
char takes up 4 bytes (e.g. '-' => '\x2d')

while we could just increase the lenght to say 256 or 512, i do not
really see the benefit to limit this at all, since users cannot create
tasks with arbitrary names, and all other fields are generated from
other valid types (username, datastore, remote, etc.)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-09-24 11:08:59 +02:00
020c8e6980 cleanup worker task logging
In order to avoid name conflicts with WorkerTaskContext

- renamed WorkerTask::log to WorkerTask::log_message

Note: Methods have different fuction signatures

Also renamed WorkerTask::warn to WorkerTask::log_warning for
consistency reasons.

Use the task_log!() and task_warn!() macros more often.
2021-09-24 10:34:11 +02:00
7a4bb6000e rename TaskState to WorkerTaskContext 2021-09-24 10:33:49 +02:00
85ec987a48 move src/server/h2service.rs into proxmox-rest-server crate 2021-09-24 10:28:17 +02:00
e8c124fe1b move worker_task.rs into proxmox-rest-server crate
Also moved pbs-datastore/src/task.rs to pbs-tools, which now depends on 'log'.
2021-09-24 10:28:17 +02:00
3cec879463 bump proxmox version to 0.13.4-1 2021-09-23 12:06:48 +02:00
ed9bdab576 add UPID api type 2021-09-23 12:04:15 +02:00
fc2253b3e8 add systemd escape_unit and unescape_unit 2021-09-23 12:04:15 +02:00
59c7c360e8 use UPID and systemd helpers from proxmox 0.13.4 2021-09-23 12:01:43 +02:00
eb1f23c588 use UPID and systemd helpers from proxmox 0.13.4 2021-09-23 12:01:43 +02:00
ecb6b64f18 src/server/worker_task.rs: Avoid using pbs-api-type::Authid
Because we want to move worker_task.rs into proxmox-rest-server crate.
2021-09-23 11:59:25 +02:00
0999494564 schema: add extra info to array parameters
it's not immediately obvious that they can be specified more than once
otherwise.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-22 07:58:52 +02:00
5f5a5194de schema: print item type-text instead of <array>
this is only used for CLI synopsis/usage strings, the API viewer already
prints the full type text in a correct format. the old variant was also
rather misleading, since on the CLI we don't pass in an array, but each
item as its own parameter.

noticed this while working on the pull/sync filtering series, but it
affects quite a lot of stuff, among other things the Updater and
Deleteable CLI, e.g. from `man proxmox-backup-manager`:

>       --delete <array>
>                     List of properties to delete.

vs.

>       --delete disable|validation-delay
>                     List of properties to delete.

But some of them might only have <string> as the item type text,
which is not much nicer but also not really worse.

The whole "List of .." is confusing anyway, but not easily solvable,
since the description is used for
- API dump/viewer (where it is a list/array of ..)
- usage message/man pages (where it's a parameter that gives a single
  element, but it might be passed in multiple times to construct an
  array)

Also, for some common occurrences, the item description is too
generic, and it's not possible to override the description for
external types with the current api macro.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
 [ Thomas: Added more context that was in the diffstat of the path ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-22 07:56:09 +02:00
3d428713c5 rename pbs-systemd to proxmox-systemd 2021-09-21 10:06:27 +02:00
1d60abf9f1 move src/server/rest.rs to proxmox-rest-server crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
1eb2dd5dac move ApiConfig, FileLogger and CommandoSocket to proxmox-rest-server workspace
ApiConfig: avoid using  pbs_config::backup_user()
CommandoSocket: avoid using  pbs_config::backup_user()
FileLogger: avoid using  pbs_config::backup_user()
- use atomic_open_or_create_file()

Auth Trait: moved definitions to proxmox-rest-server/src/lib.rs
- removed CachedUserInfo patrameter
- return user as String (not Authid)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
7c1bd58a8a rest server: cleanup auth-log handling
Handle auth logs the same way as access log.
- Configure with ApiConfig
- CommandoSocket command to reload auth-logs "api-auth-log-reopen"

Inside API calls, we now access the ApiConfig using the RestEnvironment.

The openid_login api now also logs failed logins and return http_err!(UNAUTHORIZED, ..)
on failed logins.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
48b7a61a21 rest server: do not use pbs_api_types::Authid
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
2ea6f8d01d rest server: return UserInformation from ApiAuth::check_auth
This need impl UserInformation for Arc<CachedUserInfo> which is implemented
with proxmox 0.13.2

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
efeccc11cc make get_index and ApiConfig property (callback)
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
3d73529460 rest server: simplify get_index() method signature
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
cc67441662 move normalize_uri_path and extract_cookie to proxmox-rest-server crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
fbe0de85d0 move src/tools/compression.rs to proxmox-rest-server crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
dc28aa1ae7 move src/server/formatter.rs to proxmox-rest-server crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
ba04dfb9b2 move src/server/environment.rs to proxmox-rest-server crate
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
51d84f9847 move src/tools/daemon.rs to proxmox-rest-server workspace
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
ca7a26166f move ApiConfig, FileLogger and CommandoSocket to proxmox-rest-server workspace
ApiConfig: avoid using  pbs_config::backup_user()
CommandoSocket: avoid using  pbs_config::backup_user()
FileLogger: avoid using  pbs_config::backup_user()
- use atomic_open_or_create_file()

Auth Trait: moved definitions to proxmox-rest-server/src/lib.rs
- removed CachedUserInfo patrameter
- return user as String (not Authid)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
2e426f9df2 start new proxmox-rest-server workspace
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-21 08:46:41 +02:00
3cd221c163 bump proxmox version to 0.13.3-1 2021-09-21 06:45:28 +02:00
b6b9118ce3 atomic_open_or_create_file: add support for OFlag::O_EXCL 2021-09-20 08:24:44 +02:00
b46a0720ae atomic_open_or_create_file: catch unsupported flag OFlag::O_DIRECTORY 2021-09-20 08:13:28 +02:00
c7e1a1f3c8 update proxmox/debian/control 2021-09-20 08:12:18 +02:00
297a076478 bump proxmox version to 0.13.2-1 2021-09-16 11:03:27 +02:00
fb2b7a4e93 impl <T: UserInformation> UserInformation for std::sync::Arc<T> 2021-09-16 10:21:50 +02:00
254ef7ff82 proxmox: generate_usage_str: don't require static lifetimes
this prevents us from using it under certain conditions and it's
actually not necessary, so drop them

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-09-15 08:09:24 +02:00
c983a1d623 avoid type re-exports 2021-09-14 08:35:43 +02:00
a4e56bff60 more api type cleanups: avoid re-exports 2021-09-10 12:25:32 +02:00
441b34e46e more api type cleanups: avoid re-exports 2021-09-10 12:25:32 +02:00
908d87f6cb more api type cleanups: avoid re-exports 2021-09-10 12:25:32 +02:00
157d9a45e1 move PruneOptions to pbs_api_types workspace 2021-09-10 09:21:27 +02:00
086536e3fa move datastore config to pbs_config workspace 2021-09-10 08:40:58 +02:00
bc6ad1dfce move user configuration to pbs_config workspace
Also moved memcom.rs and cached_user_info.rs
2021-09-10 07:09:04 +02:00
99ac07d906 cleanup User configuration: use Updater 2021-09-09 13:14:28 +02:00
d48f612bec move acl to pbs_config workspaces, pbs_api_types cleanups 2021-09-09 10:50:08 +02:00
db1012b5aa move network config to pbs_config workspace 2021-09-08 12:22:48 +02:00
56d4dc1034 changer config cleanup: use Updater 2021-09-08 09:29:01 +02:00
3d9b2c8fd5 tape job cleanup: user Updater 2021-09-08 08:55:55 +02:00
5f13dcc5fc verify job cleanup: use Updater/flatten 2021-09-08 08:40:32 +02:00
7760d5679c sync job cleanup: use Updater/flatten 2021-09-08 08:28:09 +02:00
7240e6374b moved tape_job.rs to pbs_config workspace 2021-09-07 12:40:15 +02:00
8fe018cfd8 move Kdf and KeyInfo to pbs_api_types workspace 2021-09-07 09:59:59 +02:00
28e668ddf3 move drive config to pbs_config workspace
Also moved the tape type definitions to pbs_api_types.
2021-09-03 09:10:18 +02:00
1c30b9da92 add missing file pbs-api-types/src/remote.rs 2021-09-02 17:36:13 +02:00
6b977533d6 move remote config into pbs-config workspace 2021-09-02 14:25:15 +02:00
80f7bf3822 start new pbs-config workspace
moved src/config/domains.rs
2021-09-02 12:58:20 +02:00
93dfd7d8e9 start new pbs-config workspace
moved src/config/domains.rs
2021-09-02 12:58:20 +02:00
3fc017a570 start new pbs-config workspace
moved src/config/domains.rs
2021-09-02 12:58:20 +02:00
401cf57883 another import cleanup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-09-01 14:46:01 +02:00
199227bd01 move some more API types
ArchiveEntry -> pbs-datastore
RestoreDaemonStatus -> pbs-api-types

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-31 11:29:17 +02:00
6dc073fa0f move some API return types to pbs-api-types
they'll be required by the api client

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-30 11:39:01 +02:00
6a52b3b8e7 proxmox: bump d/control for api-macro 0.5.1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-30 10:49:27 +02:00
36ed21c548 bump proxmox to 0.13.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-30 10:47:28 +02:00
cb96de1c4d bump proxmox-api-macro to 0.5.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-30 10:46:05 +02:00
14bd81fbc4 doc: update UpdaterType documentation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-30 08:21:08 +02:00
76641b0a72 api-macro: allow external schemas in 'returns' specification
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-26 13:34:23 +02:00
bf84e75603 api: move ReturnType from router to schema
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-26 11:39:55 +02:00
12312bcb36 more Updatable -> UpdaterType fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 10:43:58 +02:00
6970858aad bump proxmox dependency to 0.13.0
and with it:
* bump proxmox-http dependency to 0.4.0
* bump proxmox-apt dependency to 0.7.0

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 10:43:58 +02:00
35e7f2f48e use ApiType trait
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 10:43:58 +02:00
d0103000b8 use new api updater features
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 10:43:58 +02:00
8d0c0ed699 bump version to 0.7.0, depend on proxmox 0.13.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 10:41:25 +02:00
0f2caafc4e bump proxmox to 0.13.0-1 and proxmox-api-macro to 0.5.0
and proxmox-http to 0.4.0... urgh

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 09:41:17 +02:00
2df0b8efb3 tools::serde: support Option<String> in string_as_base64
This will make Updater derivations go more smoothly.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-25 09:41:17 +02:00
f9c6f7a18c bump versionto 0.7.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-24 15:41:08 +02:00
8c125364e4 websocket: fix doc test
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-24 15:19:47 +02:00
917ce00dd6 rustfmt
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-13 13:31:57 +02:00
744d69c2ab more updater cleanups
* Updatable is now named UpdaterType
* UPDATER_IS_OPTION is now assumed to always be true
    While an updater can be a non-optional struct, being an
    updater, all its fields are also Updaters, so after
    expanding all levels of nesting, the resulting list of
    fields can only contain optional values.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-13 11:11:27 +02:00
cb9d57b453 put API_SCHEMA variable into ApiType trait
This way we can assign `API_SCHEMA` constants to `Option`
types.

Here's why:

The api-macro generated code usese `T::API_SCHEMA` when
building ObjectSchemas.

For Updaters we replace `T` with
  `<T as Updatable>::Updater`

This means for "simple" wrappers like our `Authid` or
`Userid`, the ObjectSchema will try to refer to
  `<Authid as Updatable>::Updater::API_SCHEMA`
which resolves to:
  `Option<Authid>::API_SCHEMA`
which does not exist, for which we cannot add a normal
`impl` block to add the schema variable, since `Option` is
not "ours".

But we now have a blanket implementation of `ApiType` for
`Option<T> where T: ApiType` which just points to the
original `T::API_SCHEMA`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-12 10:15:13 +02:00
783cbcb499 fixup schema entry for updaters with explicit types
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-10 14:17:20 +02:00
34020ea3d6 change updater derivation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-10 12:03:29 +02:00
017b81712e api macro: assume that field types are api types by default
#[api]
    struct Foo {
        field: Bar,
    }

does not require the use of
    #[api(
        properties: {
            field: {
                type: Bar,
            },
        },
    )]

anymore

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-10 11:42:24 +02:00
8ebcd68a2c refactor serde parsing for later reuse
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-08-10 11:42:24 +02:00
4174305528 bump version to 0.6.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-29 18:11:24 +02:00
fb51dcf9db add type DebianCodename
which allows to get rid of an possible error with check_suites, and
easily detect unexpected values with get_current_release_codename.

The check_repos function needs to be adapted, since the type does
not include suite names like oldstable,experimental,etc.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-29 18:00:38 +02:00
51c69d76a5 repo: remove has_suite_variant helper
by exchanging loops in the check_suites function, which was the only
user. Exchanging loops also helps for introducing a type for Debian condenames.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-29 18:00:38 +02:00
13cdf8d1df check repos: have caller specify the current suite
Like that, a potential error is further up the stack, and it's more
consistent with what the standard_repository functions do.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-29 18:00:38 +02:00
c7f6163e2b repo: make suite_variant helper more general
use the first appearance of '-' or '/' to detect the variant instead
of keeping a list of possible variants, which would need to include
things like "-proposed-updates-debug".

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-29 18:00:38 +02:00
f48c12b00a standard repos: add suite parameter for stricter detection
Require that the suite matches too when detecting standard
repositories, since no or invalid updates will be obtained when the
suite is wrong. Thus, it should not be considered to be the same
repository.

Add the parameter for get_standard_repository too, so that the two
related calls have more similar parameters, and the detection of the
current release code name can be done once in the caller once.

This also will fix an issue with the front-end, where adding a
standard repository would end up just enabling an already present
repository with the wrong suite.

Reported-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-29 18:00:38 +02:00
0b1ecc260a cargo: update proxmox to 0.12.1
For the FS compat improvement in the atomic create file helper

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-22 10:09:53 +02:00
cb04553d47 proxmox: d/control: commit version update changes
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-22 09:53:38 +02:00
907a9f344d proxmox: bump version to 0.12.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-22 09:48:04 +02:00
3fd900d2b2 tools fs: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-22 09:44:17 +02:00
11dccc40b5 fs: link fallback for atomic create
Some file systems don't support renameat2's RENAME_NOREPLACE
flag (eg. ZFS), at the some time, some other file systems
don't support hardlinks via link (eg. vfat, cifs), so we now
try both: first the rename (since it's more efficient), then
link+unlink for the rest.

If both fail, the file system is simply not supported for
our purposes anyway...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-22 09:41:04 +02:00
7bddd33ede move remaining client tools to pbs-tools/datastore
pbs-datastore now ended up depending on tokio after all, but
that's fine for now

for the fuse code I added pbs-fuse-loop (has the old
fuse_loop and its 'loopdev' module)
ultimately only binaries should depend on this to avoid the
library link

the only thins remaining to move out the client binary are
the api method return types, those will need to be moved to
pbs-api-types...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-21 14:12:24 +02:00
e7ff5817ec add helpers to write configuration files 2021-07-20 18:54:23 +02:00
a3de24506a depend on proxmox 0.12.0, bump version to 0.6.1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 18:10:48 +02:00
68064f65bc buildsys: indent continued command
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 18:08:49 +02:00
606eac1db7 cargo: disable default-features for proxmox
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 17:54:03 +02:00
2d9a018854 proxmox: bump api-macro dependency
cyclic stuff is annoying...

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 17:45:58 +02:00
c75e706006 proxmox-api-macro: bump version to 0.4.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 17:45:29 +02:00
44d571a8e0 proxmox-http: bump version to 0.3.0
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 17:45:29 +02:00
39f12a8c05 depend on proxmox 0.12.0, bump version to 0.5.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 17:25:09 +02:00
5cbddad172 update versions in generated control files 2021-07-20 15:45:45 +02:00
82d7a1cdbf bump proxmox version to 0.12.0-1 2021-07-20 15:45:45 +02:00
08c2fc4acb open_file_locked: add options parameter (CreateOptions)
To be able to set file permissions and ownership.

This is a breaking change.
2021-07-20 15:45:45 +02:00
a818e74a23 new helper atomic_open_or_create_file() 2021-07-20 15:45:45 +02:00
716101d660 move channel/stream helpers to pbs-tools
pbs_tools
  ::blocking: std/async wrapping with block_in_place
  ::stream: stream <-> AsyncRead/AsyncWrite wrapping

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-20 11:27:40 +02:00
893cc0455d buildsys: drop buster from upload target
we are not really compatible with pbs1, needs a stable-1 branch if we
need to backport something

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-20 08:59:59 +02:00
711535cdee move some api types to pbs-api-types
and resolve some imports in the client binary

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-19 15:01:03 +02:00
e8349f3d2e move client to pbs-client subcrate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-19 12:58:43 +02:00
1081dc8d59 move client to pbs-client subcrate
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-19 12:58:43 +02:00
7d88081e0d bump version to 0.5.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-16 16:19:28 +02:00
5581858b30 code cleanup: use contains()
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-16 15:22:51 +02:00
2a1fb9bfdd standard repo detection: handle alternative URI for PVE repos
For PVE, URIs without the final "/pve" are also valid.

Make the single URL response a vector and iterate over it, lower
index is preferred.

Reported in the community forum:
https://forum.proxmox.com/threads/pve-7-0-9-no-proxmox-ve-repository-enabled.92427/

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
[ Thomas: extend commit message slightly ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-16 15:21:21 +02:00
652f52a1f2 bump proxmox dependency
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-16 15:18:45 +02:00
a25f4f3b36 api-types: move PRUNE_SCHEMA_KEEP_* to pbs-api-types
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-07-16 11:26:09 +02:00
a5093db6f3 api: add support for notes on backup groups
Stored in atomically-updated 'notes' file in backup group directory.
Available via dedicated GET/PUT API calls, as well as the first line
being included in list_groups (similar to list_snapshots).

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-12 07:13:28 +02:00
1c81fc4cc1 rest: log response: avoid unnecessary mut on variable
a match expresses the fallback slightly nicer and needs no mut,
which is always nice to avoid.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-11 13:05:19 +02:00
1e00eae767 move more api types for the client
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-09 15:17:53 +02:00
0b8cd2b305 move some api types and resolve imports
in preparation of moving client & proxmox_client_tools out
into a pbs-client subcrate

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-09 15:17:52 +02:00
0d5d32a76a move chunk_store to pbs-datastore
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 14:37:47 +02:00
419a6ce60f move UPID to pbs-api-types, add UPIDExt
pbs-server side related methods are added via the UPIDExt
trait

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 13:51:03 +02:00
223db419a4 test fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 12:17:10 +02:00
add07b08c7 move backup id related types to pbs-api-types
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 11:34:56 +02:00
4e21b52bc2 move userid types to pbs-api-types
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 09:53:48 +02:00
ad4a9aea25 move id and single line comment format to pbs-api-types
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 09:49:38 +02:00
4642ba673a move TaskState trait to pbs-datastore
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 09:24:39 +02:00
669d53c0bf add pbs-api-types subcrate, move key_derivation
move key_derivation to pbs-datastore

pbs-api-types should only contain "basic" types which
* are usually required by clients
* don't depend on pbs-related code directly

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-07 09:04:09 +02:00
6759d3ebaf split out pbs-buildcfg module
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-07-06 12:00:14 +02:00
7bed6d5c31 REST: set error message extenesion for bad-request response log
We send it already to the user via the response body, but the
log_response does not has, nor wants to have FWIW, access to the
async body stream, so pass it through the ErrorMessageExtension
mechanism like we do else where.

Note that this is not only useful for PBS API proxy/daemon but also
the REST server of the file-restore daemon running inside the restore
VM, and it really is *very* helpful to debug things there..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-03 21:34:03 +02:00
3c2492f0ff REST: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-03 21:34:03 +02:00
9a51a30f46 tests: parse and write the result again
A cheap way to "double" the number of test cases.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-02 13:04:00 +02:00
4b0935c621 bump version to 0.4.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-07-01 18:35:09 +02:00
ae7e2360b7 support quote-word parsing for one-line format
so that parsing CD ROM repositories with spaces in the name works too.
But it's not limited to that, and should make one-line parsing rather
similar to what APT does (stanza parsing in APT doesn't use
ParseQuoteWord at all AFAICS).

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-01 18:27:27 +02:00
8265d0ce33 avoid backtick unicode symbol in string
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-01 18:27:27 +02:00
fd99aa062e bump version to 0.3.1-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-06-30 20:45:27 +02:00
34074c1b80 slightly adapt repository text
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-06-30 20:42:31 +02:00
3c4e441d7c standard repos: allow conversion from handle and improve information
Add a description for the handle, which can be useful to display
alongside the name. The descriptions are essentially the first
sentence from PVE's "Package Repositories" docs, but without the
product name.

Also drop the " Repository" suffix from the names, as it's not useful,
but can be ugly: e.g. for the UI when the label already is
'Repository:'.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-30 20:38:05 +02:00
47c2c350b4 bump version to 0.3.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-06-30 13:34:37 +02:00
082c868535 buildsys: upload: switch product to devel and dist to bullseye
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-06-30 13:34:37 +02:00
3f7152383b standard repos: drop product acronym from repo name
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-30 13:24:42 +02:00
87ea23ec83 check: return 'origin' property instead of 'badge' for official host
which is obtained from the cached InRelease file and also works for
mirrors, host aliases, direct IPs.

The has_official_uri function was replaced by origin_from_uris.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-30 13:24:42 +02:00
bb0ff2ac73 add get_cached_origin method and an initial config module
Allows obtaining the 'Origin' property from the cached InRelease file.

Used the once_cell crate for the config module, because it is already
used in proxmox-backup and seemed to be the right fit here.

For now, the config module is just used to be able to override the
path for the test environment, but those are actual APT config
variables, and in the future, it can be extended and used to actually
parse the apt.conf(.d/*) on the system.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-30 13:24:42 +02:00
558fbff1d7 bump version to 0.6.0-2 2021-06-30 08:43:40 +02:00
6c9ce7dbb6 remove debug output 2021-06-30 08:42:14 +02:00
60eedc0da2 bump version to 0.6.0-1 2021-06-25 11:09:48 +02:00
1ecdc2ed72 use one lock file per realm 2021-06-25 11:05:56 +02:00
c8f0c006e7 bump version to 0.2.0-1
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-23 16:00:30 +02:00
8ada17854d add handling of Proxmox standard repositories
Get handles for the available repositories along with their current
configuration status and make it possible to add them.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-23 16:00:30 +02:00
76d3a5ba1f add more functions to check repositories
Currently includes check for suites and check for official URIs

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-23 16:00:30 +02:00
5bf8ddece7 add files for Debian packaging
The Makefile is based on the one from Mira's conntrack series, as it already got
some review.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-23 16:00:30 +02:00
b6be0f3940 initial commit
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-06-23 16:00:30 +02:00
42dedabbb1 bump version to 0.5.0-1 2021-06-23 11:29:13 +02:00
5751689167 avoid unused features "sortable-macro" and "api-macro" 2021-06-23 11:27:55 +02:00
426f7b6014 bump versionm to 0.4.0-1 2021-06-23 11:17:30 +02:00
4f90d7009d set "default-features = false" for proxmox crate 2021-06-23 11:15:33 +02:00
8286806a19 bump version toö 0.3.0-1 2021-06-22 09:23:59 +02:00
ac034c72da return Url as string
To make perl bindings simple.
2021-06-21 14:12:13 +02:00
cc64c7e35d bump version to 0.2.0-1 2021-06-21 13:37:43 +02:00
73c5c4af7c implement Deserialize/Serialize for OpenIdConfig
Useful to create perl bindings.
2021-06-21 13:27:05 +02:00
634e35489a add packaging
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-06-18 16:16:40 +02:00
dd0d18d5ce make state directory configurable (pass state_dir as parameter) 2021-06-18 10:49:38 +02:00
ca65d297b7 initial import 2021-06-18 10:23:57 +02:00
f28a85da5e build: upload to buster and bullseye
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-06-11 14:00:55 +02:00
fa8194e0bb refactor send_command
- refactor the combinators,
- make it take a `&T: Serialize` instead of a Value, and
  allow sending the raw string via `send_raw_command`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-11 16:28:08 +02:00
ee7fe8f93c bump version to 0.2.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 13:54:36 +02:00
a145553557 finish client documentation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 12:26:56 +02:00
5f4b571450 add Client::directory_url helper
allows to drop a `mut` requirement in pmg-rs without having
to store the URL twice

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 12:21:17 +02:00
1d1f80f5ca and another *new* clippy fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 11:50:54 +02:00
f406e6fb34 formatting fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 11:49:26 +02:00
9538126247 clippy fixes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 11:47:56 +02:00
dbabed6842 derive Copy for the simple status enums
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 11:47:45 +02:00
2dfc6a74bb mark Error as must_use
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-07 11:39:04 +02:00
0ef3c33538 doc fixup
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-05 11:21:16 +02:00
5f0ba96810 finish docs and #[deny(missing_docs)] at the top level
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-05 11:16:13 +02:00
357c0614cf even more documentation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-04 11:21:30 +02:00
7bd0bfe1b2 more documentation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-04 11:20:11 +02:00
47af324d94 Some documentation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-04 11:04:17 +02:00
7c67886e1f add top level doc
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-05-04 09:11:18 +02:00
610b147ba7 server/rest: fix new type ambiguity
basically the same as commit 5fb15b781e
Will be required once we get to use a newer rustc, at least the
client build for archlinux was broken due to this.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-22 21:24:44 +02:00
a6ff69404b bump version to 0.2.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-04-14 14:57:46 +02:00
558f51a167 make revocation workflow accessible without client
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-04-14 14:56:40 +02:00
b624fa1f3c bump version to 0.2.0-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-04-12 15:31:53 +02:00
a947050ec1 add util::Csr for CSR generation
This is essentially taken from pmg-rs and should be used
from there.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-04-12 14:54:21 +02:00
37b4c4f654 add is_valid to all Status enums
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-04-12 13:38:10 +02:00
5a70f0c392 add is_pending to all Status enums
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-04-12 13:32:40 +02:00
ac49fbd472 enable tape backup by default
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-12 12:31:56 +02:00
c80251ecca server/rest: add ApiAuth trait to make user auth generic
This allows switching the base user identification/authentication method
in the rest server. Will initially be used for single file restore VMs,
where authentication is based on a ticket file, not the PBS user
backend (PAM/local).

To avoid putting generic types into the RestServer type for this, we
merge the two calls "extract_auth_data" and "check_auth" into a single
one, which can use whatever type it wants internally.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2021-04-08 13:57:57 +02:00
1b2a34166d server: rest: collapse nested if for less indentation
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-07 17:57:46 +02:00
27fca61f1b server: rest: switch from fastest to default deflate compression level
I made some comparision with bombardier[0], the one listed here are
30s looped requests with two concurrent clients:

[ static download of ext-all.js ]:
  lvl                              avg /   stdev  / max
 none        1.98 MiB  100 %    5.17ms /  1.30ms / 32.38ms
 fastest   813.14 KiB   42 %   20.53ms /  2.85ms / 58.71ms
 default   626.35 KiB   30 %   39.70ms /  3.98ms / 85.47ms

[ deterministic (pre-defined data), but real API call ]:
  lvl                              avg /   stdev  / max
 none      129.09 KiB  100 %    2.70ms / 471.58us / 26.93ms
 fastest    42.12 KiB   33 %    3.47ms / 606.46us / 32.42ms
 default    34.82 KiB   27 %    4.28ms / 737.99us / 33.75ms

The reduction is quite better with default, but it's also slower, but
only when testing over unconstrained network. For real world
scenarios where compression actually matters, e.g., when using a
spotty train connection, we will be faster again with better
compression.

A GPRS limited connection (Firefox developer console) requires the
following load (until the DOMContentLoaded event triggered) times:
  lvl        t      x faster
 none      9m 18.6s   x 1.0
 fastest   3m 20.0s   x 2.8
 default   2m 30.0s   x 3.7

So for worst case using sligthly more CPU time on the server has a
tremendous effect on the client load time.

Using a more realistical example and limiting for "Good 2G" gives:

 none      1m  1.8s   x 1.0
 fastest      22.6s   x 2.7
 default      16.6s   x 3.7

16s is somewhat OK, >1m just isn't...

So, use default level to ensure we get bearable load times on
clients, and if we want to improve transmission size AND speed then
we could always use a in-memory cache, only a few MiB would be
required for the compressable static files we server.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-07 17:57:42 +02:00
692de3a903 server/rest: compress static files
compress them on the fly
and refactor the size limit for chunking files

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-04-07 12:34:31 +02:00
376d90679c server/rest: compress api calls
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-04-07 12:34:31 +02:00
c2a1c87039 server/rest: add helper to extract compression headers
for now we only extract 'deflate'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-04-07 12:34:31 +02:00
a06d3892fe tools/compression: add DeflateEncoder and helpers
implements a deflate encoder that can compress anything that implements
AsyncRead + Unpin into a file with the helper 'compress'

if the inner type is a Stream, it implements Stream itself, this way
some streaming data can be streamed compressed

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-04-07 12:34:31 +02:00
d28249c290 tools: add compression module
only contains a basic enum for the different compresssion methods

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-04-07 12:34:31 +02:00
7334107fae server/rest: drop now unused imports
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-01 11:53:13 +02:00
25a14e8eb6 server/rest: extract auth to seperate module
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2021-04-01 11:26:28 +02:00
5fb15b781e server/rest: fix type ambiguity
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-03-29 12:02:30 +02:00
45a652c26f server/rest: rust format
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-03-29 08:17:26 +02:00
afc59f6d15 add more of the ACME workflow to 'Account'
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-18 11:36:42 +01:00
02ecbb499c bump version to 0.1.4-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-17 15:28:55 +01:00
5aee14ac00 collect extra account fields in AccountData
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-17 15:20:50 +01:00
d369de8636 bump version to 0.1.3-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-17 13:35:49 +01:00
937a99a2e1 fix ec signature padding
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-17 13:32:57 +01:00
549b52cf68 don't serialize 'null' for 'expires' when ordering
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-17 12:24:21 +01:00
15fae62c15 bump version to 0.1.2-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-12 15:43:40 +01:00
ca0f57290f explicitly pass Content-Length header
pebble refuses to cooperate without it

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-10 12:01:00 +01:00
c9f137a093 bump version to 0.1.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-09 13:23:27 +01:00
cbfeb58ce4 make AccountData fields public
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-09 13:22:52 +01:00
aa23068293 import
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-03-09 13:07:59 +01:00
9dbc5f47ca ui: enable experimental tape UI if tape.cfg exists 2021-03-03 09:02:02 +01:00
8635b165b2 rest: implement tower service for UnixStream
This allows anything that can be represented as a UnixStream to be used
as transport for an API server (e.g. virtio sockets).

A tower service expects an IP address as it's peer, which we can't
reliably provide for unix socket based transports, so just fake one.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2021-02-17 07:50:35 +01:00
c94f367dec clippy: more misc fixes
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-26 09:54:55 +01:00
2a16daafdd allow complex Futures in tower_service impl
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-26 09:53:55 +01:00
38df61a621 clippy: fix/allow needless_range_loop
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-20 16:23:54 +01:00
ba81db7848 clippy: us *_or_else with function calls
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-20 16:23:54 +01:00
655ceac1c6 clippy: collapse/rework nested ifs
no semantic changes (intended).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-20 16:22:59 +01:00
724e2f47f9 clippy: use strip_prefix instead of manual stripping
it's less error-prone (off-by-one!)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-20 16:22:59 +01:00
28f3b0df9e clippy: remove unnecessary clones
and from::<T>(T)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-20 16:22:59 +01:00
d6874957e3 proxmox 0.10: adapt to moved ParameterSchema
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-14 16:01:33 +01:00
e2e0339966 cleanup: remove unnecessary 'mut' and '.clone()'
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-14 16:01:33 +01:00
c99f950310 tokio 1.0: update to new tokio-openssl interface
connect/accept are now happening on pinned SslStreams

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-14 16:01:33 +01:00
d94119efc8 tokio 1.0: delay -> sleep
almost the same thing, new name(s), no longer Unpin

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-01-14 16:01:33 +01:00
33839410b9 api: tfa management and login
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-01-11 10:22:32 +01:00
8bb221663e adaptions for proxmox 0.9 and proxmox-api-macro 0.3
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-12-22 07:31:05 +01:00
f9378cad13 tools/daemon: improve reload behaviour
it seems that sometimes, the child process signal gets handled
before the parent process signal. Systemd then ignores the
childs signal (finished reloading) and only after going into
reloading state because of the parent. this will never finish.

Instead, wait for the state to change to 'reloading' after sending
that signal in the parent, an only fork afterwards. This way
we ensure that systemd knows about the reloading before actually trying
to do it.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Tested-By: Fabian Ebner <f.ebner@proxmox.com>
2020-12-18 10:30:37 +01:00
6b0dabefd4 file logger: remove test.log after test as well
and a doc formatting fixup

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-11-30 14:13:21 +01:00
85b5be8133 rest: check for disabled token (user)
when authenticating a token, and not just when authenticating a
user/ticket.

Reported-By: Dominik Jäger <d.jaeger@proxmox.com>

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-11-11 12:21:29 +01:00
e6edbb5c3b daemon: rename method, endless loop, bail on exec error
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-11 10:14:01 +01:00
d75579790c daemon: add hack for sd_notify
sd_notify is not synchronous, iow. it only waits until the message
reaches the queue not until it is processed by systemd

when the process that sent such a message exits before systemd could
process it, it cannot be associated to the correct pid

so in case of reloading, we send a message with 'MAINPID=<newpid>'
to signal that it will change. if now the old process exits before
systemd knows this, it will not accept the 'READY=1' message from the
child, since it rejects the MAINPID change

since there is no (AFAICS) library interface to check the unit status,
we use 'systemctl is-active <SERVICE_NAME>' to check the state until
it is not 'reloading' anymore.

on newer systemd versions, there is 'sd_notify_barrier' which would
allow us to wait for systemd to have all messages from the current
pid to be processed before acknowledging to the child, but on buster
the systemd version is to old...

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-11-11 09:43:00 +01:00
df9b42db3f tools/daemon: fix reload with open connections
instead of await'ing the result of 'create_service' directly,
poll it together with the shutdown_future

if we reached that, fork_restart the new daemon, and await
the open future from 'create_service'

this way the old process still handles open connections until they finish,
while we already start a new process that handles new incoming connections

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-11-05 11:14:56 +01:00
5d7ae1f38c api: factor out auth logger and use for all API authentication failures
we have information here not available in the access log, especially
if the /api2/extjs formatter is used, which encapsulates errors in a
200 response.

So keep the auth log for now, but extend it use from create ticket
calls to all authentication failures for API calls, this ensures one
can also fail2ban tokens.

Do that logging in a central place, which makes it simple but means
that we do not have the user ID information available to include in
the log.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-04 17:26:34 +01:00
4031710b36 server: implement access log rotation with re-open via command socket
re-use the future we already have for task log rotation to trigger
it.

Move the FileLogger in ApiConfig into an Arc, so that we can actually
update it and REST using the new one.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-02 19:53:30 +01:00
6e2e7e66c5 command socket: make create_control_socket private
this is internal for now, use the comanndo socket struct
implementation, and ideally not a new one but the existing ones
created in the proxy and api daemons.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-02 19:50:24 +01:00
3dd23fd3ba server: add CommandoSocket where multiple users can register commands
This is a preparatory step to replace the task control socket with it
and provide a "reopen log file" command for the rest server.

Kept it simple by disallowing to register new commands after the
socket gets spawned, this avoids the need for locking.

If we really need that we can always wrap it in a Arc<RWLock<..>> or
something like that, or even nicer, register at compile time.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-02 19:32:22 +01:00
67769843da tools: file logger: avoid some possible unwraps in log method
writing to a file can explode quite easily.
time formatting to rfc3339 should be more robust, but it has a few
conditions where it could fail, so catch that too (and only really
do it if required).

The writes to stdout are left as is, it normally is redirected to
journal which is in memory, and thus breaks later than most stuff,
and at that point we probably do not care anymore anyway.

It could make sense to actually return a result here..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-02 19:32:22 +01:00
66b9170dda file logger: allow reopening file
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-11-02 10:03:10 +01:00
95ac6d87c3 server/rest: accept also = as token separator
Like we do in Proxmox VE

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-30 13:34:26 +01:00
7b2f5672c5 server/rest: user constants for HTTP headers
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-30 13:33:36 +01:00
e57660691d api tokens: add authorization method
and properly decode secret (which is a no-op with the current scheme).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-10-30 13:15:14 +01:00
971dc0e6bc REST: extract and handle API tokens
and refactor handling of headers in the REST server while we're at it.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-10-29 15:14:27 +01:00
ed512bc26f replace Userid with Authid
in most generic places. this is accompanied by a change in
RpcEnvironment to purposefully break existing call sites.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-10-29 15:11:39 +01:00
9af79677b2 REST: rename token to csrf_token
for easier differentiation with (future) api_token

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-10-19 14:02:19 +02:00
32bb67cf60 build: bump nix dependency
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-10-19 12:12:33 +02:00
f10a722eda file logger: add option to make the backup user the log file owner
and use that in ApiConfig to avoid that it is owned by root if the
proxmox-backup-api process creates it first.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-19 10:37:26 +02:00
98f91a4d24 server/rest: also log user agent
allows easily to see if a request is from a browser or a proxmox-backup-client
CLI

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 11:23:49 +02:00
287659dcda server/rest: implement request access log
reuse the FileLogger module in append mode.
As it implements write, which is not thread safe (mutable self) and
we use it in a async context we need to serialize access using a
mutex.

Try to use the same format we do in pveproxy, namely the one which is
also used in apache or nginx by default.

Use the response extensions to pass up the userid, if we extract it
from a ticket.

The privileged and unprivileged dameons log both to the same file, to
have a unified view, and avoiding the need to handle more log files.
We avoid extra intra-process locking by reusing the fact that a write
smaller than PIPE_BUF (4k on linux) is atomic for files opened with
the 'O_APPEND' flag. For now the logged request path is not yet
guaranteed to be smaller than that, this will be improved in a future
patch.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 11:23:49 +02:00
1d07c62e74 tools file logger: fix example and comments
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 11:16:29 +02:00
3b2aaa1878 tools: file logger: use option struct to control behavior
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 10:48:36 +02:00
43ab000e92 server: rest: also log the query part of URL
As it is part of the request and we do so in our other products

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 10:41:05 +02:00
da8439b25e server: rest: implement max URI path and query length request limits
Add a generous limit now and return the correct error (414 URI Too
Long). Otherwise we could to pretty larger GET requests, 64 KiB and
possible bigger (at 64 KiB my simple curl test failed due to
shell/curl limitations).

For now allow a 3072 characters as combined length of URI path and
query.

This is conform with the HTTP/1.1 RFCs (e.g., RFC 7231, 6.5.12 and
RFC 2616, 3.2.1) which do not specify any limits, upper or lower, but
require that all server accessible resources mus be reachable without
getting 414, which is normally fulfilled as we have various length
limits for stuff which could be in an URI, in place, e.g.:
 * user id: max. 64 chars
 * datastore: max. 32 chars

The only known problematic API endpoint is the catalog one, used in
the GUI's pxar file browser:
GET /api2/json/admin/datastore/<id>/catalog?..&filepath=<path>

The <path> is the encoded archive path, and can be arbitrary long.

But, this is a flawed design, as even without this new limit one can
easily generate archives which cannot be browsed anymore, as hyper
only accepts requests with max. 64 KiB in the URI.
So rather, we should move that to a GET-as-POST call, which has no
such limitations (and would not need to base32 encode the path).

Note: This change was inspired by adding a request access log, which
profits from such limits as we can then rely on certain atomicity
guarantees when writing requests to the log.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 10:40:39 +02:00
e4bece494c server/rest: forward real client IP on proxied request
needs new proxmox dependency to get the RpcEnvironment changes,
adding client_ip getter and setter.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-16 10:36:54 +02:00
e30fd48080 server: rest: refactor code to avoid multiple log_response calls
The 'Ok::<_, Self::Error>(res)' type annotation was from a time where
we could not use async, and had a combinator here which needed
explicity type information. We switched over to async in commit
df52ba5e45 and, as the type annotation
is already included in the Future type, we can safely drop it.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-15 13:58:47 +02:00
009737844c code cleanups
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-15 13:58:47 +02:00
4d8bd987a4 clippy fixups
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-10-14 11:20:07 +02:00
649ff6f67f server/REST: check auth: code cleanup, better variable names
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-12 18:39:45 +02:00
1fa5b1108d server/REST: make handle_request private
it's not used anywhere else, so do not suggest so

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-12 18:39:00 +02:00
4becd202c5 server: get index: make content-type non mutable
feels more idiomatic

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-12 13:36:45 +02:00
a049949d14 server/rest: code cleanup: use async
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-12 13:36:45 +02:00
16f05d6649 REST: don't print CSRF token
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-10-08 15:57:22 +02:00
d2b63c504a REST server: avoid hard coding world readable API endpoints
while we probably do not add much more to them, it still looks ugly.

If this was made so that adding a World readable API call is "hard"
and not done by accident, it rather should be done as a test on build
time. But, IMO, the API permission schema definitions are easy to
review, and not often changed/added - so any wrong World readable API
call will normally still caught.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-05 08:29:43 +02:00
c6ab240333 rest server: cleanup use statements
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-10-02 13:04:08 +02:00
6eff0b289e rrd: fix integer underflow
Causes a panic if last_update is smaller than RRD_DATA_ENTRIES*reso,
which (I believe) can happen when inserting the first value for a DB.

Clamp the value to 0 in that case.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-01 14:30:32 +02:00
dc12cf44aa avoid chrono dependency, depend on proxmox 0.3.8
- remove chrono dependency

- depend on proxmox 0.3.8

- remove epoch_now, epoch_now_u64 and epoch_now_f64

- remove tm_editor (moved to proxmox crate)

- use new helpers from proxmox 0.3.8
  * epoch_i64 and epoch_f64
  * parse_rfc3339
  * epoch_to_rfc3339_utc
  * strftime_local

- BackupDir changes:
  * store epoch and rfc3339 string instead of DateTime
  * backup_time_to_string now return a Result
  * remove unnecessary TryFrom<(BackupGroup, i64)> for BackupDir

- DynamicIndexHeader: change ctime to i64

- FixedIndexHeader: change ctime to i64
2020-09-15 07:12:57 +02:00
110ceff08c avoid chrono dependency, depend on proxmox 0.3.8
- remove chrono dependency

- depend on proxmox 0.3.8

- remove epoch_now, epoch_now_u64 and epoch_now_f64

- remove tm_editor (moved to proxmox crate)

- use new helpers from proxmox 0.3.8
  * epoch_i64 and epoch_f64
  * parse_rfc3339
  * epoch_to_rfc3339_utc
  * strftime_local

- BackupDir changes:
  * store epoch and rfc3339 string instead of DateTime
  * backup_time_to_string now return a Result
  * remove unnecessary TryFrom<(BackupGroup, i64)> for BackupDir

- DynamicIndexHeader: change ctime to i64

- FixedIndexHeader: change ctime to i64
2020-09-15 07:12:57 +02:00
28aef354ca don't truncate DateTime nanoseconds
where we don't care about them anyway..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2020-09-11 15:48:10 +02:00
1b79d5d5a5 ui: add translation support
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-09-08 08:54:33 +02:00
7917e89426 tools: rename extract_auth_cookie to extract_cookie
It does nothing specific to authentication..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-09-08 08:54:33 +02:00
3c8cb5129e replace and remove old ticket functions
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-08-12 14:28:21 +02:00
906ea7a45b introduce Username, Realm and Userid api types
and begin splitting up types.rs as it has grown quite large
already

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-08-10 12:05:01 +02:00
df9109d493 bump proxmox to 0.3, cleanup http_err macro usage
Also swap the order of a couple of `.map_err().await` to
`.await.map_err()` since that's generally more efficient.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-07-29 09:38:36 +02:00
67294d4796 followup: server/state: rename task_count to internal_task_count
so that the relation with spawn_internal_task is made more clear

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-07-24 12:11:39 +02:00
8588c41f3c server/state: add spawn_internal_task and use it for websockets
is a helper to spawn an internal tokio task without it showing up
in the task list

it is still tracked for reload and notifies the last_worker_listeners

this enables the console to survive a reload of proxmox-backup-proxy

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-07-24 11:17:33 +02:00
054951682d server/rest: add console to index
register the console template and render it when the 'console' parameter
is given

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-07-23 12:06:38 +02:00
9993f4d099 server/config: add mechanism to update template
instead of exposing handlebars itself, offer a register_template and
a render_template ourselves.

render_template checks if the template file was modified since
the last render and reloads it when necessary

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-07-23 11:55:00 +02:00
ab08bd7f39 server: add path value to NOT_FOUND http error
Especially helpful for requests not coming from browsers (where the
URL is normally easy to find out).

Makes it easier to detect if one triggered a request with an old
client, or so..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-07-16 12:46:51 +02:00
c6f8eaf481 src/server/rest.rs: avoid compiler warning 2020-07-10 09:13:52 +02:00
991cc982c7 src/server/rest.rs: disable debug logs 2020-07-09 16:18:14 +02:00
52148f892d improve 'debug' parameter
instead of checking on '1' or 'true', check that it is there and not
'0' and 'false'. this allows using simply

https://foo:8007/?debug

instead of

https://foo:8007/?debug=1

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-06-26 09:12:14 +02:00
fa9323b5c4 src/tools/daemon.rs: reopen STDOUT/STDERR journald streams to get correct PID in logs 2020-06-22 13:06:53 +02:00
ef783bdbef tools::daemon: sync with child after MainPid message
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-06-22 10:58:04 +02:00
93686a9ad7 tools::daemon: fetch exe name in the beginning
We get the path to our executable via a readlink() on
"/proc/self/exe", which appends a " (deleted)" during
package reloads.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-06-22 10:31:54 +02:00
50fa6045b3 api2/status: use new rrd::extract_cached_data
and drop the now unused extract_lists function

this also fixes a bug, where we did not add the datastore to the list at
all when there was no rrd data

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-06-10 13:31:16 +02:00
11cb8cd008 rrd: move creation of serde value into api
there is now a 'extract_cached_data' which just returns
the data of the specified field, and an api function that converts
a list of fields to the correct serde value

this way we do not have to create a serde value in rrd/cache.rs
(makes for a better interface)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-06-10 13:31:14 +02:00
9a860821e4 refactor time functions to tools
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-06-10 13:31:10 +02:00
20ada7a08c rrd: add 'extract_lists'
this is an interface to simply get the Vec<Option<f64>> out of rrd
without going through serde values

we return a list of timestamps and a HashMap with the lists we could find
(otherwise it is not in the map)

if no lists could be extracted, the time list is also empty

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-06-09 12:19:06 +02:00
4e33373ca1 typo fixes all over the place
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-05-30 16:39:08 +02:00
1fa3341d68 rrd: reduce io by saving data only once a minute 2020-05-29 09:16:13 +02:00
a76b0e3c09 src/rrd/rrd.rs: do not wrap error and return ErrorKind::NotFound 2020-05-25 10:30:04 +02:00
9ce21e005d src/rrd/cache.rs: display/log error when RRD load fails 2020-05-25 10:18:53 +02:00
207d0c7714 src/rrd/rrd.rs: store/verify magic number 2020-05-25 09:21:54 +02:00
bfc2f8b7a2 src/rrd/rrd.rs: implement DST_COUNTER 2020-05-25 08:14:30 +02:00
d261516f2d src/rrd/rrd.rs: correctly compute derived values
use f64 for time.
2020-05-25 07:02:04 +02:00
f4561029ba src/rrd/rrd.rs: implement DST_DERIVE 2020-05-24 19:02:35 +02:00
7acdec12e8 src/rrd/rrd.rs: restructure whole code 2020-05-24 16:51:28 +02:00
777f527603 src/rrd/rrd.rs: reduce size by using f64:NAN as UNKNOWN 2020-05-24 09:09:09 +02:00
fd2c16f704 src/rrd/rrd.rs: simplify an fix old value deletion 2020-05-24 06:44:06 +02:00
6357746f2e rrd: fix display interval, try to avoid numeric errors 2020-05-23 16:03:43 +02:00
69da9baf59 rrd: simplify code 2020-05-23 15:37:17 +02:00
e073e5c107 rrd: pack multiple rrd values into th estat list 2020-05-23 14:03:44 +02:00
1c0117790f add experimental rrd api to get cpu stats 2020-05-23 11:50:53 +02:00
5bc85d2d04 add simple rrd implementation 2020-05-23 10:42:48 +02:00
3f633844fa depend on proxmox 0.1.31 - use Value to store result metadata 2020-05-18 09:57:35 +02:00
813aacde7e src/server/command_socket.rs: do not abort loop on client errors, allow backup gid 2020-05-07 09:27:33 +02:00
d0815e5e71 change index to templates using handlebars
using a handlebars instance in ApiConfig, to cache the templates
as long as possible, this is currently ok, as the index template
can only change when the whole package changes

if we split this in the future, we have to trigger a reload of
the daemon on gui package upgrade (so that the template gets reloaded)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-04-29 17:05:53 +02:00
d9d3da2b68 src/config/cached_user_info.rs: cache it up to 5 seconds 2020-04-18 08:49:20 +02:00
803e71103a switch from failure to anyhow
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-04-17 18:43:30 +02:00
5bbd4eef2d src/server/rest.rs: reduce delay for permission error to 500ms 2020-04-16 12:56:34 +02:00
a73a7c33b2 start impl. access permissions 2020-04-16 12:47:16 +02:00
d967838214 api: add list_domains 2020-04-09 11:36:45 +02:00
7726d660b6 src/server/rest.rs: use correct formatter 2020-03-26 12:54:20 +01:00
6b5dc96c7d bump proxmox crate to 0.1.7
The -sys, -tools and -api crate have now been merged into
the proxmx crate directly. Only macro crates are separate
(but still reexported by the proxmox crate in their
designated locations).

When we need to depend on "parts" of the crate later on
we'll just have to use features.

The reason is mostly that these modules had
inter-dependencies which really make them not independent
enough to be their own crates.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-01-21 13:48:37 +01:00
a185257e80 update to nix 0.16 2019-12-19 09:29:44 +01:00
4a08490b81 add CSS file for PBS ExtJS6 basic ui
some fitting rules copied over from PVE's ext6-pve.css file.
simply place it in the css subfolder where the proxmox-backup-gui.js
file is hosted and add a "css/" alias for that directory, the
formatter gets use the right content type with that.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2019-12-17 11:20:32 +01:00
26fff0806a handle_static_file_download: move from and_then to await
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2019-12-17 08:56:55 +01:00
3e2c83471a api2: update for latest proxmox-api changes
- rename ApiFuture into ApiResponseFuture
- impl. ApiHandler::Async
2019-12-16 10:01:51 +01:00
9717c1b4eb update a chunk of stuff to the hyper release
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-12-13 11:24:41 +01:00
4e1e7b8738 src/server/formatter.rs: impl. new result attribute "active" 2019-12-07 15:29:42 +01:00
51ea723de3 rename ApiHandler::Async into ApiHandler::AsyncHttp 2019-11-23 09:03:21 +01:00
764bdf54cf src/server/rest.rs: simplify code 2019-11-22 18:44:14 +01:00
fe73df5be1 src/server/rest.rs: rename get_request_parameters_async to get_request_parameters 2019-11-22 17:24:16 +01:00
ac5b1c2805 src/server/rest.rs - only pass ObjectSchema to get_request_parameters_async() 2019-11-22 17:22:07 +01:00
b0e1e693d9 src/server/rest.rs: cleanup async code 2019-11-22 13:02:05 +01:00
4759ecac59 move src/api_schema/config.rs -> src/server/config.rs 2019-11-22 09:23:03 +01:00
ac21864dcf api/compat: drop more compat imports from api_schema.rs
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-11-21 14:36:28 +01:00
13f6a30f52 api/compat: drop api_handler submodule
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-11-21 14:18:41 +01:00
92ffe68022 api: BoxFut -> ApiFuture
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-11-21 14:16:37 +01:00
4da0705cc4 move api schema into proxmox::api crate
And leave some compat imports in api_schema.rs to get it to
build with minimal changes.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-11-21 14:14:54 +01:00
3de9361d12 use const api definitions 2019-11-21 13:32:09 +01:00
40acfdf04c avoid some clippy warnings 2019-10-26 11:42:05 +02:00
d26fde6986 avoid some clippy warnings 2019-10-25 18:44:51 +02:00
00d669295f clippy: use write_all in file logger
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-11 13:56:09 +02:00
5d6ebfb8dc update to tokio 0.2.0-alpha.4
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
149b9d997f src/server/state.rs: update to tokio alpha.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
a19a6f1b37 src/server/rest.rs: use tokio::timer::delay
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
9b7434ba15 src/tools/daemon.rs: switch to async
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
6e35cda060 src/server/state.rs: switch to async
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
df52ba5e45 src/server/rest.rs: switch to async
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
3a1a7cbdfc src/server/h2service.rs: switch to async
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
a079f39b40 src/server/command_socket.rs: switch to async
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-09-02 15:21:26 +02:00
0c3f5449d7 use new proxmox::tools::nodename 2019-08-03 17:06:23 +02:00
465990e139 update to nix 0.14, use code from proxmox:tools 2019-08-03 13:05:38 +02:00
fd40d69ae0 src/server/rest.rs: avoid unwrap 2019-07-03 12:00:43 +02:00
4ca8acb083 src/server/rest.rs: log peer address, use hyper MakeService 2019-07-03 11:54:35 +02:00
744903f874 daemon: remove last use of tools::read/write
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-07-01 10:39:13 +02:00
206d63a20b file download: avoid unnecessary copy 2019-06-28 07:07:52 +02:00
319e42552b src/server/h2service.rs: implement generic h2 service 2019-06-26 17:38:33 +02:00
a99f7ec987 tree-wide: use 'dyn' for all trait objects
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-06-07 13:13:48 +02:00
e8a799cf06 src/server/rest.rs: correctly verify json parameters 2019-05-24 09:10:59 +02:00
4dd886d9a7 src/server/rest.rs: improve error handling 2019-05-23 08:15:32 +02:00
27c2183ef4 src/server/rest.rs: do not log 1xx status codes as errors 2019-05-14 06:23:22 +02:00
3bbbece6a2 handle_async_api_request: put rpcenv into a Box
So that we can pass rpcenv into futures.
2019-05-09 18:01:24 +02:00
d5901112de src/server/formatter.rs: further cleanups and renaming ... 2019-05-09 13:28:26 +02:00
5b91995837 src/server/formatter.rs: rename format_result to format_data
To avoid confusions with Rust Result type.
2019-05-09 13:15:15 +02:00
1b61b80482 src/api2/admin/datastore/backup.rs: implement upload chunk 2019-05-09 13:06:09 +02:00
66f849d272 rc/api2/admin/datastore/h2upload.rs: implement BackupEnvironment
To pass arbitrary information/state to api methods.
2019-05-08 11:26:54 +02:00
010e7b80a8 src/server/rest.rs: use generics to pass RpcEnvironment 2019-05-08 11:09:01 +02:00
19b33e55af src/server/rest.rs: make handle_(a)sync_api_request public 2019-05-07 11:23:52 +02:00
16b5c3c80b RestEnvironment: derive Clone 2019-05-07 11:09:18 +02:00
e53d4dadaa move normalize_path to tools::normalize_uri_path 2019-05-07 09:44:34 +02:00
83f663b7a3 src/server/state.rs: use new BroadcastData helper 2019-04-30 10:21:48 +02:00
88aaa1841a use double-fork for reload
To ensure the new process' parent is pid 1, so systemd won't
complain about supervising a process it does not own.

Fixes the following log spam on reloads:
Apr 25 10:50:54 deb-dev systemd[1]: proxmox-backup.service: Supervising process 1625 which is not our child. We'll most likely not notice when it exits.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-04-25 11:02:12 +00:00
30150eef0f use service Type=notify
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-04-25 11:01:33 +00:00
efd898a71c tools/daemon: add sd_notify wrapper
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-04-25 11:01:28 +00:00
e6bdfe0674 api_schema: allow generic api handler functions
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-04-16 11:22:23 +02:00
3e4290e956 src/server/command_socket.rs: check control socket permissions 2019-04-11 10:51:59 +02:00
04f7276b1a tools/daemon: dup the TcpListener file descriptor
Now that we let hyper shutdown gracefully we need an owned
version of the listening socket to prevent it from closing
before running the reload preparations.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-04-10 15:20:10 +02:00
dfb73ee286 src/server/worker_task.rs: implement abort_worker (via command_socket) 2019-04-10 12:42:24 +02:00
1662975b70 src/server/command_socket.rs: correctly handle/spawn handle parallel connections 2019-04-10 11:05:00 +02:00
116990f264 src/server/worker_task.rs: use abstract socket 2019-04-10 09:03:17 +02:00
203b64ee92 start hyper server using with_graceful_shutdown()
Without, hyper keeps some futures running, and the server does not
correctly shutdown.
2019-04-10 08:24:32 +02:00
230d6ebc2a src/server/command_socket.rs: code cleanup - fix error message 2019-04-09 12:47:42 +02:00
1432561044 src/server/command_socket.rs: implement auto_remove flag
Remove the socket file on close.
2019-04-09 11:47:23 +02:00
8c4656ea04 src/server/command_socket.rs: simple command socket 2019-04-08 17:59:39 +02:00
b9e9f05aaf src/tools/daemon.rs: use new ServerState handler 2019-04-08 14:00:23 +02:00
9761b81b84 implement server state/signal handling, depend on tokio-signal 2019-04-08 13:59:07 +02:00
71d03f1ef4 src/tools/file_logger.rs: fix test 2019-04-06 11:24:37 +02:00
ff995ce0e1 src/server.rs: improve crate layout 2019-04-06 09:17:25 +02:00
e3e5ef3929 src/tools/file_logger.rs: new - accept AsRef<Path> 2019-04-03 14:13:33 +02:00
1ee4442d87 src/tools/file_logger.rs: change timestamp format to rfc3339 2019-04-03 08:58:43 +02:00
edc588857e add global var to indicate server shutdown requests 2019-04-01 12:05:11 +02:00
c76ceea941 src/server/rest.rs: use formatter to encode errors 2019-04-01 08:04:12 +02:00
24c023fe47 src/server/rest.rs: generate csrf token if we have a valid ticket
This is important if the user reloads the browser page.
2019-04-01 07:52:30 +02:00
022b626bc0 src/server/rest.rs: correctly extract content type 2019-03-19 12:50:15 +01:00
50e95f7c39 daemon: simplify daemon creation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-03-19 12:12:54 +01:00
3aa2dbc857 tools: daemon: rename some structs
Reloadable resources are now 'Reloadable' instead of
'ReexecContinue'.

The struct handling the reload is a 'Reloader', not a
'ReexecStore'.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-03-19 11:09:46 +01:00
14ed3eb57c tools: implement ReexecContinue for tokio's TcpListener
This is the only thing we currently need to keep alive for
reloads.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-03-18 12:29:18 +01:00
66c138a51a tools: daemon: add a default signalfd helper
Proxy and daemon for now just want to handle reload via
`SIGHUP`, so provide a helper creating the signalfd stream
doing that - this is simply a filtered stream which passes
the remaining signals through, so it can be used exactly
like the signalfd stream could before to add more signals.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-03-18 12:29:18 +01:00
946995d984 tools: add daemon helpers
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-03-18 12:29:18 +01:00
6dd8bfb84b src/tools/ticket.rs: define const TICKET_LIFETIME 2019-03-05 12:56:21 +01:00
b9febc5f1c src/tools/file_logger.rs: class to log into files 2019-03-01 09:34:29 +01:00
9b4e1de1c0 rc/server/rest.rs: allow to pass parameters as application/json 2019-02-27 12:37:53 +01:00
3dc99a5049 cleanup
Error::from is already a function taking 1 parameter,
there's no need to wrap it with `|e| Error::from(e)`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-02-18 13:21:27 +01:00
435615a34a src/server/rest.rs: correctly insert NoLogExtension() 2019-02-18 06:54:12 +01:00
bc6fa1684e src/server/rest.rs: get_index() include username and CSRF token
When we have an valid ticket. Also delay get_index() if called with
an invalid ticket.
2019-02-17 19:28:32 +01:00
c4c7466024 src/server/rest.rs: factor our normalize_path() 2019-02-17 17:31:53 +01:00
fce8be6fe1 src/server/rest.rs: improve logs for unauthorized request 2019-02-17 17:18:44 +01:00
b1c1c468ee improve api_schema module structure 2019-02-17 10:16:33 +01:00
304bfa59a8 rename src/api to src/api_schema 2019-02-17 09:59:20 +01:00
124b26b892 cleanup auth code, verify CSRF prevention token 2019-02-16 15:52:55 +01:00
1aff635a23 server/rest.rs: add method to log message 2019-02-15 10:16:12 +01:00
1314000db7 server/rest.rs: log full error messages 2019-02-15 09:55:12 +01:00
8daf9fd839 server/rest.rs: use a protocol extension to avoid double log
Instead of modifying the response header itself.
2019-02-14 16:04:24 +01:00
9bbd574fba avoid double logging of proxied requests 2019-02-14 13:28:41 +01:00
e683d9ccb7 src/server/rest.rs: log failed requests 2019-02-14 13:07:34 +01:00
50ff21da59 src/client/http_client.rs: try to login
use an environment var to store passphrase (PBS_PASSWORD)
2019-02-13 14:31:43 +01:00
fe3b25029b remove some rather inconvenient debug output
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-02-04 15:34:38 +01:00
9707fdadd7 implement relead_timezone flag 2019-02-01 10:04:46 +01:00
5d63509787 delay unauthorized request (rate limit) 2019-01-31 14:34:21 +01:00
8f75d998be move http error class to router.rs 2019-01-31 13:22:30 +01:00
0ef7c190e1 server/rest.rs: verify auth cookie 2019-01-31 12:22:00 +01:00
200b5b87ea Utils.js: fix cookie handling
Use unsecure cookie foör testing.
2019-01-31 10:08:08 +01:00
1701fd9bd4 api2/access.rs: add ticket api 2019-01-30 15:16:10 +01:00
c4f2b212c5 server/rest.rs: simplify proxy code
Only pass neccessary parameters.
2019-01-28 18:22:16 +01:00
8ec1299ab3 server/rest.rs: implement proxy_sync_api_request 2019-01-28 18:06:42 +01:00
1aa3b197a6 server/rest.rs: add proxy_sync_api_request() dummy 2019-01-28 17:30:39 +01:00
4e5a5728cb server/formatter.rs: fix extjs error format 2019-01-28 13:44:48 +01:00
08e45e3573 src/bin/proxmox-backup-proxy.rs: implement unpriviledged server
We want to run the public server as user www-data. Requests needing
root priviledges needs to be proxied to the proxmox-backup.service, which
now listens to 127.0.0.1:82.
2019-01-28 13:29:58 +01:00
42e06fc5ca RpcEnvironment: implement set_user() and get_user() 2019-01-27 10:52:26 +01:00
23db39488f RpcEnvironment: add environment type enum RpcEnvironmentType 2019-01-27 10:33:42 +01:00
084ccdd590 also pass rpcenv to async handlers 2019-01-27 10:18:52 +01:00
a0a545c720 move rpc environment implementation to separate files 2019-01-26 15:08:02 +01:00
32f3db27bd api: pass RpcEnvirnment to api handlers 2019-01-26 14:50:37 +01:00
b1be01218a server/rest.rs: fake login cookie 2019-01-23 12:49:10 +01:00
c643065864 rename api3 back to api2
There is no real need to change the path, so using api2 we can reuse
all helpers (like tools from proxmox widget toolkit).
2019-01-22 12:10:38 +01:00
e35404deb7 remove crate tokio-codec (seems to be part of tokio now) 2019-01-20 14:28:06 +01:00
85722a8492 api/router.rs: rename ApiUploadMethod to ApiAsyncMethod
We can use this for uploads and downloads ...
2019-01-19 16:42:43 +01:00
6e219aefd3 api3/admin/datastore/upload_catar.rs: verify content type ("application/x-proxmox-backup-catar") 2019-01-17 12:43:29 +01:00
90e1d858e0 api/router.rs: return Result in upload handler 2019-01-17 12:03:38 +01:00
148b327e63 server/rest.rs: correctly pass query/url parameters 2019-01-16 13:58:36 +01:00
c36fa61287 api3/admin/datastore/upload_catar.rs: implement upload future 2019-01-15 11:38:26 +01:00
c1582dcf39 api/router.rs: allow different types of api methods
Added a prototype for file/backup uploads.
2019-01-14 12:26:04 +01:00
ac1397dedb rest: rename utf-8-checked 'bytes' to 'utf8'
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-01-08 14:22:43 +01:00
3cd4bb8a63 rest: don't copy the body
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2019-01-08 14:21:54 +01:00
2ad4db5d13 simplify formatter code 2018-12-05 18:22:56 +01:00
53acf7490b add output formatter 2018-12-05 12:43:22 +01:00
0f30b2b4c4 move src/api/server.rs -> src/server/rest.rs 2018-12-05 10:16:23 +01:00
fc45b741cb start the GUI 2018-12-04 17:53:10 +01:00
185f4301dc set content type for static file download 2018-12-02 11:00:52 +01:00
4892b32829 fix file download, listen to 0.0.0.0 2018-12-01 15:21:25 +01:00
b53007523d remove www/pbs-index.html.tt, hardcode into rust for now 2018-12-01 13:37:49 +01:00
dd4b1a797b router: no need to use Fn (fn also works for static closures) 2018-11-16 11:12:00 +01:00
1716112285 handle uri parameters correctly 2018-11-16 09:15:33 +01:00
5106bbc70e allow closure handlers 2018-11-15 17:47:59 +01:00
1ac1f7fd24 cleanup module names 2018-11-15 17:07:10 +01:00
746 changed files with 1234243 additions and 9678 deletions

5
.cargo/config.toml Normal file
View File

@ -0,0 +1,5 @@
# [source]
# [source.debian-packages]
# directory = "/usr/share/cargo/registry"
# [source.crates-io]
# replace-with = "debian-packages"

4
.gitignore vendored
View File

@ -2,3 +2,7 @@
/*/target
Cargo.lock
**/*.rs.bk
/*.buildinfo
/*.changes
/build
/*-deb

View File

@ -1,10 +1,162 @@
[workspace]
members = [
"proxmox",
"proxmox-access-control",
"proxmox-acme",
"proxmox-acme-api",
"proxmox-api-macro",
"proxmox-apt",
"proxmox-apt-api-types",
"proxmox-async",
"proxmox-auth-api",
"proxmox-base64",
"proxmox-borrow",
"proxmox-client",
"proxmox-compression",
"proxmox-config-digest",
"proxmox-daemon",
"proxmox-dns-api",
"proxmox-http",
"proxmox-http-error",
"proxmox-human-byte",
"proxmox-io",
"proxmox-lang",
"proxmox-ldap",
"proxmox-log",
"proxmox-login",
"proxmox-metrics",
"proxmox-network-api",
"proxmox-notify",
"proxmox-openid",
"proxmox-product-config",
"proxmox-rest-server",
"proxmox-router",
"proxmox-rrd",
"proxmox-rrd-api-types",
"proxmox-schema",
"proxmox-section-config",
"proxmox-sendmail",
"proxmox-serde",
"proxmox-shared-cache",
"proxmox-shared-memory",
"proxmox-simple-config",
"proxmox-sortable-macro",
"proxmox-subscription",
"proxmox-sys",
"proxmox-syslog-api",
"proxmox-systemd",
"proxmox-tfa",
"proxmox-time",
"proxmox-time-api",
"proxmox-uuid",
"proxmox-worker-task",
"pbs-api-types",
]
exclude = [
"build",
]
resolver = "2"
[workspace.package]
authors = ["Proxmox Support Team <support@proxmox.com>"]
edition = "2021"
license = "AGPL-3"
repository = "https://git.proxmox.com/?p=proxmox.git"
homepage = "https://proxmox.com"
exclude = [ "debian" ]
rust-version = "1.82"
[workspace.dependencies]
# any features enabled here are enabled on all members using 'workspace = true'!
# external dependencies
anyhow = "1.0"
base32 = "0.4"
base64 = "0.22"
bitflags = "2.4"
bytes = "1.0"
const_format = "0.2"
crc32fast = "1"
crossbeam-channel = "0.5"
endian_trait = "0.6"
env_logger = "0.11"
flate2 = "1.0"
foreign-types = "0.3"
form_urlencoded = "1.1"
futures = "0.3"
handlebars = "5"
hex = "0.4"
http = "1"
http-body = "1"
http-body-util = "0.1"
hyper = "1"
hyper-util = "0.1.12"
ldap3 = { version = "0.11", default-features = false }
lettre = "0.11.1"
libc = "0.2.107"
log = "0.4.17"
mail-parser = "0.11"
native-tls = "0.2"
nix = "0.29"
openssl = "0.10"
pam-sys = "0.5"
percent-encoding = "2.1"
pin-utils = "0.1.0"
proc-macro2 = "1.0"
quote = "1.0"
regex = "1.5"
serde = "1.0"
serde_cbor = "0.11.1"
serde_json = "1.0"
serde_plain = "1.0"
syn = { version = "2", features = [ "full", "visit-mut" ] }
sync_wrapper = "1"
tar = "0.4"
tokio = "1.6"
tokio-openssl = "0.6.1"
tokio-stream = "0.1.0"
tower-service = "0.3.0"
tracing = "0.1"
tracing-journald = "0.3.1"
tracing-log = { version = "0.2", default-features = false }
tracing-subscriber = "0.3.16"
url = "2.2"
walkdir = "2"
zstd = "0.13"
# workspace dependencies
proxmox-access-control = { version = "0.2.5", path = "proxmox-access-control" }
proxmox-acme = { version = "1.0.0", path = "proxmox-acme", default-features = false }
proxmox-api-macro = { version = "1.4.0", path = "proxmox-api-macro" }
proxmox-apt-api-types = { version = "2.0.0", path = "proxmox-apt-api-types" }
proxmox-auth-api = { version = "1.0.0", path = "proxmox-auth-api" }
proxmox-async = { version = "0.5.0", path = "proxmox-async" }
proxmox-base64 = { version = "1.0.0", path = "proxmox-base64" }
proxmox-compression = { version = "1.0.0", path = "proxmox-compression" }
proxmox-daemon = { version = "1.0.0", path = "proxmox-daemon" }
proxmox-http = { version = "1.0.0", path = "proxmox-http" }
proxmox-http-error = { version = "1.0.0", path = "proxmox-http-error" }
proxmox-human-byte = { version = "1.0.0", path = "proxmox-human-byte" }
proxmox-io = { version = "1.2.0", path = "proxmox-io" }
proxmox-lang = { version = "1.5", path = "proxmox-lang" }
proxmox-log = { version = "1.0.0", path = "proxmox-log" }
proxmox-login = { version = "1.0.0", path = "proxmox-login" }
proxmox-product-config = { version = "1.0.0", path = "proxmox-product-config" }
proxmox-config-digest = { version = "1.0.0", path = "proxmox-config-digest" }
proxmox-rest-server = { version = "1.0.0", path = "proxmox-rest-server" }
proxmox-router = { version = "3.2.2", path = "proxmox-router" }
proxmox-schema = { version = "4.1.0", path = "proxmox-schema" }
proxmox-section-config = { version = "3.1.0", path = "proxmox-section-config" }
proxmox-sendmail = { version = "1.0.0", path = "proxmox-sendmail" }
proxmox-serde = { version = "1.0.0", path = "proxmox-serde", features = [ "serde_json" ] }
proxmox-shared-memory = { version = "1.0.0", path = "proxmox-shared-memory" }
proxmox-sortable-macro = { version = "0.1.3", path = "proxmox-sortable-macro" }
proxmox-sys = { version = "1.0.0", path = "proxmox-sys" }
proxmox-systemd = { version = "1.0.0", path = "proxmox-systemd" }
proxmox-tfa = { version = "6.0.0", path = "proxmox-tfa" }
proxmox-time = { version = "2.1.0", path = "proxmox-time" }
proxmox-uuid = { version = "1.1.0", path = "proxmox-uuid" }
proxmox-worker-task = { version = "1.0.0", path = "proxmox-worker-task" }
[workspace.dependencies.http_1]
package = "http"
version = "1"

View File

@ -1,6 +1,7 @@
# Shortcut for common operations:
CRATES=proxmox proxmox-api-macro proxmox-http proxmox-sortable-macro
# see proxmox-backup if we ever want to support other prefixes
CRATES != echo pbs-*/Cargo.toml proxmox-*/Cargo.toml | sed -e 's|/Cargo.toml||g'
# By default we just run checks:
.PHONY: all
@ -11,6 +12,11 @@ deb: $(foreach c,$(CRATES), $c-deb)
echo $(foreach c,$(CRATES), $c-deb)
lintian build/*.deb
.PHONY: dsc
dsc: $(foreach c,$(CRATES), $c-dsc)
echo $(foreach c,$(CRATES), $c-dsc)
lintian build/*.dsc
.PHONY: autopkgtest
autopkgtest: $(foreach c,$(CRATES), $c-autopkgtest)
@ -24,10 +30,20 @@ dinstall:
./build.sh $*
touch $@
%-dsc:
BUILDCMD='dpkg-buildpackage -S -us -uc -d' NOTEST=1 ./build.sh $*
touch $@
%-autopkgtest:
autopkgtest build/$* build/*.deb -- null
touch $@
.PHONY: list-packages
list-packages:
@for p in $(CRATES); do \
echo "librust-$$p-dev"; \
done
.PHONY: check
check:
cargo test
@ -51,7 +67,8 @@ doc:
.PHONY: clean
clean:
cargo clean
rm -rf build *-deb *-autopkgtest
rm -rf build/
rm -f -- *-deb *-dsc *-autopkgtest *.build *.buildinfo *.changes
.PHONY: update
update:
@ -62,5 +79,72 @@ update:
dcmd --deb rust-$*_*.changes \
| grep -v '.changes$$' \
| tar -cf "$@.tar" -T-; \
cat "$@.tar" | ssh -X repoman@repo.proxmox.com upload --product devel --dist buster; \
cat "$@.tar" | ssh -X repoman@repo.proxmox.com upload --product devel --dist bullseye
cat "$@.tar" | ssh -X repoman@repo.proxmox.com upload --product devel --dist trixie
%-install:
rm -rf build/install/$*
mkdir -p build/install/$*
BUILDDIR=build/install/$* BUILDCMD=/usr/bin/true NOCONTROL=1 ./build.sh "$*" || true
version="$$(dpkg-parsechangelog -l $*/debian/changelog -SVersion | sed -e 's/-.*//')"; \
install -m755 -Dd "$(DESTDIR)/usr/share/cargo/registry/$*-$${version}"; \
rm -rf "$(DESTDIR)/usr/share/cargo/registry/$*-$${version}"; \
mv "build/install/$*/$*" \
"$(DESTDIR)/usr/share/cargo/registry/$*-$${version}"; \
mv "$(DESTDIR)/usr/share/cargo/registry/$*-$${version}/debian/cargo-checksum.json" \
"$(DESTDIR)/usr/share/cargo/registry/$*-$${version}/.cargo-checksum.json"; \
rm -rf "$(DESTDIR)/usr/share/cargo/registry/$*-$${version}/debian" \
.PHONY: install
install: $(foreach c,$(CRATES), $c-install)
%-install-overlay: %-install
version="$$(dpkg-parsechangelog -l $*/debian/changelog -SVersion | sed -e 's/-.*//')"; \
setfattr -n trusted.overlay.opaque -v y \
"$(DESTDIR)/usr/share/cargo/registry/$*-$${version}"
install -m755 -Dd $(DESTDIR)/usr/lib/extension-release.d
echo 'ID=_any' >$(DESTDIR)/usr/lib/extension-release.d/extension-release.$*
.PHONY: install-overlay
install-overlay: $(foreach c,$(CRATES), $c-install-overlay)
# To make sure a sysext *replaces* a crate, rather than "merging" with it, we
# need to be able to set the 'trusted.overlay.opaque' xattr. Since we cannot do
# this as a user, we utilize `fakeroot` which keeps track of this for us, and
# turn the final directory into an 'erofs' file system image.
#
# The reason is that if a crate gets changed like this:
#
# old:
# src/foo.rs
# new:
# src/foo/mod.rs
#
# if its /usr/share/cargo/registry/$crate-$version directory was not marked as
# "opaque", the merged file system would end up with both
#
# src/foo.rs
# src/foo/mod.rs
#
# together.
#
# See https://docs.kernel.org/filesystems/overlayfs.html
%-sysext:
fakeroot $(MAKE) $*-sysext-do
%-sysext-do:
rm -f extensions/$*.raw
rm -rf build/sysext/$*
rm -rf build/install/$*
$(MAKE) DESTDIR=build/sysext/$* $*-install-overlay
mkdir -p extensions
mkfs.erofs extensions/$*.raw build/sysext/$*
sysext:
fakeroot $(MAKE) sysext-do
sysext-do:
rm -f extensions/proxmox-workspace.raw
[ -n "$(NOCLEAN)" ] || rm -rf build/sysext/workspace
$(MAKE) DESTDIR=build/sysext/workspace $(foreach c,$(CRATES), $c-install)
install -m755 -Dd build/sysext/workspace/usr/lib/extension-release.d
echo 'ID=_any' >build/sysext/workspace/usr/lib/extension-release.d/extension-release.proxmox-workspace
mkdir -p extensions
mkfs.erofs extensions/proxmox-workspace.raw build/sysext/workspace

153
README.md Normal file
View File

@ -0,0 +1,153 @@
# Local cargo config
This repository ships with a `.cargo/config.toml` that replaces the crates.io
registry with packaged crates located in `/usr/share/cargo/registry`.
A similar config is also applied building with `dh_cargo`. Cargo.lock needs to
be deleted when switching between packaged crates and crates.io, since the
checksums are not compatible.
To reference new dependencies (or updated versions) that are not yet packaged,
the dependency needs to point directly to a path or git source.
# Quickly installing all packages from apt
To a void too many manual installations when `mk-build-deps` etc. fail, a quick
way to install all the main packages of this workspace is to run:
# apt install $(make list-packages)
# Steps for Releases
- Run `./bump.sh <CRATE> [patch|minor|major|<VERSION>]`
- Fill out changelog
- Confirm bump commit
- Build packages with `make <crate>-deb`.
- Don't forget to commit updated d/control!
# Adding Crates
1. At the top level:
- Generate the crate: `cargo new --lib the-name`
- Sort the crate into `Cargo.toml`'s `workspace.members`
2. In the new crate's `Cargo.toml`:
- In `[package]` set:
authors.workspace = true
edition.workspace = true
exclude.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
If a separate ``exclude`` is need it, separate it out as its own
block above the inherited fields.
- Add a meaningful `description`
- Copy `debian/copyright` and `debian/debcargo.toml` from another subcrate.
3. In the new crate\'s `lib.rs`, add the following preamble on top:
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
4. Ideally (but optionally) in the new crate\'s `lib.rs`, add the following
preamble on top as well:
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(missing_docs)]
# Adding a new Dependency
1. At the top level:
- Add it to `[workspace.dependencies]` specifying the version and any
features that should be enabled throughout the workspace
2. In each member\'s `Cargo.toml`:
- Add it to the desired dependencies section with `workspace = true` and no
version specified.
- If this member requires additional features, add only the extra features
to the member dependency.
# Updating a Dependency\'s Version
1. At the top level:
- Bump the version in `[workspace.dependencies]` as desired.
- Check for deprecations or breakage throughout the workspace.
# Notes on Workspace Inheritance
Common metadata (like authors, license, ..) are inherited throughout the
workspace. If new fields are added that are identical for all crates, they
should be defined in the top-level `Cargo.toml` file\'s `[workspace.package]`
section, and inherited in all members explicitly by setting `FIELD.workspace =
true` in the member\'s `[package]` section.
Dependency information is also inherited throughout the workspace, allowing a
single dependency specification in the top-level `Cargo.toml` file to be used
by all members.
Some restrictions apply:
- features can only be added in members, never removed (this includes
`default_features = false`!)
- the base feature set at the workspace level should be the minimum (possibly
empty!) set required by all members
- workspace dependency specifications cannot include `optional`
- if needed, the `optional` flag needs to be set at the member level when
using a workspace dependency
# Working with *other* projects while changing to *single crates here*
When crates from this workspace need changes caused by requirements in projects
*outside* of this repository, it can often be annoying to keep building and
installing `.deb` files.
Additionally, doing so often requires complete rebuilds as cargo will not pick
up *file* changes of external dependencies.
One way to fix this is by actually changing the version. Since we cut away
anything starting at the first hyphen in the version, we need to use a `+`
(build metadata) version suffix.
Eg. turn `5.0.0` into `5.0.0+test8`.
There are 2 faster ways:
## Adding a `#[patch.crates-io]` section to the other project.
Note, however, that this requires *ALL* crates from this workspace to be listed,
otherwise multiple conflicting versions of the same crate AND even the same
numerical *version* might be built, causing *weird* errors.
The advantage, however, is that `cargo` will pick up on file changes and rebuild
the crate on changes.
## An in-between: system extensions
An easy way to quickly get the new package "installed" *temporarily*, such that
real apt package upgrades are unaffected is as a system-extension.
The easiest way — if no other extensions are used — is to just symlink the
`extensions/` directory to `/run` as root via:
```
# ln -s ${THIS_DIR}/extensions /run/extensions
```
This does not persist across reboots.
(Note: that the `extensions/` directory does not need to exist for the above to
work.)
Once this is done, trying a new version of a crate works by:
1. Bump the version: eg. `5.0.0+test8` -> `5.0.0+test9`
While this is technically optional (the sysext would then *replace*
(temporarily) the installed version as long as the sysext is active), just
like with `.deb` files, not doing this causes `cargo` to consider the crate
to be unchanged and it will not rebuild its code.
2. here: `$ make ${crate}-sysext` (rebuilds `extensions/${crate}.raw`)
3. as root: `# systemd-sysext refresh` (activates current extensions images)
4. in the other project: `$ cargo update && cargo build`
In the last step, cargo sees that there's a newer version of the crate available
and use that.

View File

@ -1,22 +0,0 @@
Local cargo config
==================
This repository ships with a ``.cargo/config`` that replaces the crates.io
registry with packaged crates located in ``/usr/share/cargo/registry``.
A similar config is also applied building with dh_cargo. Cargo.lock needs to be
deleted when switching between packaged crates and crates.io, since the
checksums are not compatible.
To reference new dependencies (or updated versions) that are not yet packaged,
the dependency needs to point directly to a path or git source.
Steps for Releases
==================
- Cargo.toml updates:
- Bump all modified crate versions.
- Update all the other crates' Cargo.toml to depend on the new versions if
required, then bump their version as well if not already done.
- Update debian/changelog files in all the crates updated above.
- Build packages with `make deb`.

View File

@ -7,21 +7,33 @@ export RUSTC=/usr/bin/rustc
CRATE=$1
BUILDCMD=${BUILDCMD:-"dpkg-buildpackage -b -uc -us"}
BUILDDIR="${BUILDDIR:-"build"}"
mkdir -p build
echo system >build/rust-toolchain
rm -rf "build/${CRATE}"
mkdir -p "${BUILDDIR}"
echo system >"${BUILDDIR}"/rust-toolchain
rm -rf ""${BUILDDIR}"/${CRATE}"
CONTROL="$PWD/${CRATE}/debian/control"
if [ -e "$CONTROL" ]; then
# check but only warn, debcargo fails anyway if crates are missing
dpkg-checkbuilddeps $PWD/${CRATE}/debian/control || true
rm -f "$PWD/${CRATE}/debian/control"
[ "x$NOCONTROL" = 'x' ] && rm -f "$PWD/${CRATE}/debian/control"
fi
debcargo package --config "$PWD/${CRATE}/debian/debcargo.toml" --changelog-ready --no-overlay-write-back --directory "$PWD/build/${CRATE}" "${CRATE}" "$(dpkg-parsechangelog -l "${CRATE}/debian/changelog" -SVersion | sed -e 's/-.*//')"
cd "build/${CRATE}"
debcargo package \
--config "$PWD/${CRATE}/debian/debcargo.toml" \
--changelog-ready \
--no-overlay-write-back \
--directory "$PWD/"${BUILDDIR}"/${CRATE}" \
"${CRATE}" \
"$(dpkg-parsechangelog -l "${CRATE}/debian/changelog" -SVersion | sed -e 's/-.*//')"
cd ""${BUILDDIR}"/${CRATE}"
rm -f debian/source/format.debcargo.hint
${BUILDCMD}
cp debian/control "$CONTROL"
# needs all crates build-dependencies, which can be more than what debcargo assembles.
[ "x$NOTEST" = "x" ] && $CARGO test --all-features --all-targets
[ "x$NOCONTROL" = "x" ] && cp debian/control "$CONTROL"

44
bump.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
package=$1
if [[ -z "$package" ]]; then
echo "USAGE:"
echo -e "\t bump.sh <crate> [patch|minor|major|<version>]"
echo ""
echo "Defaults to bumping patch version by 1"
exit 0
fi
cargo_set_version="$(command -v cargo-set-version)"
if [[ -z "$cargo_set_version" || ! -x "$cargo_set_version" ]]; then
echo 'bump.sh requires "cargo set-version", provided by "cargo-edit".'
exit 1
fi
if [[ ! -e "$package/Cargo.toml" ]]; then
echo "Invalid crate '$package'"
exit 1
fi
version=$2
if [[ -z "$version" ]]; then
version="patch"
fi
case "$version" in
patch|minor|major)
bump="--bump"
;;
*)
bump=
;;
esac
cargo_toml="$package/Cargo.toml"
changelog="$package/debian/changelog"
cargo set-version -p "$package" $bump "$version"
version="$(cargo metadata --format-version=1 | jq ".packages[] | select(.name == \"$package\").version" | sed -e 's/\"//g')"
DEBFULLNAME="Proxmox Support Team" DEBEMAIL="support@proxmox.com" dch --no-conf --changelog "$changelog" --newversion "$version-1" --distribution stable
git commit --edit -sm "bump $package to $version-1" Cargo.toml "$cargo_toml" "$changelog"

26
pbs-api-types/Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "pbs-api-types"
version = "1.0.0"
license.workspace = true
authors.workspace = true
edition.workspace = true
description = "API types for Proxmox Backup Server"
exclude.workspace = true
[dependencies]
anyhow.workspace = true
const_format.workspace = true
hex.workspace = true
percent-encoding.workspace = true
regex.workspace = true
serde.workspace = true
serde_plain.workspace = true
proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
proxmox-apt-api-types.workspace = true
proxmox-human-byte.workspace = true
proxmox-lang.workspace=true
proxmox-schema = { workspace = true, features = [ "api-macro" ] }
proxmox-serde.workspace = true
proxmox-time.workspace = true
proxmox-uuid = { workspace = true, features = [ "serde" ] }

View File

@ -0,0 +1,31 @@
rust-pbs-api-types (1.0.0-1) trixie; urgency=medium
* re-build for Debian Trixie based releases.
-- Proxmox Support Team <support@proxmox.com> Fri, 30 May 2025 18:51:13 +0200
rust-pbs-api-types (0.2.2) bookworm; urgency=medium
* add garbage collection atime safety check flag.
* add option to set GC chunk cleanup atime cutoff.
* fix #5379: add `default` field for all realm types.
* sync: add sync encrypted/verified snapshots only flags.
* add garbage collection cache capacity tuning option.
-- Proxmox Support Team <support@proxmox.com> Sat, 05 Apr 2025 17:22:09 +0200
rust-pbs-api-types (0.2.1) bookworm; urgency=medium
* metrics: relax influx organization and bucket length
* tape backup job: add worker threads option
* user: add boolean-schema for regenerate-api-token flag
-- Proxmox Support Team <support@proxmox.com> Wed, 02 Apr 2025 15:34:49 +0200
rust-pbs-api-types (0.2.0) bookworm; urgency=medium
* imported from proxmox-backup repository
-- Proxmox Support Team <support@proxmox.com> Wed, 22 Jan 2025 09:40:51 +0100

View File

@ -0,0 +1,68 @@
Source: rust-pbs-api-types
Section: rust
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-sequence-cargo
Build-Depends-Arch: cargo:native <!nocheck>,
rustc:native <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-anyhow-1+default-dev <!nocheck>,
librust-const-format-0.2+default-dev <!nocheck>,
librust-hex-0.4+default-dev <!nocheck>,
librust-percent-encoding-2+default-dev (>= 2.1-~~) <!nocheck>,
librust-proxmox-apt-api-types-2+default-dev <!nocheck>,
librust-proxmox-auth-api-1+api-types-dev <!nocheck>,
librust-proxmox-auth-api-1+default-dev <!nocheck>,
librust-proxmox-human-byte-1+default-dev <!nocheck>,
librust-proxmox-lang-1+default-dev (>= 1.5-~~) <!nocheck>,
librust-proxmox-schema-4+api-macro-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-serde-1+default-dev <!nocheck>,
librust-proxmox-serde-1+serde-json-dev <!nocheck>,
librust-proxmox-time-2+default-dev (>= 2.1.0-~~) <!nocheck>,
librust-proxmox-uuid-1+default-dev (>= 1.1.0-~~) <!nocheck>,
librust-proxmox-uuid-1+serde-dev (>= 1.1.0-~~) <!nocheck>,
librust-regex-1+default-dev (>= 1.5-~~) <!nocheck>,
librust-serde-1+default-dev <!nocheck>,
librust-serde-plain-1+default-dev <!nocheck>
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.7.0
Vcs-Git: git://git.proxmox.com/git/proxmox.git
Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
X-Cargo-Crate: pbs-api-types
Rules-Requires-Root: no
Package: librust-pbs-api-types-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-anyhow-1+default-dev,
librust-const-format-0.2+default-dev,
librust-hex-0.4+default-dev,
librust-percent-encoding-2+default-dev (>= 2.1-~~),
librust-proxmox-apt-api-types-2+default-dev,
librust-proxmox-auth-api-1+api-types-dev,
librust-proxmox-auth-api-1+default-dev,
librust-proxmox-human-byte-1+default-dev,
librust-proxmox-lang-1+default-dev (>= 1.5-~~),
librust-proxmox-schema-4+api-macro-dev (>= 4.1.0-~~),
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~),
librust-proxmox-serde-1+default-dev,
librust-proxmox-serde-1+serde-json-dev,
librust-proxmox-time-2+default-dev (>= 2.1.0-~~),
librust-proxmox-uuid-1+default-dev (>= 1.1.0-~~),
librust-proxmox-uuid-1+serde-dev (>= 1.1.0-~~),
librust-regex-1+default-dev (>= 1.5-~~),
librust-serde-1+default-dev,
librust-serde-plain-1+default-dev
Provides:
librust-pbs-api-types+default-dev (= ${binary:Version}),
librust-pbs-api-types-1-dev (= ${binary:Version}),
librust-pbs-api-types-1+default-dev (= ${binary:Version}),
librust-pbs-api-types-1.0-dev (= ${binary:Version}),
librust-pbs-api-types-1.0+default-dev (= ${binary:Version}),
librust-pbs-api-types-1.0.0-dev (= ${binary:Version}),
librust-pbs-api-types-1.0.0+default-dev (= ${binary:Version})
Description: API types for Proxmox Backup Server - Rust source code
Source code for Debianized Rust crate "pbs-api-types"

View File

@ -0,0 +1,18 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Files:
*
Copyright: 2019 - 2024 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/>.

View File

@ -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"

332
pbs-api-types/src/acl.rs Normal file
View File

@ -0,0 +1,332 @@
use std::str::FromStr;
use const_format::concatcp;
use serde::de::{value, IntoDeserializer};
use serde::{Deserialize, Serialize};
use proxmox_lang::constnamedbitmap;
use proxmox_schema::{
api, const_regex, ApiStringFormat, BooleanSchema, EnumEntry, Schema, StringSchema,
};
use crate::PROXMOX_SAFE_ID_REGEX_STR;
const_regex! {
pub ACL_PATH_REGEX = concatcp!(r"^(?:/|", r"(?:/", PROXMOX_SAFE_ID_REGEX_STR, ")+", r")$");
}
// define Privilege bitfield
constnamedbitmap! {
/// Contains a list of privilege name to privilege value mappings.
///
/// The names are used when displaying/persisting privileges anywhere, the values are used to
/// allow easy matching of privileges as bitflags.
PRIVILEGES: u64 => {
/// Sys.Audit allows knowing about the system and its status
PRIV_SYS_AUDIT("Sys.Audit");
/// Sys.Modify allows modifying system-level configuration
PRIV_SYS_MODIFY("Sys.Modify");
/// Sys.Modify allows to poweroff/reboot/.. the system
PRIV_SYS_POWER_MANAGEMENT("Sys.PowerManagement");
/// Datastore.Audit allows knowing about a datastore,
/// including reading the configuration entry and listing its contents
PRIV_DATASTORE_AUDIT("Datastore.Audit");
/// Datastore.Allocate allows creating or deleting datastores
PRIV_DATASTORE_ALLOCATE("Datastore.Allocate");
/// Datastore.Modify allows modifying a datastore and its contents
PRIV_DATASTORE_MODIFY("Datastore.Modify");
/// Datastore.Read allows reading arbitrary backup contents
PRIV_DATASTORE_READ("Datastore.Read");
/// Allows verifying a datastore
PRIV_DATASTORE_VERIFY("Datastore.Verify");
/// Datastore.Backup allows Datastore.Read|Verify and creating new snapshots,
/// but also requires backup ownership
PRIV_DATASTORE_BACKUP("Datastore.Backup");
/// Datastore.Prune allows deleting snapshots,
/// but also requires backup ownership
PRIV_DATASTORE_PRUNE("Datastore.Prune");
/// Permissions.Modify allows modifying ACLs
PRIV_PERMISSIONS_MODIFY("Permissions.Modify");
/// Remote.Audit allows reading remote.cfg and sync.cfg entries
PRIV_REMOTE_AUDIT("Remote.Audit");
/// Remote.Modify allows modifying remote.cfg
PRIV_REMOTE_MODIFY("Remote.Modify");
/// Remote.Read allows reading data from a configured `Remote`
PRIV_REMOTE_READ("Remote.Read");
/// Remote.DatastoreBackup allows creating new snapshots on remote datastores
PRIV_REMOTE_DATASTORE_BACKUP("Remote.DatastoreBackup");
/// Remote.DatastoreModify allows to modify remote datastores
PRIV_REMOTE_DATASTORE_MODIFY("Remote.DatastoreModify");
/// Remote.DatastorePrune allows deleting snapshots on remote datastores
PRIV_REMOTE_DATASTORE_PRUNE("Remote.DatastorePrune");
/// Sys.Console allows access to the system's console
PRIV_SYS_CONSOLE("Sys.Console");
/// Tape.Audit allows reading tape backup configuration and status
PRIV_TAPE_AUDIT("Tape.Audit");
/// Tape.Modify allows modifying tape backup configuration
PRIV_TAPE_MODIFY("Tape.Modify");
/// Tape.Write allows writing tape media
PRIV_TAPE_WRITE("Tape.Write");
/// Tape.Read allows reading tape backup configuration and media contents
PRIV_TAPE_READ("Tape.Read");
/// Realm.Allocate allows viewing, creating, modifying and deleting realms
PRIV_REALM_ALLOCATE("Realm.Allocate");
}
}
pub fn privs_to_priv_names(privs: u64) -> Vec<&'static str> {
PRIVILEGES
.iter()
.fold(Vec::new(), |mut priv_names, (name, value)| {
if value & privs != 0 {
priv_names.push(name);
}
priv_names
})
}
/// Admin always has all privileges. It can do everything except a few actions
/// which are limited to the 'root@pam` superuser
pub const ROLE_ADMIN: u64 = u64::MAX;
/// NoAccess can be used to remove privileges from specific (sub-)paths
pub const ROLE_NO_ACCESS: u64 = 0;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Audit can view configuration and status information, but not modify it.
pub const ROLE_AUDIT: u64 = 0
| PRIV_SYS_AUDIT
| PRIV_DATASTORE_AUDIT;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Datastore.Admin can do anything on the datastore.
pub const ROLE_DATASTORE_ADMIN: u64 = 0
| PRIV_DATASTORE_AUDIT
| PRIV_DATASTORE_MODIFY
| PRIV_DATASTORE_READ
| PRIV_DATASTORE_VERIFY
| PRIV_DATASTORE_BACKUP
| PRIV_DATASTORE_PRUNE;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Datastore.Reader can read/verify datastore content and do restore
pub const ROLE_DATASTORE_READER: u64 = 0
| PRIV_DATASTORE_AUDIT
| PRIV_DATASTORE_VERIFY
| PRIV_DATASTORE_READ;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Datastore.Backup can do backup and restore, but no prune.
pub const ROLE_DATASTORE_BACKUP: u64 = 0
| PRIV_DATASTORE_BACKUP;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Datastore.PowerUser can do backup, restore, and prune.
pub const ROLE_DATASTORE_POWERUSER: u64 = 0
| PRIV_DATASTORE_PRUNE
| PRIV_DATASTORE_BACKUP;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Datastore.Audit can audit the datastore.
pub const ROLE_DATASTORE_AUDIT: u64 = 0
| PRIV_DATASTORE_AUDIT;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Remote.Audit can audit the remote
pub const ROLE_REMOTE_AUDIT: u64 = 0
| PRIV_REMOTE_AUDIT;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Remote.Admin can do anything on the remote.
pub const ROLE_REMOTE_ADMIN: u64 = 0
| PRIV_REMOTE_AUDIT
| PRIV_REMOTE_MODIFY
| PRIV_REMOTE_READ;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Remote.SyncOperator can do read and prune on the remote.
pub const ROLE_REMOTE_SYNC_OPERATOR: u64 = 0
| PRIV_REMOTE_AUDIT
| PRIV_REMOTE_READ;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Remote.SyncPushOperator can read and push snapshots to the remote.
pub const ROLE_REMOTE_SYNC_PUSH_OPERATOR: u64 = 0
| PRIV_REMOTE_AUDIT
| PRIV_REMOTE_DATASTORE_BACKUP;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Remote.DatastorePowerUser can read and push snapshots to the remote, and prune owned snapshots
/// and groups but not create or remove namespaces.
pub const ROLE_REMOTE_DATASTORE_POWERUSER: u64 = 0
| PRIV_REMOTE_AUDIT
| PRIV_REMOTE_DATASTORE_BACKUP
| PRIV_REMOTE_DATASTORE_PRUNE;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Remote.DatastoreAdmin can read and push snapshots to the remote, prune owned snapshots
/// and groups, as well as create or remove namespaces.
pub const ROLE_REMOTE_DATASTORE_ADMIN: u64 = 0
| PRIV_REMOTE_AUDIT
| PRIV_REMOTE_DATASTORE_BACKUP
| PRIV_REMOTE_DATASTORE_MODIFY
| PRIV_REMOTE_DATASTORE_PRUNE;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Tape.Audit can audit the tape backup configuration and media content
pub const ROLE_TAPE_AUDIT: u64 = 0
| PRIV_TAPE_AUDIT;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Tape.Admin can do anything on the tape backup
pub const ROLE_TAPE_ADMIN: u64 = 0
| PRIV_TAPE_AUDIT
| PRIV_TAPE_MODIFY
| PRIV_TAPE_READ
| PRIV_TAPE_WRITE;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Tape.Operator can do tape backup and restore (but no configuration changes)
pub const ROLE_TAPE_OPERATOR: u64 = 0
| PRIV_TAPE_AUDIT
| PRIV_TAPE_READ
| PRIV_TAPE_WRITE;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
/// Tape.Reader can do read and inspect tape content
pub const ROLE_TAPE_READER: u64 = 0
| PRIV_TAPE_AUDIT
| PRIV_TAPE_READ;
/// NoAccess can be used to remove privileges from specific (sub-)paths
pub const ROLE_NAME_NO_ACCESS: &str = "NoAccess";
#[api(
type_text: "<role>",
)]
#[repr(u64)]
#[derive(Serialize, Deserialize)]
/// Enum representing roles via their [PRIVILEGES] combination.
///
/// Since privileges are implemented as bitflags, each unique combination of privileges maps to a
/// single, unique `u64` value that is used in this enum definition.
pub enum Role {
/// Administrator
Admin = ROLE_ADMIN,
/// Auditor
Audit = ROLE_AUDIT,
/// Disable Access
NoAccess = ROLE_NO_ACCESS,
/// Datastore Administrator
DatastoreAdmin = ROLE_DATASTORE_ADMIN,
/// Datastore Reader (inspect datastore content and do restores)
DatastoreReader = ROLE_DATASTORE_READER,
/// Datastore Backup (backup and restore owned backups)
DatastoreBackup = ROLE_DATASTORE_BACKUP,
/// Datastore PowerUser (backup, restore and prune owned backup)
DatastorePowerUser = ROLE_DATASTORE_POWERUSER,
/// Datastore Auditor
DatastoreAudit = ROLE_DATASTORE_AUDIT,
/// Remote Auditor
RemoteAudit = ROLE_REMOTE_AUDIT,
/// Remote Administrator
RemoteAdmin = ROLE_REMOTE_ADMIN,
/// Synchronization Operator
RemoteSyncOperator = ROLE_REMOTE_SYNC_OPERATOR,
/// Synchronisation Operator (push direction)
RemoteSyncPushOperator = ROLE_REMOTE_SYNC_PUSH_OPERATOR,
/// Remote Datastore Prune
RemoteDatastorePowerUser = ROLE_REMOTE_DATASTORE_POWERUSER,
/// Remote Datastore Admin
RemoteDatastoreAdmin = ROLE_REMOTE_DATASTORE_ADMIN,
/// Tape Auditor
TapeAudit = ROLE_TAPE_AUDIT,
/// Tape Administrator
TapeAdmin = ROLE_TAPE_ADMIN,
/// Tape Operator
TapeOperator = ROLE_TAPE_OPERATOR,
/// Tape Reader
TapeReader = ROLE_TAPE_READER,
}
impl FromStr for Role {
type Err = value::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::deserialize(s.into_deserializer())
}
}
pub const ACL_PATH_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&ACL_PATH_REGEX);
pub const ACL_PATH_SCHEMA: Schema = StringSchema::new("Access control path.")
.format(&ACL_PATH_FORMAT)
.min_length(1)
.max_length(128)
.schema();
pub const ACL_PROPAGATE_SCHEMA: Schema =
BooleanSchema::new("Allow to propagate (inherit) permissions.")
.default(true)
.schema();
pub const ACL_UGID_TYPE_SCHEMA: Schema = StringSchema::new("Type of 'ugid' property.")
.format(&ApiStringFormat::Enum(&[
EnumEntry::new("user", "User"),
EnumEntry::new("group", "Group"),
]))
.schema();
#[api(
properties: {
propagate: {
schema: ACL_PROPAGATE_SCHEMA,
},
path: {
schema: ACL_PATH_SCHEMA,
},
ugid_type: {
schema: ACL_UGID_TYPE_SCHEMA,
},
ugid: {
type: String,
description: "User or Group ID.",
},
roleid: {
type: Role,
}
}
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
/// ACL list entry.
pub struct AclListItem {
pub path: String,
pub ugid: String,
pub ugid_type: String,
pub propagate: bool,
pub roleid: String,
}

105
pbs-api-types/src/ad.rs Normal file
View File

@ -0,0 +1,105 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, Updater};
use super::{
LdapMode, LDAP_DOMAIN_SCHEMA, REALM_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
SYNC_ATTRIBUTES_SCHEMA, SYNC_DEFAULTS_STRING_SCHEMA, USER_CLASSES_SCHEMA,
};
#[api(
properties: {
"realm": {
schema: REALM_ID_SCHEMA,
},
"comment": {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"default": {
optional: true,
default: false,
},
"verify": {
optional: true,
default: false,
},
"sync-defaults-options": {
schema: SYNC_DEFAULTS_STRING_SCHEMA,
optional: true,
},
"sync-attributes": {
schema: SYNC_ATTRIBUTES_SCHEMA,
optional: true,
},
"user-classes" : {
optional: true,
schema: USER_CLASSES_SCHEMA,
},
"base-dn" : {
schema: LDAP_DOMAIN_SCHEMA,
optional: true,
},
"bind-dn" : {
schema: LDAP_DOMAIN_SCHEMA,
optional: true,
}
},
)]
#[derive(Serialize, Deserialize, Updater, Clone)]
#[serde(rename_all = "kebab-case")]
/// AD realm configuration properties.
pub struct AdRealmConfig {
#[updater(skip)]
pub realm: String,
/// AD server address
pub server1: String,
/// Fallback AD server address
#[serde(skip_serializing_if = "Option::is_none")]
pub server2: Option<String>,
/// AD server Port
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
/// Base domain name. Users are searched under this domain using a `subtree search`.
/// Expected to be set only internally to `defaultNamingContext` of the AD server, but can be
/// overridden if the need arises.
#[serde(skip_serializing_if = "Option::is_none")]
pub base_dn: Option<String>,
/// Comment
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// True if you want this to be the default realm selected on login.
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
/// Connection security
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<LdapMode>,
/// Verify server certificate
#[serde(skip_serializing_if = "Option::is_none")]
pub verify: Option<bool>,
/// CA certificate to use for the server. The path can point to
/// either a file, or a directory. If it points to a file,
/// the PEM-formatted X.509 certificate stored at the path
/// will be added as a trusted certificate.
/// If the path points to a directory,
/// the directory replaces the system's default certificate
/// store at `/etc/ssl/certs` - Every file in the directory
/// will be loaded as a trusted certificate.
#[serde(skip_serializing_if = "Option::is_none")]
pub capath: Option<String>,
/// Bind domain to use for looking up users
#[serde(skip_serializing_if = "Option::is_none")]
pub bind_dn: Option<String>,
/// Custom LDAP search filter for user sync
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
/// Default options for AD sync
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_defaults_options: Option<String>,
/// List of LDAP attributes to sync from AD to user config
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_attributes: Option<String>,
/// User ``objectClass`` classes to sync
#[serde(skip_serializing_if = "Option::is_none")]
pub user_classes: Option<String>,
}

View File

@ -0,0 +1,95 @@
use std::fmt::{self, Display};
use anyhow::Error;
use serde::{Deserialize, Serialize};
use proxmox_schema::api;
#[api(default: "encrypt")]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
/// Defines whether data is encrypted (using an AEAD cipher), only signed, or neither.
pub enum CryptMode {
/// Don't encrypt.
None,
/// Encrypt.
Encrypt,
/// Only sign.
SignOnly,
}
#[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)]
#[serde(transparent)]
/// 32-byte fingerprint, usually calculated with SHA256.
pub struct Fingerprint {
#[serde(with = "bytes_as_fingerprint")]
bytes: [u8; 32],
}
impl Fingerprint {
pub fn new(bytes: [u8; 32]) -> Self {
Self { bytes }
}
pub fn bytes(&self) -> &[u8; 32] {
&self.bytes
}
pub fn signature(&self) -> String {
as_fingerprint(&self.bytes)
}
}
/// Display as short key ID
impl Display for Fingerprint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", as_fingerprint(&self.bytes[0..8]))
}
}
impl std::str::FromStr for Fingerprint {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
let mut tmp = s.to_string();
tmp.retain(|c| c != ':');
let mut bytes = [0u8; 32];
hex::decode_to_slice(&tmp, &mut bytes)?;
Ok(Fingerprint::new(bytes))
}
}
fn as_fingerprint(bytes: &[u8]) -> String {
hex::encode(bytes)
.as_bytes()
.chunks(2)
.map(|v| unsafe { std::str::from_utf8_unchecked(v) }) // it's a hex string
.collect::<Vec<&str>>()
.join(":")
}
pub mod bytes_as_fingerprint {
use std::mem::MaybeUninit;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = super::as_fingerprint(bytes);
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
where
D: Deserializer<'de>,
{
// TODO: more efficiently implement with a Visitor implementing visit_str using split() and
// hex::decode by-byte
let mut s = String::deserialize(deserializer)?;
s.retain(|c| c != ':');
let mut out = MaybeUninit::<[u8; 32]>::uninit();
hex::decode_to_slice(s.as_bytes(), unsafe { &mut (*out.as_mut_ptr())[..] })
.map_err(serde::de::Error::custom)?;
Ok(unsafe { out.assume_init() })
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::api;
#[api]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// General status information about a running VM file-restore daemon
pub struct RestoreDaemonStatus {
/// VM uptime in seconds
pub uptime: i64,
/// time left until auto-shutdown, keep in mind that this is useless when 'keep-timeout' is
/// not set, as then the status call will have reset the timer before returning the value
pub timeout: i64,
}
#[api]
#[derive(Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
/// The desired format of the result.
pub enum FileRestoreFormat {
/// Plain file (only works for single files)
Plain,
/// PXAR archive
Pxar,
/// ZIP archive
Zip,
/// TAR archive
Tar,
}

870
pbs-api-types/src/jobs.rs Normal file
View File

@ -0,0 +1,870 @@
use std::str::FromStr;
use anyhow::bail;
use const_format::concatcp;
use regex::Regex;
use serde::{Deserialize, Serialize};
use proxmox_schema::*;
use crate::{
Authid, BackupNamespace, BackupType, NotificationMode, RateLimitConfig, Userid,
BACKUP_GROUP_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_NS_RE, DATASTORE_SCHEMA,
DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA, NS_MAX_DEPTH_REDUCED_SCHEMA, PROXMOX_SAFE_ID_FORMAT,
PROXMOX_SAFE_ID_REGEX_STR, REMOTE_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
};
const_regex! {
/// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
pub VERIFICATION_JOB_WORKER_ID_REGEX = concatcp!(r"^(", PROXMOX_SAFE_ID_REGEX_STR, r"):");
/// Regex for sync jobs '(REMOTE|\-):REMOTE_DATASTORE:LOCAL_DATASTORE:(?:LOCAL_NS_ANCHOR:)ACTUAL_JOB_ID'
pub SYNC_JOB_WORKER_ID_REGEX = concatcp!(r"^(", PROXMOX_SAFE_ID_REGEX_STR, r"|\-):(", PROXMOX_SAFE_ID_REGEX_STR, r"):(", PROXMOX_SAFE_ID_REGEX_STR, r")(?::(", BACKUP_NS_RE, r"))?:");
}
pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new("Run sync job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>")
.schema();
pub const GC_SCHEDULE_SCHEMA: Schema =
StringSchema::new("Run garbage collection job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>")
.schema();
pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new("Run prune job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>")
.schema();
pub const VERIFICATION_SCHEDULE_SCHEMA: Schema =
StringSchema::new("Run verify job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>")
.schema();
pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Delete vanished backups. This remove the local copy if the remote backup was deleted.",
)
.default(false)
.schema();
#[api(
properties: {
"next-run": {
description: "Estimated time of the next run (UNIX epoch).",
optional: true,
type: Integer,
},
"last-run-state": {
description: "Result of the last run.",
optional: true,
type: String,
},
"last-run-upid": {
description: "Task UPID of the last run.",
optional: true,
type: String,
},
"last-run-endtime": {
description: "Endtime of the last run.",
optional: true,
type: Integer,
},
}
)]
#[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Job Scheduling Status
pub struct JobScheduleStatus {
#[serde(skip_serializing_if = "Option::is_none")]
pub next_run: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_run_state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_run_upid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_run_endtime: Option<i64>,
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// When do we send notifications
pub enum Notify {
/// Never send notification
Never,
/// Send notifications for failed and successful jobs
Always,
/// Send notifications for failed jobs only
Error,
}
#[api(
properties: {
gc: {
type: Notify,
optional: true,
},
verify: {
type: Notify,
optional: true,
},
sync: {
type: Notify,
optional: true,
},
prune: {
type: Notify,
optional: true,
},
},
)]
#[derive(Debug, Serialize, Deserialize)]
/// Datastore notify settings
pub struct DatastoreNotify {
/// Garbage collection settings
#[serde(skip_serializing_if = "Option::is_none")]
pub gc: Option<Notify>,
/// Verify job setting
#[serde(skip_serializing_if = "Option::is_none")]
pub verify: Option<Notify>,
/// Sync job setting
#[serde(skip_serializing_if = "Option::is_none")]
pub sync: Option<Notify>,
/// Prune job setting
#[serde(skip_serializing_if = "Option::is_none")]
pub prune: Option<Notify>,
}
pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new(
"Datastore notification setting, enum can be one of 'always', 'never', or 'error'.",
)
.format(&ApiStringFormat::PropertyString(
&DatastoreNotify::API_SCHEMA,
))
.schema();
pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Do not verify backups that are already verified if their verification is not outdated.",
)
.default(true)
.schema();
pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema =
IntegerSchema::new("Days after that a verification becomes outdated. (0 is deprecated)'")
.minimum(0)
.schema();
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
"ignore-verified": {
optional: true,
schema: IGNORE_VERIFIED_BACKUPS_SCHEMA,
},
"outdated-after": {
optional: true,
schema: VERIFICATION_OUTDATED_AFTER_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: VERIFICATION_SCHEDULE_SCHEMA,
},
ns: {
optional: true,
schema: BACKUP_NAMESPACE_SCHEMA,
},
"max-depth": {
optional: true,
schema: crate::NS_MAX_DEPTH_SCHEMA,
},
}
)]
#[derive(Serialize, Deserialize, Updater, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Verification Job
pub struct VerificationJobConfig {
/// unique ID to address this job
#[updater(skip)]
pub id: String,
/// the datastore ID this verification job affects
pub store: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// if not set to false, check the age of the last snapshot verification to filter
/// out recent ones, depending on 'outdated_after' configuration.
pub ignore_verified: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Reverify snapshots after X days, never if 0. Ignored if 'ignore_verified' is false.
pub outdated_after: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// when to schedule this job in calendar event notation
pub schedule: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
/// on which backup namespace to run the verification recursively
pub ns: Option<BackupNamespace>,
#[serde(skip_serializing_if = "Option::is_none", default)]
/// how deep the verify should go from the `ns` level downwards. Passing 0 verifies only the
/// snapshots on the same level as the passed `ns`, or the datastore root if none.
pub max_depth: Option<usize>,
}
impl VerificationJobConfig {
pub fn acl_path(&self) -> Vec<&str> {
match self.ns.as_ref() {
Some(ns) => ns.acl_path(&self.store),
None => vec!["datastore", &self.store],
}
}
}
#[api(
properties: {
config: {
type: VerificationJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Status of Verification Job
pub struct VerificationJobStatus {
#[serde(flatten)]
pub config: VerificationJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}
#[api(
properties: {
store: {
schema: DATASTORE_SCHEMA,
},
pool: {
schema: MEDIA_POOL_NAME_SCHEMA,
},
drive: {
schema: DRIVE_NAME_SCHEMA,
},
"eject-media": {
description: "Eject media upon job completion.",
type: bool,
optional: true,
},
"export-media-set": {
description: "Export media set upon job completion.",
type: bool,
optional: true,
},
"latest-only": {
description: "Backup latest snapshots only.",
type: bool,
optional: true,
},
"notify-user": {
optional: true,
type: Userid,
},
"group-filter": {
schema: GROUP_FILTER_LIST_SCHEMA,
optional: true,
},
ns: {
type: BackupNamespace,
optional: true,
},
"max-depth": {
schema: crate::NS_MAX_DEPTH_SCHEMA,
optional: true,
},
"worker-threads": {
type: Integer,
optional: true,
minimum: 1,
maximum: 32,
default: 1,
},
}
)]
#[derive(Serialize, Deserialize, Clone, Updater, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Tape Backup Job Setup
pub struct TapeBackupJobSetup {
pub store: String,
pub pool: String,
pub drive: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub eject_media: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub export_media_set: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latest_only: Option<bool>,
/// Send job email notification to this user
#[serde(skip_serializing_if = "Option::is_none")]
pub notify_user: Option<Userid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notification_mode: Option<NotificationMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group_filter: Option<Vec<GroupFilter>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ns: Option<BackupNamespace>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub max_depth: Option<usize>,
/// Set the number of worker threads to use for the job
#[serde(skip_serializing_if = "Option::is_none")]
pub worker_threads: Option<u64>,
}
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
setup: {
type: TapeBackupJobSetup,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: SYNC_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize, Deserialize, Clone, Updater, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Tape Backup Job
pub struct TapeBackupJobConfig {
#[updater(skip)]
pub id: String,
#[serde(flatten)]
pub setup: TapeBackupJobSetup,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: TapeBackupJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Status of Tape Backup Job
pub struct TapeBackupJobStatus {
#[serde(flatten)]
pub config: TapeBackupJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
/// Next tape used (best guess)
#[serde(skip_serializing_if = "Option::is_none")]
pub next_media_label: Option<String>,
}
#[derive(Clone, Debug)]
/// Filter for matching `BackupGroup`s, for use with `BackupGroup::filter`.
pub enum FilterType {
/// BackupGroup type - either `vm`, `ct`, or `host`.
BackupType(BackupType),
/// Full identifier of BackupGroup, including type
Group(String),
/// A regular expression matched against the full identifier of the BackupGroup
Regex(Regex),
}
impl PartialEq for FilterType {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::BackupType(a), Self::BackupType(b)) => a == b,
(Self::Group(a), Self::Group(b)) => a == b,
(Self::Regex(a), Self::Regex(b)) => a.as_str() == b.as_str(),
_ => false,
}
}
}
impl std::str::FromStr for FilterType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.split_once(':') {
Some(("group", value)) => BACKUP_GROUP_SCHEMA.parse_simple_value(value).map(|_| FilterType::Group(value.to_string()))?,
Some(("type", value)) => FilterType::BackupType(value.parse()?),
Some(("regex", value)) => FilterType::Regex(Regex::new(value)?),
Some((ty, _value)) => bail!("expected 'group', 'type' or 'regex' prefix, got '{}'", ty),
None => bail!("input doesn't match expected format '<group:GROUP||type:<vm|ct|host>|regex:REGEX>'"),
})
}
}
// used for serializing below, caution!
impl std::fmt::Display for FilterType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FilterType::BackupType(backup_type) => write!(f, "type:{}", backup_type),
FilterType::Group(backup_group) => write!(f, "group:{}", backup_group),
FilterType::Regex(regex) => write!(f, "regex:{}", regex.as_str()),
}
}
}
#[derive(Clone, Debug)]
pub struct GroupFilter {
pub is_exclude: bool,
pub filter_type: FilterType,
}
impl PartialEq for GroupFilter {
fn eq(&self, other: &Self) -> bool {
self.filter_type == other.filter_type && self.is_exclude == other.is_exclude
}
}
impl Eq for GroupFilter {}
impl std::str::FromStr for GroupFilter {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (is_exclude, type_str) = match s.split_once(':') {
Some(("include", value)) => (false, value),
Some(("exclude", value)) => (true, value),
_ => (false, s),
};
Ok(GroupFilter {
is_exclude,
filter_type: type_str.parse()?,
})
}
}
// used for serializing below, caution!
impl std::fmt::Display for GroupFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_exclude {
f.write_str("exclude:")?;
}
std::fmt::Display::fmt(&self.filter_type, f)
}
}
proxmox_serde::forward_deserialize_to_from_str!(GroupFilter);
proxmox_serde::forward_serialize_to_display!(GroupFilter);
fn verify_group_filter(input: &str) -> Result<(), anyhow::Error> {
GroupFilter::from_str(input).map(|_| ())
}
pub const GROUP_FILTER_SCHEMA: Schema = StringSchema::new(
"Group filter based on group identifier ('group:GROUP'), group type ('type:<vm|ct|host>'), or regex ('regex:RE'). Can be inverted by prepending 'exclude:'.")
.format(&ApiStringFormat::VerifyFn(verify_group_filter))
.type_text("[<exclude:|include:>]<type:<vm|ct|host>|group:GROUP|regex:RE>")
.schema();
pub const GROUP_FILTER_LIST_SCHEMA: Schema =
ArraySchema::new("List of group filters.", &GROUP_FILTER_SCHEMA).schema();
pub const TRANSFER_LAST_SCHEMA: Schema =
IntegerSchema::new("Limit transfer to last N snapshots (per group), skipping others")
.minimum(1)
.schema();
#[api()]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Direction of the sync job, push or pull
pub enum SyncDirection {
/// Sync direction pull
#[default]
Pull,
/// Sync direction push
Push,
}
impl std::fmt::Display for SyncDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SyncDirection::Pull => f.write_str("pull"),
SyncDirection::Push => f.write_str("push"),
}
}
}
pub const RESYNC_CORRUPT_SCHEMA: Schema =
BooleanSchema::new("If the verification failed for a local snapshot, try to pull it again.")
.schema();
pub const SYNC_ENCRYPTED_ONLY_SCHEMA: Schema =
BooleanSchema::new("Only synchronize encrypted backup snapshots, exclude others.").schema();
pub const SYNC_VERIFIED_ONLY_SCHEMA: Schema =
BooleanSchema::new("Only synchronize verified backup snapshots, exclude others.").schema();
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
ns: {
type: BackupNamespace,
optional: true,
},
"owner": {
type: Authid,
optional: true,
},
remote: {
schema: REMOTE_ID_SCHEMA,
optional: true,
},
"remote-store": {
schema: DATASTORE_SCHEMA,
},
"remote-ns": {
type: BackupNamespace,
optional: true,
},
"remove-vanished": {
schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
optional: true,
},
"max-depth": {
schema: NS_MAX_DEPTH_REDUCED_SCHEMA,
optional: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
limit: {
type: RateLimitConfig,
},
schedule: {
optional: true,
schema: SYNC_SCHEDULE_SCHEMA,
},
"group-filter": {
schema: GROUP_FILTER_LIST_SCHEMA,
optional: true,
},
"transfer-last": {
schema: TRANSFER_LAST_SCHEMA,
optional: true,
},
"resync-corrupt": {
schema: RESYNC_CORRUPT_SCHEMA,
optional: true,
},
"encrypted-only": {
schema: SYNC_ENCRYPTED_ONLY_SCHEMA,
optional: true,
},
"verified-only": {
schema: SYNC_VERIFIED_ONLY_SCHEMA,
optional: true,
},
"sync-direction": {
type: SyncDirection,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Clone, Updater, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Sync Job
pub struct SyncJobConfig {
#[updater(skip)]
pub id: String,
pub store: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<BackupNamespace>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<Authid>,
#[serde(skip_serializing_if = "Option::is_none")]
/// None implies local sync.
pub remote: Option<String>,
pub remote_store: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub remote_ns: Option<BackupNamespace>,
#[serde(skip_serializing_if = "Option::is_none")]
pub remove_vanished: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_depth: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group_filter: Option<Vec<GroupFilter>>,
#[serde(flatten)]
pub limit: RateLimitConfig,
#[serde(skip_serializing_if = "Option::is_none")]
pub transfer_last: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resync_corrupt: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypted_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verified_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_direction: Option<SyncDirection>,
}
impl SyncJobConfig {
pub fn acl_path(&self) -> Vec<&str> {
match self.ns.as_ref() {
Some(ns) => ns.acl_path(&self.store),
None => vec!["datastore", &self.store],
}
}
pub fn remote_acl_path(&self) -> Option<Vec<&str>> {
let remote = self.remote.as_ref()?;
match &self.remote_ns {
Some(remote_ns) => Some(remote_ns.remote_acl_path(remote, &self.remote_store)),
None => Some(vec!["remote", remote, &self.remote_store]),
}
}
}
#[api(
properties: {
config: {
type: SyncJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Status of Sync Job
pub struct SyncJobStatus {
#[serde(flatten)]
pub config: SyncJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}
/// These are used separately without `ns`/`max-depth` sometimes in the API, specifically in the API
/// call to prune a specific group, where `max-depth` makes no sense.
#[api(
properties: {
"keep-last": {
schema: crate::PRUNE_SCHEMA_KEEP_LAST,
optional: true,
},
"keep-hourly": {
schema: crate::PRUNE_SCHEMA_KEEP_HOURLY,
optional: true,
},
"keep-daily": {
schema: crate::PRUNE_SCHEMA_KEEP_DAILY,
optional: true,
},
"keep-weekly": {
schema: crate::PRUNE_SCHEMA_KEEP_WEEKLY,
optional: true,
},
"keep-monthly": {
schema: crate::PRUNE_SCHEMA_KEEP_MONTHLY,
optional: true,
},
"keep-yearly": {
schema: crate::PRUNE_SCHEMA_KEEP_YEARLY,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Default, Updater, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Common pruning options
pub struct KeepOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_last: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_hourly: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_daily: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_weekly: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_monthly: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_yearly: Option<u64>,
}
impl KeepOptions {
pub fn keeps_something(&self) -> bool {
self.keep_last.unwrap_or(0)
+ self.keep_hourly.unwrap_or(0)
+ self.keep_daily.unwrap_or(0)
+ self.keep_weekly.unwrap_or(0)
+ self.keep_monthly.unwrap_or(0)
+ self.keep_yearly.unwrap_or(0)
> 0
}
}
#[api(
properties: {
keep: {
type: KeepOptions,
},
ns: {
type: BackupNamespace,
optional: true,
},
"max-depth": {
schema: NS_MAX_DEPTH_REDUCED_SCHEMA,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Default, Updater, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Common pruning options
pub struct PruneJobOptions {
#[serde(flatten)]
pub keep: KeepOptions,
/// The (optional) recursion depth
#[serde(skip_serializing_if = "Option::is_none")]
pub max_depth: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<BackupNamespace>,
}
impl PruneJobOptions {
pub fn keeps_something(&self) -> bool {
self.keep.keeps_something()
}
pub fn acl_path<'a>(&'a self, store: &'a str) -> Vec<&'a str> {
match &self.ns {
Some(ns) => ns.acl_path(store),
None => vec!["datastore", store],
}
}
}
#[api(
properties: {
disable: {
type: Boolean,
optional: true,
default: false,
},
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
schedule: {
schema: PRUNE_SCHEDULE_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
options: {
type: PruneJobOptions,
},
},
)]
#[derive(Deserialize, Serialize, Updater, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Prune configuration.
pub struct PruneJobConfig {
/// unique ID to address this job
#[updater(skip)]
pub id: String,
pub store: String,
/// Disable this job.
#[serde(default, skip_serializing_if = "is_false")]
#[updater(serde(skip_serializing_if = "Option::is_none"))]
pub disable: bool,
pub schedule: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(flatten)]
pub options: PruneJobOptions,
}
impl PruneJobConfig {
pub fn acl_path(&self) -> Vec<&str> {
self.options.acl_path(&self.store)
}
}
fn is_false(b: &bool) -> bool {
!b
}
#[api(
properties: {
config: {
type: PruneJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Status of prune job
pub struct PruneJobStatus {
#[serde(flatten)]
pub config: PruneJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}

View File

@ -0,0 +1,55 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::api;
use crate::CERT_FINGERPRINT_SHA256_SCHEMA;
#[api(default: "scrypt")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
/// Key derivation function for password protected encryption keys.
pub enum Kdf {
/// Do not encrypt the key.
None,
/// Encrypt they key with a password using SCrypt.
Scrypt,
/// Encrtypt the Key with a password using PBKDF2
PBKDF2,
}
impl Default for Kdf {
#[inline]
fn default() -> Self {
Kdf::Scrypt
}
}
#[api(
properties: {
kdf: {
type: Kdf,
},
fingerprint: {
schema: CERT_FINGERPRINT_SHA256_SCHEMA,
optional: true,
},
},
)]
#[derive(Deserialize, Serialize)]
/// Encryption Key Information
pub struct KeyInfo {
/// Path to key (if stored in a file)
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
pub kdf: Kdf,
/// Key creation time
pub created: i64,
/// Key modification time
pub modified: i64,
/// Key fingerprint
#[serde(skip_serializing_if = "Option::is_none")]
pub fingerprint: Option<String>,
/// Password hint
#[serde(skip_serializing_if = "Option::is_none")]
pub hint: Option<String>,
}

215
pbs-api-types/src/ldap.rs Normal file
View File

@ -0,0 +1,215 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, ApiStringFormat, ApiType, ArraySchema, Schema, StringSchema, Updater};
use super::{REALM_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA};
#[api()]
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
/// LDAP connection type
pub enum LdapMode {
/// Plaintext LDAP connection
#[serde(rename = "ldap")]
#[default]
Ldap,
/// Secure STARTTLS connection
#[serde(rename = "ldap+starttls")]
StartTls,
/// Secure LDAPS connection
#[serde(rename = "ldaps")]
Ldaps,
}
#[api(
properties: {
"realm": {
schema: REALM_ID_SCHEMA,
},
"comment": {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"default": {
optional: true,
default: false,
},
"verify": {
optional: true,
default: false,
},
"sync-defaults-options": {
schema: SYNC_DEFAULTS_STRING_SCHEMA,
optional: true,
},
"sync-attributes": {
schema: SYNC_ATTRIBUTES_SCHEMA,
optional: true,
},
"user-classes" : {
optional: true,
schema: USER_CLASSES_SCHEMA,
},
"base-dn" : {
schema: LDAP_DOMAIN_SCHEMA,
},
"bind-dn" : {
schema: LDAP_DOMAIN_SCHEMA,
optional: true,
}
},
)]
#[derive(Serialize, Deserialize, Updater, Clone)]
#[serde(rename_all = "kebab-case")]
/// LDAP configuration properties.
pub struct LdapRealmConfig {
#[updater(skip)]
pub realm: String,
/// LDAP server address
pub server1: String,
/// Fallback LDAP server address
#[serde(skip_serializing_if = "Option::is_none")]
pub server2: Option<String>,
/// Port
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
/// Base domain name. Users are searched under this domain using a `subtree search`.
pub base_dn: String,
/// Username attribute. Used to map a ``userid`` to LDAP to an LDAP ``dn``.
pub user_attr: String,
/// Comment
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// True if you want this to be the default realm selected on login.
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
/// Connection security
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<LdapMode>,
/// Verify server certificate
#[serde(skip_serializing_if = "Option::is_none")]
pub verify: Option<bool>,
/// CA certificate to use for the server. The path can point to
/// either a file, or a directory. If it points to a file,
/// the PEM-formatted X.509 certificate stored at the path
/// will be added as a trusted certificate.
/// If the path points to a directory,
/// the directory replaces the system's default certificate
/// store at `/etc/ssl/certs` - Every file in the directory
/// will be loaded as a trusted certificate.
#[serde(skip_serializing_if = "Option::is_none")]
pub capath: Option<String>,
/// Bind domain to use for looking up users
#[serde(skip_serializing_if = "Option::is_none")]
pub bind_dn: Option<String>,
/// Custom LDAP search filter for user sync
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
/// Default options for LDAP sync
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_defaults_options: Option<String>,
/// List of attributes to sync from LDAP to user config
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_attributes: Option<String>,
/// User ``objectClass`` classes to sync
#[serde(skip_serializing_if = "Option::is_none")]
pub user_classes: Option<String>,
}
#[api(
properties: {
"remove-vanished": {
optional: true,
schema: REMOVE_VANISHED_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize, Updater, Default, Debug)]
#[serde(rename_all = "kebab-case")]
/// Default options for LDAP synchronization runs
pub struct SyncDefaultsOptions {
/// How to handle vanished properties/users
pub remove_vanished: Option<String>,
/// Enable new users after sync
pub enable_new: Option<bool>,
}
#[api()]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
/// remove-vanished options
pub enum RemoveVanished {
/// Delete ACLs for vanished users
Acl,
/// Remove vanished users
Entry,
/// Remove vanished properties from users (e.g. email)
Properties,
}
pub const LDAP_DOMAIN_SCHEMA: Schema = StringSchema::new("LDAP Domain").schema();
pub const SYNC_DEFAULTS_STRING_SCHEMA: Schema = StringSchema::new("sync defaults options")
.format(&ApiStringFormat::PropertyString(
&SyncDefaultsOptions::API_SCHEMA,
))
.schema();
const REMOVE_VANISHED_DESCRIPTION: &str =
"A semicolon-separated list of things to remove when they or the user \
vanishes during user synchronization. The following values are possible: ``entry`` removes the \
user when not returned from the sync; ``properties`` removes any \
properties on existing user that do not appear in the source. \
``acl`` removes ACLs when the user is not returned from the sync.";
pub const REMOVE_VANISHED_SCHEMA: Schema = StringSchema::new(REMOVE_VANISHED_DESCRIPTION)
.format(&ApiStringFormat::PropertyString(&REMOVE_VANISHED_ARRAY))
.schema();
pub const REMOVE_VANISHED_ARRAY: Schema = ArraySchema::new(
"Array of remove-vanished options",
&RemoveVanished::API_SCHEMA,
)
.min_length(1)
.schema();
#[api()]
#[derive(Serialize, Deserialize, Updater, Default, Debug)]
#[serde(rename_all = "kebab-case")]
/// Determine which LDAP attributes should be synced to which user attributes
pub struct SyncAttributes {
/// Name of the LDAP attribute containing the user's email address
pub email: Option<String>,
/// Name of the LDAP attribute containing the user's first name
pub firstname: Option<String>,
/// Name of the LDAP attribute containing the user's last name
pub lastname: Option<String>,
}
const SYNC_ATTRIBUTES_TEXT: &str = "Comma-separated list of key=value pairs for specifying \
which LDAP attributes map to which PBS user field. For example, \
to map the LDAP attribute ``mail`` to PBS's ``email``, write \
``email=mail``.";
pub const SYNC_ATTRIBUTES_SCHEMA: Schema = StringSchema::new(SYNC_ATTRIBUTES_TEXT)
.format(&ApiStringFormat::PropertyString(
&SyncAttributes::API_SCHEMA,
))
.schema();
pub const USER_CLASSES_ARRAY: Schema = ArraySchema::new(
"Array of user classes",
&StringSchema::new("user class").schema(),
)
.min_length(1)
.schema();
const USER_CLASSES_TEXT: &str = "Comma-separated list of allowed objectClass values for \
user synchronization. For instance, if ``user-classes`` is set to ``person,user``, \
then user synchronization will consider all LDAP entities \
where ``objectClass: person`` `or` ``objectClass: user``.";
pub const USER_CLASSES_SCHEMA: Schema = StringSchema::new(USER_CLASSES_TEXT)
.format(&ApiStringFormat::PropertyString(&USER_CLASSES_ARRAY))
.default("inetorgperson,posixaccount,person,user")
.schema();

491
pbs-api-types/src/lib.rs Normal file
View File

@ -0,0 +1,491 @@
//! Basic API types used by most of the PBS code.
use const_format::concatcp;
use serde::{Deserialize, Serialize};
pub mod percent_encoding;
use proxmox_schema::{
api, const_regex, ApiStringFormat, ApiType, ArraySchema, EnumEntry, ReturnType, Schema,
StringSchema, Updater,
};
use proxmox_time::parse_daily_duration;
use proxmox_auth_api::types::{APITOKEN_ID_REGEX_STR, USER_ID_REGEX_STR};
pub use proxmox_schema::api_types::SAFE_ID_FORMAT as PROXMOX_SAFE_ID_FORMAT;
pub use proxmox_schema::api_types::SAFE_ID_REGEX as PROXMOX_SAFE_ID_REGEX;
pub use proxmox_schema::api_types::SAFE_ID_REGEX_STR as PROXMOX_SAFE_ID_REGEX_STR;
pub use proxmox_schema::api_types::{
BLOCKDEVICE_DISK_AND_PARTITION_NAME_REGEX, BLOCKDEVICE_NAME_REGEX,
};
pub use proxmox_schema::api_types::{DNS_ALIAS_REGEX, DNS_NAME_OR_IP_REGEX, DNS_NAME_REGEX};
pub use proxmox_schema::api_types::{FINGERPRINT_SHA256_REGEX, SHA256_HEX_REGEX};
pub use proxmox_schema::api_types::{
GENERIC_URI_REGEX, HOSTNAME_REGEX, HOST_PORT_REGEX, HTTP_URL_REGEX,
};
pub use proxmox_schema::api_types::{MULTI_LINE_COMMENT_REGEX, SINGLE_LINE_COMMENT_REGEX};
pub use proxmox_schema::api_types::{PASSWORD_REGEX, SYSTEMD_DATETIME_REGEX, UUID_REGEX};
pub use proxmox_schema::api_types::{CIDR_FORMAT, CIDR_REGEX};
pub use proxmox_schema::api_types::{CIDR_V4_FORMAT, CIDR_V4_REGEX};
pub use proxmox_schema::api_types::{CIDR_V6_FORMAT, CIDR_V6_REGEX};
pub use proxmox_schema::api_types::{IPRE_STR, IP_FORMAT, IP_REGEX};
pub use proxmox_schema::api_types::{IPV4RE_STR, IP_V4_FORMAT, IP_V4_REGEX};
pub use proxmox_schema::api_types::{IPV6RE_STR, IP_V6_FORMAT, IP_V6_REGEX};
pub use proxmox_schema::api_types::COMMENT_SCHEMA as SINGLE_LINE_COMMENT_SCHEMA;
pub use proxmox_schema::api_types::HOSTNAME_SCHEMA;
pub use proxmox_schema::api_types::HOST_PORT_SCHEMA;
pub use proxmox_schema::api_types::HTTP_URL_SCHEMA;
pub use proxmox_schema::api_types::MULTI_LINE_COMMENT_SCHEMA;
pub use proxmox_schema::api_types::NODE_SCHEMA;
pub use proxmox_schema::api_types::SINGLE_LINE_COMMENT_FORMAT;
pub use proxmox_schema::api_types::{
BLOCKDEVICE_DISK_AND_PARTITION_NAME_SCHEMA, BLOCKDEVICE_NAME_SCHEMA,
};
pub use proxmox_schema::api_types::{CERT_FINGERPRINT_SHA256_SCHEMA, FINGERPRINT_SHA256_FORMAT};
pub use proxmox_schema::api_types::{DISK_ARRAY_SCHEMA, DISK_LIST_SCHEMA};
pub use proxmox_schema::api_types::{DNS_ALIAS_FORMAT, DNS_NAME_FORMAT, DNS_NAME_OR_IP_SCHEMA};
pub use proxmox_schema::api_types::{PASSWORD_FORMAT, PASSWORD_SCHEMA};
pub use proxmox_schema::api_types::{SERVICE_ID_SCHEMA, UUID_FORMAT};
pub use proxmox_schema::api_types::{SYSTEMD_DATETIME_FORMAT, TIME_ZONE_SCHEMA};
use proxmox_schema::api_types::{DNS_NAME_STR, IPRE_BRACKET_STR};
// re-export APT API types
pub use proxmox_apt_api_types::{
APTChangeRepositoryOptions, APTGetChangelogOptions, APTRepositoriesResult, APTRepositoryFile,
APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo, APTStandardRepository,
APTUpdateInfo, APTUpdateOptions,
};
#[rustfmt::skip]
pub const BACKUP_ID_RE: &str = r"[A-Za-z0-9_][A-Za-z0-9._\-]*";
#[rustfmt::skip]
pub const BACKUP_TYPE_RE: &str = r"(?:host|vm|ct)";
#[rustfmt::skip]
pub const BACKUP_TIME_RE: &str = r"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z";
#[rustfmt::skip]
pub const BACKUP_NS_RE: &str =
concatcp!("(?:",
"(?:", PROXMOX_SAFE_ID_REGEX_STR, r"/){0,7}", PROXMOX_SAFE_ID_REGEX_STR,
")?");
#[rustfmt::skip]
pub const BACKUP_NS_PATH_RE: &str =
concatcp!(r"(?:ns/", PROXMOX_SAFE_ID_REGEX_STR, r"/){0,7}ns/", PROXMOX_SAFE_ID_REGEX_STR, r"/");
#[rustfmt::skip]
pub const SNAPSHOT_PATH_REGEX_STR: &str =
concatcp!(
r"(", BACKUP_TYPE_RE, ")/(", BACKUP_ID_RE, ")/(", BACKUP_TIME_RE, r")",
);
#[rustfmt::skip]
pub const GROUP_OR_SNAPSHOT_PATH_REGEX_STR: &str =
concatcp!(
r"(", BACKUP_TYPE_RE, ")/(", BACKUP_ID_RE, ")(?:/(", BACKUP_TIME_RE, r"))?",
);
mod acl;
pub use acl::*;
mod datastore;
pub use datastore::*;
mod jobs;
pub use jobs::*;
mod key_derivation;
pub use key_derivation::{Kdf, KeyInfo};
mod maintenance;
pub use maintenance::*;
mod network;
pub use network::*;
mod node;
pub use node::*;
pub use proxmox_auth_api::types as userid;
pub use proxmox_auth_api::types::{Authid, Userid};
pub use proxmox_auth_api::types::{Realm, RealmRef};
pub use proxmox_auth_api::types::{Tokenname, TokennameRef};
pub use proxmox_auth_api::types::{Username, UsernameRef};
pub use proxmox_auth_api::types::{
PROXMOX_GROUP_ID_SCHEMA, PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA,
};
#[macro_use]
mod user;
pub use user::*;
pub use proxmox_schema::upid::*;
mod crypto;
pub use crypto::{bytes_as_fingerprint, CryptMode, Fingerprint};
pub mod file_restore;
mod openid;
pub use openid::*;
mod ldap;
pub use ldap::*;
mod ad;
pub use ad::*;
mod remote;
pub use remote::*;
mod pathpatterns;
pub use pathpatterns::*;
mod tape;
pub use tape::*;
mod traffic_control;
pub use traffic_control::*;
mod zfs;
pub use zfs::*;
mod metrics;
pub use metrics::*;
mod version;
pub use version::*;
const_regex! {
// just a rough check - dummy acceptor is used before persisting
pub OPENSSL_CIPHERS_REGEX = r"^[0-9A-Za-z_:, +!\-@=.]+$";
pub BACKUP_REPO_URL_REGEX = concatcp!(
r"^^(?:(?:(",
USER_ID_REGEX_STR, "|", APITOKEN_ID_REGEX_STR,
")@)?(",
DNS_NAME_STR, "|", IPRE_BRACKET_STR,
"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR, r")$"
);
pub SUBSCRIPTION_KEY_REGEX = concat!(r"^pbs(?:[cbsp])-[0-9a-f]{10}$");
}
pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
pub const SUBSCRIPTION_KEY_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&SUBSCRIPTION_KEY_REGEX);
pub const OPENSSL_CIPHERS_TLS_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&OPENSSL_CIPHERS_REGEX);
pub const DAILY_DURATION_FORMAT: ApiStringFormat =
ApiStringFormat::VerifyFn(|s| parse_daily_duration(s).map(drop));
pub const SEARCH_DOMAIN_SCHEMA: Schema =
StringSchema::new("Search domain for host-name lookup.").schema();
pub const FIRST_DNS_SERVER_SCHEMA: Schema = StringSchema::new("First name server IP address.")
.format(&IP_FORMAT)
.schema();
pub const SECOND_DNS_SERVER_SCHEMA: Schema = StringSchema::new("Second name server IP address.")
.format(&IP_FORMAT)
.schema();
pub const THIRD_DNS_SERVER_SCHEMA: Schema = StringSchema::new("Third name server IP address.")
.format(&IP_FORMAT)
.schema();
pub const OPENSSL_CIPHERS_TLS_1_2_SCHEMA: Schema =
StringSchema::new("OpenSSL cipher list used by the proxy for TLS <= 1.2")
.format(&OPENSSL_CIPHERS_TLS_FORMAT)
.schema();
pub const OPENSSL_CIPHERS_TLS_1_3_SCHEMA: Schema =
StringSchema::new("OpenSSL ciphersuites list used by the proxy for TLS 1.3")
.format(&OPENSSL_CIPHERS_TLS_FORMAT)
.schema();
pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
.format(&PASSWORD_FORMAT)
.min_length(8)
.max_length(64)
.schema();
pub const REALM_ID_SCHEMA: Schema = StringSchema::new("Realm name.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(2)
.max_length(32)
.schema();
const PAM_REALM_ID_SCHEMA: Schema = StringSchema::new("Realm name.")
.format(&ApiStringFormat::Enum(&[EnumEntry::new(
"pam",
"Default PAM realm.",
)]))
.schema();
const PBS_REALM_ID_SCHEMA: Schema = StringSchema::new("Realm name.")
.format(&ApiStringFormat::Enum(&[EnumEntry::new(
"pbs",
"Default PBS realm.",
)]))
.schema();
pub const SUBSCRIPTION_KEY_SCHEMA: Schema =
StringSchema::new("Proxmox Backup Server subscription key.")
.format(&SUBSCRIPTION_KEY_FORMAT)
.min_length(15)
.max_length(16)
.schema();
pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
"Prevent changes if current configuration file has different \
SHA256 digest. This can be used to prevent concurrent \
modifications.",
)
.format(&PVE_CONFIG_DIGEST_FORMAT)
.schema();
/// API schema format definition for repository URLs
pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_REPO_URL_REGEX);
// Complex type definitions
#[api()]
#[derive(Default, Serialize, Deserialize)]
/// Storage space usage information.
pub struct StorageStatus {
/// Total space (bytes).
pub total: u64,
/// Used space (bytes).
pub used: u64,
/// Available space (bytes).
pub avail: u64,
}
pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(1)
.max_length(64)
.schema();
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Node Power command type.
pub enum NodePowerCommand {
/// Restart the server
Reboot,
/// Shutdown the server
Shutdown,
}
#[api()]
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// The state (result) of a finished worker task.
pub enum TaskStateType {
/// Ok
OK,
/// Warning
Warning,
/// Error
Error,
/// Unknown
Unknown,
}
#[api(
properties: {
upid: { schema: UPID::API_SCHEMA },
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
/// Task properties.
pub struct TaskListItem {
pub upid: String,
/// The node name where the task is running on.
pub node: String,
/// The Unix PID
pub pid: i64,
/// The task start time (Epoch)
pub pstart: u64,
/// The task start time (Epoch)
pub starttime: i64,
/// Worker type (arbitrary ASCII string)
pub worker_type: String,
/// Worker ID (arbitrary ASCII string)
pub worker_id: Option<String>,
/// The authenticated entity who started the task
pub user: String,
/// The task end time (Epoch)
#[serde(skip_serializing_if = "Option::is_none")]
pub endtime: Option<i64>,
/// Task end status
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE: ReturnType = ReturnType {
optional: false,
schema: &ArraySchema::new("A list of tasks.", &TaskListItem::API_SCHEMA).schema(),
};
#[api]
#[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
/// type of the realm
pub enum RealmType {
/// The PAM realm
Pam,
/// The PBS realm
Pbs,
/// An OpenID Connect realm
OpenId,
/// An LDAP realm
Ldap,
/// An Active Directory (AD) realm
Ad,
}
serde_plain::derive_display_from_serialize!(RealmType);
serde_plain::derive_fromstr_from_deserialize!(RealmType);
#[api(
properties: {
realm: {
schema: REALM_ID_SCHEMA,
},
"type": {
type: RealmType,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"default": {
optional: true,
default: false,
},
},
)]
#[derive(Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Basic Information about a realm
pub struct BasicRealmInfo {
/// Realm name
pub realm: String,
/// Realm type
#[serde(rename = "type")]
pub ty: RealmType,
/// True if it is the default realm
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
/// Optional comment for this realm
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
}
#[api(
properties: {
realm: {
schema: REALM_ID_SCHEMA,
},
"type": {
type: RealmType,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"default": {
optional: true,
default: false,
},
}
)]
#[derive(Serialize, Deserialize, Updater, Clone)]
#[serde(rename_all = "kebab-case")]
/// Built-in PAM realm configuration properties.
pub struct PamRealmConfig {
/// Realm name. Always "pam".
#[updater(skip)]
pub realm: String,
/// Realm type. Always [`RealmType::Pam`].
#[updater(skip)]
#[serde(rename = "type")]
pub ty: RealmType,
/// Comment for this realm
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// True if you want this to be the default realm selected on login.
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
}
impl Default for PamRealmConfig {
fn default() -> Self {
Self {
realm: "pam".to_owned(),
ty: RealmType::Pam,
comment: Some("Linux PAM standard authentication".to_owned()),
default: None,
}
}
}
#[api(
properties: {
realm: {
schema: REALM_ID_SCHEMA,
},
"type": {
type: RealmType,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"default": {
optional: true,
default: false,
},
}
)]
#[derive(Serialize, Deserialize, Updater, Clone)]
#[serde(rename_all = "kebab-case")]
/// Built-in Proxmox Backup Server realm configuration properties.
pub struct PbsRealmConfig {
/// Realm name. Always "pbs".
#[updater(skip)]
pub realm: String,
/// Realm type. Always [`RealmType::Pbs`].
#[updater(skip)]
#[serde(rename = "type")]
pub ty: RealmType,
/// Comment for this realm
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// True if you want this to be the default realm selected on login.
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
}
impl Default for PbsRealmConfig {
fn default() -> Self {
Self {
realm: "pbs".to_owned(),
ty: RealmType::Pbs,
comment: Some("Proxmox Backup authentication server".to_owned()),
default: None,
}
}
}

View File

@ -0,0 +1,110 @@
use anyhow::{bail, Error};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use proxmox_schema::{api, const_regex, ApiStringFormat, Schema, StringSchema};
const_regex! {
pub MAINTENANCE_MESSAGE_REGEX = r"^[[:^cntrl:]]*$";
}
pub const MAINTENANCE_MESSAGE_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&MAINTENANCE_MESSAGE_REGEX);
pub const MAINTENANCE_MESSAGE_SCHEMA: Schema =
StringSchema::new("Message describing the reason for the maintenance.")
.format(&MAINTENANCE_MESSAGE_FORMAT)
.max_length(64)
.schema();
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
/// Operation requirements, used when checking for maintenance mode.
pub enum Operation {
/// for any read operation like backup restore or RRD metric collection
Read,
/// for any write/delete operation, like backup create or GC
Write,
/// for any purely logical operation on the in-memory state of the datastore, e.g., to check if
/// some mutex could be locked (e.g., GC already running?)
///
/// NOTE: one must *not* do any IO operations when only helding this Op state
Lookup,
// GarbageCollect or Delete?
}
#[api]
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
/// Maintenance type.
pub enum MaintenanceType {
// TODO:
// - Add "GarbageCollection" or "DeleteOnly" as type and track GC (or all deletes) as separate
// operation, so that one can enable a mode where nothing new can be added but stuff can be
// cleaned
/// Only read operations are allowed on the datastore.
ReadOnly,
/// Neither read nor write operations are allowed on the datastore.
Offline,
/// The datastore is being deleted.
Delete,
/// The (removable) datastore is being unmounted.
Unmount,
}
serde_plain::derive_display_from_serialize!(MaintenanceType);
serde_plain::derive_fromstr_from_deserialize!(MaintenanceType);
#[api(
properties: {
type: {
type: MaintenanceType,
},
message: {
optional: true,
schema: MAINTENANCE_MESSAGE_SCHEMA,
}
},
default_key: "type",
)]
#[derive(Deserialize, Serialize)]
/// Maintenance mode
pub struct MaintenanceMode {
/// Type of maintenance ("read-only" or "offline").
#[serde(rename = "type")]
pub ty: MaintenanceType,
/// Reason for maintenance.
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
impl MaintenanceMode {
/// Used for deciding whether the datastore is cleared from the internal cache
pub fn clear_from_cache(&self) -> bool {
self.ty == MaintenanceType::Offline
|| self.ty == MaintenanceType::Delete
|| self.ty == MaintenanceType::Unmount
}
pub fn check(&self, operation: Option<Operation>) -> Result<(), Error> {
if self.ty == MaintenanceType::Delete {
bail!("datastore is being deleted");
}
let message = percent_encoding::percent_decode_str(self.message.as_deref().unwrap_or(""))
.decode_utf8()
.unwrap_or(Cow::Borrowed(""));
if let Some(Operation::Lookup) = operation {
return Ok(());
} else if self.ty == MaintenanceType::Unmount {
bail!("datastore is being unmounted");
} else if self.ty == MaintenanceType::Offline {
bail!("offline maintenance mode: {}", message);
} else if self.ty == MaintenanceType::ReadOnly {
if let Some(Operation::Write) = operation {
bail!("read-only maintenance mode: {}", message);
}
}
Ok(())
}
}

View File

@ -0,0 +1,255 @@
use serde::{Deserialize, Serialize};
use crate::{
HOST_PORT_SCHEMA, HTTP_URL_SCHEMA, PROXMOX_SAFE_ID_FORMAT, SINGLE_LINE_COMMENT_SCHEMA,
};
use proxmox_schema::{api, Schema, StringSchema, Updater};
pub const METRIC_SERVER_ID_SCHEMA: Schema = StringSchema::new("Metrics Server ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const INFLUXDB_BUCKET_SCHEMA: Schema = StringSchema::new("InfluxDB Bucket.")
.min_length(1)
.max_length(32)
.default("proxmox")
.schema();
pub const INFLUXDB_ORGANIZATION_SCHEMA: Schema = StringSchema::new("InfluxDB Organization.")
.min_length(1)
.max_length(32)
.default("proxmox")
.schema();
fn return_true() -> bool {
true
}
fn is_true(b: &bool) -> bool {
*b
}
#[api(
properties: {
name: {
schema: METRIC_SERVER_ID_SCHEMA,
},
enable: {
type: bool,
optional: true,
default: true,
},
host: {
schema: HOST_PORT_SCHEMA,
},
mtu: {
type: u16,
optional: true,
default: 1500,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")]
/// InfluxDB Server (UDP)
pub struct InfluxDbUdp {
#[updater(skip)]
pub name: String,
#[serde(default = "return_true", skip_serializing_if = "is_true")]
#[updater(serde(skip_serializing_if = "Option::is_none"))]
/// Enables or disables the metrics server
pub enable: bool,
/// the host + port
pub host: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// The MTU
pub mtu: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
}
#[api(
properties: {
name: {
schema: METRIC_SERVER_ID_SCHEMA,
},
enable: {
type: bool,
optional: true,
default: true,
},
url: {
schema: HTTP_URL_SCHEMA,
},
token: {
type: String,
optional: true,
},
bucket: {
schema: INFLUXDB_BUCKET_SCHEMA,
optional: true,
},
organization: {
schema: INFLUXDB_ORGANIZATION_SCHEMA,
optional: true,
},
"max-body-size": {
type: usize,
optional: true,
default: 25_000_000,
},
"verify-tls": {
type: bool,
optional: true,
default: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")]
/// InfluxDB Server (HTTP(s))
pub struct InfluxDbHttp {
#[updater(skip)]
pub name: String,
#[serde(default = "return_true", skip_serializing_if = "is_true")]
#[updater(serde(skip_serializing_if = "Option::is_none"))]
/// Enables or disables the metrics server
pub enable: bool,
/// The base url of the influxdb server
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// The (optional) API token
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Named location where time series data is stored
pub bucket: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Workspace for a group of users
pub organization: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// The (optional) maximum body size
pub max_body_size: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
/// If true, the certificate will be validated.
pub verify_tls: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
}
#[api]
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)]
/// Type of the metric server
pub enum MetricServerType {
/// InfluxDB HTTP
#[serde(rename = "influxdb-http")]
InfluxDbHttp,
/// InfluxDB UDP
#[serde(rename = "influxdb-udp")]
InfluxDbUdp,
}
#[api(
properties: {
name: {
schema: METRIC_SERVER_ID_SCHEMA,
},
"type": {
type: MetricServerType,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
},
)]
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "kebab-case")]
/// Basic information about a metric server that's available for all types
pub struct MetricServerInfo {
pub name: String,
#[serde(rename = "type")]
pub ty: MetricServerType,
/// Enables or disables the metrics server
#[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>,
/// The target server
pub server: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[api(
properties: {
data: {
type: Array,
items: {
type: MetricDataPoint,
}
}
}
)]
/// Return type for the metric API endpoint
pub struct Metrics {
/// List of metric data points, sorted by timestamp
pub data: Vec<MetricDataPoint>,
}
#[api(
properties: {
id: {
type: String,
},
metric: {
type: String,
},
timestamp: {
type: Integer,
},
},
)]
/// Metric data point
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct MetricDataPoint {
/// Unique identifier for this metric object, for instance `node/<nodename>`
/// or `qemu/<vmid>`.
pub id: String,
/// Name of the metric.
pub metric: String,
/// Time at which this metric was observed
pub timestamp: i64,
#[serde(rename = "type")]
pub ty: MetricDataType,
/// Metric value.
pub value: f64,
}
#[api]
/// Type of the metric.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum MetricDataType {
/// gauge.
Gauge,
/// counter.
Counter,
/// derive.
Derive,
}
serde_plain::derive_display_from_serialize!(MetricDataType);
serde_plain::derive_fromstr_from_deserialize!(MetricDataType);

View File

@ -0,0 +1,345 @@
use std::fmt;
use serde::{Deserialize, Serialize};
use proxmox_schema::*;
use crate::{
CIDR_FORMAT, CIDR_V4_FORMAT, CIDR_V6_FORMAT, IP_FORMAT, IP_V4_FORMAT, IP_V6_FORMAT,
PROXMOX_SAFE_ID_REGEX,
};
pub const NETWORK_INTERFACE_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
pub const IP_V4_SCHEMA: Schema = StringSchema::new("IPv4 address.")
.format(&IP_V4_FORMAT)
.max_length(15)
.schema();
pub const IP_V6_SCHEMA: Schema = StringSchema::new("IPv6 address.")
.format(&IP_V6_FORMAT)
.max_length(39)
.schema();
pub const IP_SCHEMA: Schema = StringSchema::new("IP (IPv4 or IPv6) address.")
.format(&IP_FORMAT)
.max_length(39)
.schema();
pub const CIDR_V4_SCHEMA: Schema = StringSchema::new("IPv4 address with netmask (CIDR notation).")
.format(&CIDR_V4_FORMAT)
.max_length(18)
.schema();
pub const CIDR_V6_SCHEMA: Schema = StringSchema::new("IPv6 address with netmask (CIDR notation).")
.format(&CIDR_V6_FORMAT)
.max_length(43)
.schema();
pub const CIDR_SCHEMA: Schema =
StringSchema::new("IP address (IPv4 or IPv6) with netmask (CIDR notation).")
.format(&CIDR_FORMAT)
.max_length(43)
.schema();
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Interface configuration method
pub enum NetworkConfigMethod {
/// Configuration is done manually using other tools
Manual,
/// Define interfaces with statically allocated addresses.
Static,
/// Obtain an address via DHCP
DHCP,
/// Define the loopback interface.
Loopback,
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[repr(u8)]
/// Linux Bond Mode
pub enum LinuxBondMode {
/// Round-robin policy
BalanceRr = 0,
/// Active-backup policy
ActiveBackup = 1,
/// XOR policy
BalanceXor = 2,
/// Broadcast policy
Broadcast = 3,
/// IEEE 802.3ad Dynamic link aggregation
#[serde(rename = "802.3ad")]
Ieee802_3ad = 4,
/// Adaptive transmit load balancing
BalanceTlb = 5,
/// Adaptive load balancing
BalanceAlb = 6,
}
impl fmt::Display for LinuxBondMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
LinuxBondMode::BalanceRr => "balance-rr",
LinuxBondMode::ActiveBackup => "active-backup",
LinuxBondMode::BalanceXor => "balance-xor",
LinuxBondMode::Broadcast => "broadcast",
LinuxBondMode::Ieee802_3ad => "802.3ad",
LinuxBondMode::BalanceTlb => "balance-tlb",
LinuxBondMode::BalanceAlb => "balance-alb",
})
}
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[repr(u8)]
/// Bond Transmit Hash Policy for LACP (802.3ad)
pub enum BondXmitHashPolicy {
/// Layer 2
Layer2 = 0,
/// Layer 2+3
#[serde(rename = "layer2+3")]
Layer2_3 = 1,
/// Layer 3+4
#[serde(rename = "layer3+4")]
Layer3_4 = 2,
}
impl fmt::Display for BondXmitHashPolicy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
BondXmitHashPolicy::Layer2 => "layer2",
BondXmitHashPolicy::Layer2_3 => "layer2+3",
BondXmitHashPolicy::Layer3_4 => "layer3+4",
})
}
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Network interface type
pub enum NetworkInterfaceType {
/// Loopback
Loopback,
/// Physical Ethernet device
Eth,
/// Linux Bridge
Bridge,
/// Linux Bond
Bond,
/// Linux VLAN (eth.10)
Vlan,
/// Interface Alias (eth:1)
Alias,
/// Unknown interface type
Unknown,
}
pub const NETWORK_INTERFACE_NAME_SCHEMA: Schema = StringSchema::new("Network interface name.")
.format(&NETWORK_INTERFACE_FORMAT)
.min_length(1)
.max_length(15) // libc::IFNAMSIZ-1
.schema();
pub const NETWORK_INTERFACE_ARRAY_SCHEMA: Schema =
ArraySchema::new("Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA).schema();
pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema =
StringSchema::new("A list of network devices, comma separated.")
.format(&ApiStringFormat::PropertyString(
&NETWORK_INTERFACE_ARRAY_SCHEMA,
))
.schema();
#[api(
properties: {
name: {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
},
"type": {
type: NetworkInterfaceType,
},
method: {
type: NetworkConfigMethod,
optional: true,
},
method6: {
type: NetworkConfigMethod,
optional: true,
},
cidr: {
schema: CIDR_V4_SCHEMA,
optional: true,
},
cidr6: {
schema: CIDR_V6_SCHEMA,
optional: true,
},
gateway: {
schema: IP_V4_SCHEMA,
optional: true,
},
gateway6: {
schema: IP_V6_SCHEMA,
optional: true,
},
options: {
description: "Option list (inet)",
type: Array,
items: {
description: "Optional attribute line.",
type: String,
},
},
options6: {
description: "Option list (inet6)",
type: Array,
items: {
description: "Optional attribute line.",
type: String,
},
},
comments: {
description: "Comments (inet, may span multiple lines)",
type: String,
optional: true,
},
comments6: {
description: "Comments (inet6, may span multiple lines)",
type: String,
optional: true,
},
bridge_ports: {
schema: NETWORK_INTERFACE_ARRAY_SCHEMA,
optional: true,
},
slaves: {
schema: NETWORK_INTERFACE_ARRAY_SCHEMA,
optional: true,
},
"vlan-id": {
description: "VLAN ID.",
type: u16,
optional: true,
},
"vlan-raw-device": {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
optional: true,
},
bond_mode: {
type: LinuxBondMode,
optional: true,
},
"bond-primary": {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
optional: true,
},
bond_xmit_hash_policy: {
type: BondXmitHashPolicy,
optional: true,
},
}
)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
/// Network Interface configuration
pub struct Interface {
/// Autostart interface
#[serde(rename = "autostart")]
pub autostart: bool,
/// Interface is active (UP)
pub active: bool,
/// Interface name
pub name: String,
/// Interface type
#[serde(rename = "type")]
pub interface_type: NetworkInterfaceType,
#[serde(skip_serializing_if = "Option::is_none")]
pub method: Option<NetworkConfigMethod>,
#[serde(skip_serializing_if = "Option::is_none")]
pub method6: Option<NetworkConfigMethod>,
#[serde(skip_serializing_if = "Option::is_none")]
/// IPv4 address with netmask
pub cidr: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// IPv4 gateway
pub gateway: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// IPv6 address with netmask
pub cidr6: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// IPv6 gateway
pub gateway6: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub options: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub options6: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comments: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comments6: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Maximum Transmission Unit
pub mtu: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bridge_ports: Option<Vec<String>>,
/// Enable bridge vlan support.
#[serde(skip_serializing_if = "Option::is_none")]
pub bridge_vlan_aware: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "vlan-id")]
pub vlan_id: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "vlan-raw-device")]
pub vlan_raw_device: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slaves: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bond_mode: Option<LinuxBondMode>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "bond-primary")]
pub bond_primary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
}
impl Interface {
pub fn new(name: String) -> Self {
Self {
name,
interface_type: NetworkInterfaceType::Unknown,
autostart: false,
active: false,
method: None,
method6: None,
cidr: None,
gateway: None,
cidr6: None,
gateway6: None,
options: Vec::new(),
options6: Vec::new(),
comments: None,
comments6: None,
mtu: None,
bridge_ports: None,
bridge_vlan_aware: None,
vlan_id: None,
vlan_raw_device: None,
slaves: None,
bond_mode: None,
bond_primary: None,
bond_xmit_hash_policy: None,
}
}
}

162
pbs-api-types/src/node.rs Normal file
View File

@ -0,0 +1,162 @@
use std::ffi::OsStr;
use proxmox_schema::*;
use serde::{Deserialize, Serialize};
use crate::StorageStatus;
#[api]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
/// Node memory usage counters
pub struct NodeMemoryCounters {
/// Total memory
pub total: u64,
/// Used memory
pub used: u64,
/// Free memory
pub free: u64,
}
#[api]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
/// Node swap usage counters
pub struct NodeSwapCounters {
/// Total swap
pub total: u64,
/// Used swap
pub used: u64,
/// Free swap
pub free: u64,
}
#[api]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
/// Contains general node information such as the fingerprint`
pub struct NodeInformation {
/// The SSL Fingerprint
pub fingerprint: String,
}
#[api]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
/// The current kernel version (output of `uname`)
pub struct KernelVersionInformation {
/// The systemname/nodename
pub sysname: String,
/// The kernel release number
pub release: String,
/// The kernel version
pub version: String,
/// The machine architecture
pub machine: String,
}
impl KernelVersionInformation {
pub fn from_uname_parts(
sysname: &OsStr,
release: &OsStr,
version: &OsStr,
machine: &OsStr,
) -> Self {
KernelVersionInformation {
sysname: sysname.to_str().map(String::from).unwrap_or_default(),
release: release.to_str().map(String::from).unwrap_or_default(),
version: version.to_str().map(String::from).unwrap_or_default(),
machine: machine.to_str().map(String::from).unwrap_or_default(),
}
}
pub fn get_legacy(&self) -> String {
format!("{} {} {}", self.sysname, self.release, self.version)
}
}
#[api]
#[derive(Serialize, Deserialize, Copy, Clone)]
#[serde(rename_all = "kebab-case")]
/// The possible BootModes
pub enum BootMode {
/// The BootMode is EFI/UEFI
Efi,
/// The BootMode is Legacy BIOS
LegacyBios,
}
#[api]
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
/// Holds the Bootmodes
pub struct BootModeInformation {
/// The BootMode, either Efi or Bios
pub mode: BootMode,
/// SecureBoot status
pub secureboot: bool,
}
#[api]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
/// Information about the CPU
pub struct NodeCpuInformation {
/// The CPU model
pub model: String,
/// The number of CPU sockets
pub sockets: usize,
/// The number of CPU cores (incl. threads)
pub cpus: usize,
}
#[api(
properties: {
memory: {
type: NodeMemoryCounters,
},
root: {
type: StorageStatus,
},
swap: {
type: NodeSwapCounters,
},
loadavg: {
type: Array,
items: {
type: Number,
description: "the load",
}
},
cpuinfo: {
type: NodeCpuInformation,
},
info: {
type: NodeInformation,
}
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// The Node status
pub struct NodeStatus {
pub memory: NodeMemoryCounters,
pub root: StorageStatus,
pub swap: NodeSwapCounters,
/// The current uptime of the server.
pub uptime: u64,
/// Load for 1, 5 and 15 minutes.
pub loadavg: [f64; 3],
/// The current kernel version (NEW struct type).
pub current_kernel: KernelVersionInformation,
/// The current kernel version (LEGACY string type).
pub kversion: String,
/// Total CPU usage since last query.
pub cpu: f64,
/// Total IO wait since last query.
pub wait: f64,
pub cpuinfo: NodeCpuInformation,
pub info: NodeInformation,
/// Current boot mode
pub boot_info: BootModeInformation,
}

127
pbs-api-types/src/openid.rs Normal file
View File

@ -0,0 +1,127 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, ApiStringFormat, ArraySchema, Schema, StringSchema, Updater};
use super::{
GENERIC_URI_REGEX, PROXMOX_SAFE_ID_FORMAT, PROXMOX_SAFE_ID_REGEX, REALM_ID_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA,
};
pub const OPENID_SCOPE_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
pub const OPENID_SCOPE_SCHEMA: Schema = StringSchema::new("OpenID Scope Name.")
.format(&OPENID_SCOPE_FORMAT)
.schema();
pub const OPENID_SCOPE_ARRAY_SCHEMA: Schema =
ArraySchema::new("Array of OpenId Scopes.", &OPENID_SCOPE_SCHEMA).schema();
pub const OPENID_SCOPE_LIST_FORMAT: ApiStringFormat =
ApiStringFormat::PropertyString(&OPENID_SCOPE_ARRAY_SCHEMA);
pub const OPENID_DEFAILT_SCOPE_LIST: &str = "email profile";
pub const OPENID_SCOPE_LIST_SCHEMA: Schema = StringSchema::new("OpenID Scope List")
.format(&OPENID_SCOPE_LIST_FORMAT)
.default(OPENID_DEFAILT_SCOPE_LIST)
.schema();
pub const OPENID_ACR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&GENERIC_URI_REGEX);
pub const OPENID_ACR_SCHEMA: Schema =
StringSchema::new("OpenID Authentication Context Class Reference.")
.format(&OPENID_ACR_FORMAT)
.schema();
pub const OPENID_ACR_ARRAY_SCHEMA: Schema =
ArraySchema::new("Array of OpenId ACRs.", &OPENID_ACR_SCHEMA).schema();
pub const OPENID_ACR_LIST_FORMAT: ApiStringFormat =
ApiStringFormat::PropertyString(&OPENID_ACR_ARRAY_SCHEMA);
pub const OPENID_ACR_LIST_SCHEMA: Schema = StringSchema::new("OpenID ACR List")
.format(&OPENID_ACR_LIST_FORMAT)
.schema();
pub const OPENID_USERNAME_CLAIM_SCHEMA: Schema = StringSchema::new(
"Use the value of this attribute/claim as unique user name. It \
is up to the identity provider to guarantee the uniqueness. The \
OpenID specification only guarantees that Subject ('sub') is \
unique. Also make sure that the user is not allowed to change that \
attribute by himself!",
)
.max_length(64)
.min_length(1)
.format(&PROXMOX_SAFE_ID_FORMAT)
.schema();
#[api(
properties: {
realm: {
schema: REALM_ID_SCHEMA,
},
"client-key": {
optional: true,
},
"scopes": {
schema: OPENID_SCOPE_LIST_SCHEMA,
optional: true,
},
"acr-values": {
schema: OPENID_ACR_LIST_SCHEMA,
optional: true,
},
prompt: {
description: "OpenID Prompt",
type: String,
format: &PROXMOX_SAFE_ID_FORMAT,
optional: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"default": {
optional: true,
default: false,
},
autocreate: {
optional: true,
default: false,
},
"username-claim": {
schema: OPENID_USERNAME_CLAIM_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")]
/// OpenID configuration properties.
pub struct OpenIdRealmConfig {
#[updater(skip)]
pub realm: String,
/// OpenID Issuer Url
pub issuer_url: String,
/// OpenID Client ID
pub client_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub scopes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub acr_values: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt: Option<String>,
/// OpenID Client Key
#[serde(skip_serializing_if = "Option::is_none")]
pub client_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// True if you want this to be the default realm selected on login.
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
/// Automatically create users if they do not exist.
#[serde(skip_serializing_if = "Option::is_none")]
pub autocreate: Option<bool>,
#[updater(skip)]
#[serde(skip_serializing_if = "Option::is_none")]
pub username_claim: Option<String>,
}

View File

@ -0,0 +1,30 @@
use proxmox_schema::{const_regex, ApiStringFormat, ApiType, Schema, StringSchema};
use serde::{Deserialize, Serialize};
const_regex! {
pub PATH_PATTERN_REGEX = concat!(r"^.+[^\\]$");
}
pub const PATH_PATTERN_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PATH_PATTERN_REGEX);
pub const PATH_PATTERN_SCHEMA: Schema =
StringSchema::new("Path or match pattern for matching filenames.")
.format(&PATH_PATTERN_FORMAT)
.schema();
#[derive(Default, Deserialize, Serialize)]
/// Path or path pattern for filename matching
pub struct PathPattern {
pattern: String,
}
impl ApiType for PathPattern {
const API_SCHEMA: Schema = PATH_PATTERN_SCHEMA;
}
impl AsRef<[u8]> for PathPattern {
fn as_ref(&self) -> &[u8] {
self.pattern.as_bytes()
}
}

View File

@ -0,0 +1,22 @@
use percent_encoding::{utf8_percent_encode, AsciiSet};
/// This used to be: `SIMPLE_ENCODE_SET` plus space, `"`, `#`, `<`, `>`, backtick, `?`, `{`, `}`
pub const DEFAULT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS // 0..1f and 7e
// The SIMPLE_ENCODE_SET adds space and anything >= 0x7e (7e itself is already included above)
.add(0x20)
.add(0x7f)
// the DEFAULT_ENCODE_SET added:
.add(b' ')
.add(b'"')
.add(b'#')
.add(b'<')
.add(b'>')
.add(b'`')
.add(b'?')
.add(b'{')
.add(b'}');
/// percent encode a url component
pub fn percent_encode_component(comp: &str) -> String {
utf8_percent_encode(comp, percent_encoding::NON_ALPHANUMERIC).to_string()
}

106
pbs-api-types/src/remote.rs Normal file
View File

@ -0,0 +1,106 @@
use serde::{Deserialize, Serialize};
use super::*;
use proxmox_schema::*;
pub const REMOTE_PASSWORD_SCHEMA: Schema =
StringSchema::new("Password or auth token for remote host.")
.format(&PASSWORD_FORMAT)
.min_length(1)
.max_length(1024)
.schema();
pub const REMOTE_PASSWORD_BASE64_SCHEMA: Schema =
StringSchema::new("Password or auth token for remote host (stored as base64 string).")
.format(&PASSWORD_FORMAT)
.min_length(1)
.max_length(1024)
.schema();
pub const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
#[api(
properties: {
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
host: {
schema: DNS_NAME_OR_IP_SCHEMA,
},
port: {
optional: true,
description: "The (optional) port",
type: u16,
},
"auth-id": {
type: Authid,
},
fingerprint: {
optional: true,
schema: CERT_FINGERPRINT_SHA256_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize, Updater, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Remote configuration properties.
pub struct RemoteConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
pub host: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
pub auth_id: Authid,
#[serde(skip_serializing_if = "Option::is_none")]
pub fingerprint: Option<String>,
}
#[api(
properties: {
name: {
schema: REMOTE_ID_SCHEMA,
},
config: {
type: RemoteConfig,
},
password: {
schema: REMOTE_PASSWORD_BASE64_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Remote properties.
pub struct Remote {
pub name: String,
// Note: The stored password is base64 encoded
#[serde(default, skip_serializing_if = "String::is_empty")]
#[serde(with = "proxmox_serde::string_as_base64")]
pub password: String,
#[serde(flatten)]
pub config: RemoteConfig,
}
#[api(
properties: {
name: {
schema: REMOTE_ID_SCHEMA,
},
config: {
type: RemoteConfig,
},
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Remote properties.
pub struct RemoteWithoutPassword {
pub name: String,
#[serde(flatten)]
pub config: RemoteConfig,
}

View File

@ -0,0 +1,134 @@
//! Types for tape changer API
use serde::{Deserialize, Serialize};
use proxmox_schema::{
api, ApiStringFormat, ArraySchema, IntegerSchema, Schema, StringSchema, Updater,
};
use crate::{OptionalDeviceIdentification, PROXMOX_SAFE_ID_FORMAT};
pub const CHANGER_NAME_SCHEMA: Schema = StringSchema::new("Tape Changer Identifier.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const SCSI_CHANGER_PATH_SCHEMA: Schema =
StringSchema::new("Path to Linux generic SCSI device (e.g. '/dev/sg4')").schema();
pub const MEDIA_LABEL_SCHEMA: Schema = StringSchema::new("Media Label/Barcode.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(2)
.max_length(32)
.schema();
pub const SLOT_ARRAY_SCHEMA: Schema = ArraySchema::new(
"Slot list.",
&IntegerSchema::new("Slot number").minimum(1).schema(),
)
.schema();
pub const EXPORT_SLOT_LIST_SCHEMA: Schema = StringSchema::new(
"\
A list of slot numbers, comma separated. Those slots are reserved for
Import/Export, i.e. any media in those slots are considered to be
'offline'.
",
)
.format(&ApiStringFormat::PropertyString(&SLOT_ARRAY_SCHEMA))
.schema();
#[api(
properties: {
name: {
schema: CHANGER_NAME_SCHEMA,
},
path: {
schema: SCSI_CHANGER_PATH_SCHEMA,
},
"export-slots": {
schema: EXPORT_SLOT_LIST_SCHEMA,
optional: true,
},
"eject-before-unload": {
optional: true,
default: false,
}
},
)]
#[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")]
/// SCSI tape changer
pub struct ScsiTapeChanger {
#[updater(skip)]
pub name: String,
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub export_slots: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// if set to true, tapes are ejected manually before unloading
pub eject_before_unload: Option<bool>,
}
#[api(
properties: {
config: {
type: ScsiTapeChanger,
},
info: {
type: OptionalDeviceIdentification,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Changer config with optional device identification attributes
pub struct ChangerListEntry {
#[serde(flatten)]
pub config: ScsiTapeChanger,
#[serde(flatten)]
pub info: OptionalDeviceIdentification,
}
#[api()]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Mtx Entry Kind
pub enum MtxEntryKind {
/// Drive
Drive,
/// Slot
Slot,
/// Import/Export Slot
ImportExport,
}
#[api(
properties: {
"entry-kind": {
type: MtxEntryKind,
},
"label-text": {
schema: MEDIA_LABEL_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Mtx Status Entry
pub struct MtxStatusEntry {
pub entry_kind: MtxEntryKind,
/// The ID of the slot or drive
pub entry_id: u64,
/// The media label (volume tag) if the slot/drive is full
#[serde(skip_serializing_if = "Option::is_none")]
pub label_text: Option<String>,
/// The slot the drive was loaded from
#[serde(skip_serializing_if = "Option::is_none")]
pub loaded_slot: Option<u64>,
/// The current state of the drive
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
}

View File

@ -0,0 +1,55 @@
use ::serde::{Deserialize, Serialize};
use proxmox_schema::api;
#[api()]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Optional Device Identification Attributes
pub struct OptionalDeviceIdentification {
/// Vendor (autodetected)
#[serde(skip_serializing_if = "Option::is_none")]
pub vendor: Option<String>,
/// Model (autodetected)
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
/// Serial number (autodetected)
#[serde(skip_serializing_if = "Option::is_none")]
pub serial: Option<String>,
}
#[api()]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Kind of device
pub enum DeviceKind {
/// Tape changer (Autoloader, Robot)
Changer,
/// Normal SCSI tape device
Tape,
}
#[api(
properties: {
kind: {
type: DeviceKind,
},
},
)]
#[derive(Debug, Serialize, Deserialize)]
/// Tape device information
pub struct TapeDeviceInfo {
pub kind: DeviceKind,
/// Path to the linux device node
pub path: String,
/// Serial number (autodetected)
pub serial: String,
/// Vendor (autodetected)
pub vendor: String,
/// Model (autodetected)
pub model: String,
/// Device major number
pub major: u32,
/// Device minor number
pub minor: u32,
}

View File

@ -0,0 +1,350 @@
//! Types for tape drive API
use anyhow::{bail, Error};
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, IntegerSchema, Schema, StringSchema, Updater};
use crate::{OptionalDeviceIdentification, CHANGER_NAME_SCHEMA, PROXMOX_SAFE_ID_FORMAT};
pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const LTO_DRIVE_PATH_SCHEMA: Schema =
StringSchema::new("The path to a LTO SCSI-generic tape device (i.e. '/dev/sg0')").schema();
pub const CHANGER_DRIVENUM_SCHEMA: Schema =
IntegerSchema::new("Associated changer drive number (requires option changer)")
.minimum(0)
.maximum(255)
.default(0)
.schema();
#[api(
properties: {
name: {
schema: DRIVE_NAME_SCHEMA,
}
}
)]
#[derive(Serialize, Deserialize)]
/// Simulate tape drives (only for test and debug)
#[serde(rename_all = "kebab-case")]
pub struct VirtualTapeDrive {
pub name: String,
/// Path to directory
pub path: String,
/// Virtual tape size
#[serde(skip_serializing_if = "Option::is_none")]
pub max_size: Option<usize>,
}
#[api(
properties: {
name: {
schema: DRIVE_NAME_SCHEMA,
},
path: {
schema: LTO_DRIVE_PATH_SCHEMA,
},
changer: {
schema: CHANGER_NAME_SCHEMA,
optional: true,
},
"changer-drivenum": {
schema: CHANGER_DRIVENUM_SCHEMA,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Updater, Clone)]
#[serde(rename_all = "kebab-case")]
/// Lto SCSI tape driver
pub struct LtoTapeDrive {
#[updater(skip)]
pub name: String,
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub changer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub changer_drivenum: Option<u64>,
}
#[api(
properties: {
config: {
type: LtoTapeDrive,
},
info: {
type: OptionalDeviceIdentification,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Drive list entry
pub struct DriveListEntry {
#[serde(flatten)]
pub config: LtoTapeDrive,
#[serde(flatten)]
pub info: OptionalDeviceIdentification,
/// the state of the drive if locked
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
/// Current device activity
#[serde(skip_serializing_if = "Option::is_none")]
pub activity: Option<DeviceActivity>,
}
#[api()]
#[derive(Serialize, Deserialize)]
/// Medium auxiliary memory attributes (MAM)
pub struct MamAttribute {
/// Attribute id
pub id: u16,
/// Attribute name
pub name: String,
/// Attribute value
pub value: String,
}
#[api()]
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
/// The density of a tape medium, derived from the LTO version.
pub enum TapeDensity {
/// Unknown (no media loaded)
Unknown,
/// LTO1
LTO1,
/// LTO2
LTO2,
/// LTO3
LTO3,
/// LTO4
LTO4,
/// LTO5
LTO5,
/// LTO6
LTO6,
/// LTO7
LTO7,
/// LTO7M8
LTO7M8,
/// LTO8
LTO8,
/// LTO9
LTO9,
}
impl TryFrom<u8> for TapeDensity {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let density = match value {
0x00 => TapeDensity::Unknown,
0x40 => TapeDensity::LTO1,
0x42 => TapeDensity::LTO2,
0x44 => TapeDensity::LTO3,
0x46 => TapeDensity::LTO4,
0x58 => TapeDensity::LTO5,
0x5a => TapeDensity::LTO6,
0x5c => TapeDensity::LTO7,
0x5d => TapeDensity::LTO7M8,
0x5e => TapeDensity::LTO8,
0x60 => TapeDensity::LTO9,
_ => bail!("unknown tape density code 0x{:02x}", value),
};
Ok(density)
}
}
#[api(
properties: {
density: {
type: TapeDensity,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Drive/Media status for Lto SCSI drives.
///
/// Media related data is optional - only set if there is a medium
/// loaded.
pub struct LtoDriveAndMediaStatus {
/// Vendor
pub vendor: String,
/// Product
pub product: String,
/// Revision
pub revision: String,
/// Block size (0 is variable size)
pub blocksize: u32,
/// Compression enabled
pub compression: bool,
/// Drive buffer mode
pub buffer_mode: u8,
/// Tape density
pub density: TapeDensity,
/// Media is write protected
#[serde(skip_serializing_if = "Option::is_none")]
pub write_protect: Option<bool>,
/// Tape Alert Flags
#[serde(skip_serializing_if = "Option::is_none")]
pub alert_flags: Option<String>,
/// Current file number
#[serde(skip_serializing_if = "Option::is_none")]
pub file_number: Option<u64>,
/// Current block number
#[serde(skip_serializing_if = "Option::is_none")]
pub block_number: Option<u64>,
/// Medium Manufacture Date (epoch)
#[serde(skip_serializing_if = "Option::is_none")]
pub manufactured: Option<i64>,
/// Total Bytes Read in Medium Life
#[serde(skip_serializing_if = "Option::is_none")]
pub bytes_read: Option<u64>,
/// Total Bytes Written in Medium Life
#[serde(skip_serializing_if = "Option::is_none")]
pub bytes_written: Option<u64>,
/// Number of mounts for the current volume (i.e., Thread Count)
#[serde(skip_serializing_if = "Option::is_none")]
pub volume_mounts: Option<u64>,
/// Count of the total number of times the medium has passed over
/// the head.
#[serde(skip_serializing_if = "Option::is_none")]
pub medium_passes: Option<u64>,
/// Estimated tape wearout factor (assuming max. 16000 end-to-end passes)
#[serde(skip_serializing_if = "Option::is_none")]
pub medium_wearout: Option<f64>,
/// Current device activity
#[serde(skip_serializing_if = "Option::is_none")]
pub drive_activity: Option<DeviceActivity>,
}
#[api()]
/// Volume statistics from SCSI log page 17h
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Lp17VolumeStatistics {
/// Volume mounts (thread count)
pub volume_mounts: u64,
/// Total data sets written
pub volume_datasets_written: u64,
/// Write retries
pub volume_recovered_write_data_errors: u64,
/// Total unrecovered write errors
pub volume_unrecovered_write_data_errors: u64,
/// Total suspended writes
pub volume_write_servo_errors: u64,
/// Total fatal suspended writes
pub volume_unrecovered_write_servo_errors: u64,
/// Total datasets read
pub volume_datasets_read: u64,
/// Total read retries
pub volume_recovered_read_errors: u64,
/// Total unrecovered read errors
pub volume_unrecovered_read_errors: u64,
/// Last mount unrecovered write errors
pub last_mount_unrecovered_write_errors: u64,
/// Last mount unrecovered read errors
pub last_mount_unrecovered_read_errors: u64,
/// Last mount bytes written
pub last_mount_bytes_written: u64,
/// Last mount bytes read
pub last_mount_bytes_read: u64,
/// Lifetime bytes written
pub lifetime_bytes_written: u64,
/// Lifetime bytes read
pub lifetime_bytes_read: u64,
/// Last load write compression ratio
pub last_load_write_compression_ratio: u64,
/// Last load read compression ratio
pub last_load_read_compression_ratio: u64,
/// Medium mount time
pub medium_mount_time: u64,
/// Medium ready time
pub medium_ready_time: u64,
/// Total native capacity
pub total_native_capacity: u64,
/// Total used native capacity
pub total_used_native_capacity: u64,
/// Write protect
pub write_protect: bool,
/// Volume is WORM
pub worm: bool,
/// Beginning of medium passes
pub beginning_of_medium_passes: u64,
/// Middle of medium passes
pub middle_of_tape_passes: u64,
/// Volume serial number
pub serial: String,
}
/// The DT Device Activity from DT Device Status LP page
#[api]
#[derive(Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum DeviceActivity {
/// No activity
NoActivity,
/// Cleaning
Cleaning,
/// Loading
Loading,
/// Unloading
Unloading,
/// Other unspecified activity
Other,
/// Reading
Reading,
/// Writing
Writing,
/// Locating
Locating,
/// Rewinding
Rewinding,
/// Erasing
Erasing,
/// Formatting
Formatting,
/// Calibrating
Calibrating,
/// Other (DT)
OtherDT,
/// Updating microcode
MicrocodeUpdate,
/// Reading encrypted data
ReadingEncrypted,
/// Writing encrypted data
WritingEncrypted,
}
impl TryFrom<u8> for DeviceActivity {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0x00 => DeviceActivity::NoActivity,
0x01 => DeviceActivity::Cleaning,
0x02 => DeviceActivity::Loading,
0x03 => DeviceActivity::Unloading,
0x04 => DeviceActivity::Other,
0x05 => DeviceActivity::Reading,
0x06 => DeviceActivity::Writing,
0x07 => DeviceActivity::Locating,
0x08 => DeviceActivity::Rewinding,
0x09 => DeviceActivity::Erasing,
0x0A => DeviceActivity::Formatting,
0x0B => DeviceActivity::Calibrating,
0x0C => DeviceActivity::OtherDT,
0x0D => DeviceActivity::MicrocodeUpdate,
0x0E => DeviceActivity::ReadingEncrypted,
0x0F => DeviceActivity::WritingEncrypted,
other => bail!("invalid DT device activity value: {:x}", other),
})
}
}

View File

@ -0,0 +1,179 @@
use ::serde::{Deserialize, Serialize};
use proxmox_schema::*;
use proxmox_uuid::Uuid;
use crate::{MediaLocation, MediaStatus, UUID_FORMAT};
pub const MEDIA_SET_UUID_SCHEMA: Schema = StringSchema::new(
"MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).",
)
.format(&UUID_FORMAT)
.schema();
pub const MEDIA_UUID_SCHEMA: Schema = StringSchema::new("Media Uuid.")
.format(&UUID_FORMAT)
.schema();
#[api(
properties: {
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media Set list entry
pub struct MediaSetListEntry {
/// Media set name
pub media_set_name: String,
pub media_set_uuid: Uuid,
/// MediaSet creation time stamp
pub media_set_ctime: i64,
/// Media Pool
pub pool: String,
}
#[api(
properties: {
location: {
type: MediaLocation,
},
status: {
type: MediaStatus,
},
uuid: {
schema: MEDIA_UUID_SCHEMA,
},
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media list entry
pub struct MediaListEntry {
/// Media label text (or Barcode)
pub label_text: String,
pub uuid: Uuid,
/// Creation time stamp
pub ctime: i64,
pub location: MediaLocation,
pub status: MediaStatus,
/// Expired flag
pub expired: bool,
/// Catalog status OK
pub catalog: bool,
/// Media set name
#[serde(skip_serializing_if = "Option::is_none")]
pub media_set_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_set_uuid: Option<Uuid>,
/// Media set seq_nr
#[serde(skip_serializing_if = "Option::is_none")]
pub seq_nr: Option<u64>,
/// MediaSet creation time stamp
#[serde(skip_serializing_if = "Option::is_none")]
pub media_set_ctime: Option<i64>,
/// Media Pool
#[serde(skip_serializing_if = "Option::is_none")]
pub pool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Bytes currently used
pub bytes_used: Option<u64>,
}
#[api(
properties: {
uuid: {
schema: MEDIA_UUID_SCHEMA,
},
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media label info
pub struct MediaIdFlat {
/// Unique ID
pub uuid: Uuid,
/// Media label text (or Barcode)
pub label_text: String,
/// Creation time stamp
pub ctime: i64,
// All MediaSet properties are optional here
/// MediaSet Pool
#[serde(skip_serializing_if = "Option::is_none")]
pub pool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_set_uuid: Option<Uuid>,
/// MediaSet media sequence number
#[serde(skip_serializing_if = "Option::is_none")]
pub seq_nr: Option<u64>,
/// MediaSet Creation time stamp
#[serde(skip_serializing_if = "Option::is_none")]
pub media_set_ctime: Option<i64>,
/// Encryption key fingerprint
#[serde(skip_serializing_if = "Option::is_none")]
pub encryption_key_fingerprint: Option<String>,
}
#[api(
properties: {
uuid: {
schema: MEDIA_UUID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Label with optional Uuid
pub struct LabelUuidMap {
/// Changer label text (or Barcode)
pub label_text: String,
/// Associated Uuid (if any)
pub uuid: Option<Uuid>,
}
#[api(
properties: {
uuid: {
schema: MEDIA_UUID_SCHEMA,
},
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Media content list entry
pub struct MediaContentEntry {
/// Media label text (or Barcode)
pub label_text: String,
/// Media Uuid
pub uuid: Uuid,
/// Media set name
pub media_set_name: String,
/// Media set uuid
pub media_set_uuid: Uuid,
/// MediaSet Creation time stamp
pub media_set_ctime: i64,
/// Media set seq_nr
pub seq_nr: u64,
/// Media Pool
pub pool: String,
/// Datastore Name
pub store: String,
/// Backup snapshot
pub snapshot: String,
/// Snapshot creation time (epoch)
pub backup_time: i64,
}

View File

@ -0,0 +1,80 @@
use anyhow::{bail, Error};
use proxmox_schema::{ApiStringFormat, Schema, StringSchema};
use crate::{CHANGER_NAME_SCHEMA, PROXMOX_SAFE_ID_FORMAT};
pub const VAULT_NAME_SCHEMA: Schema = StringSchema::new("Vault name.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
#[derive(Debug, PartialEq, Eq, Clone)]
/// Media location
pub enum MediaLocation {
/// Ready for use (inside tape library)
Online(String),
/// Local available, but need to be mounted (insert into tape
/// drive)
Offline,
/// Media is inside a Vault
Vault(String),
}
proxmox_serde::forward_deserialize_to_from_str!(MediaLocation);
proxmox_serde::forward_serialize_to_display!(MediaLocation);
impl proxmox_schema::ApiType for MediaLocation {
const API_SCHEMA: Schema = StringSchema::new(
"Media location (e.g. 'offline', 'online-<changer_name>', 'vault-<vault_name>')",
)
.format(&ApiStringFormat::VerifyFn(|text| {
let location: MediaLocation = text.parse()?;
match location {
MediaLocation::Online(ref changer) => {
CHANGER_NAME_SCHEMA.parse_simple_value(changer)?;
}
MediaLocation::Vault(ref vault) => {
VAULT_NAME_SCHEMA.parse_simple_value(vault)?;
}
MediaLocation::Offline => { /* OK */ }
}
Ok(())
}))
.schema();
}
impl std::fmt::Display for MediaLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MediaLocation::Offline => {
write!(f, "offline")
}
MediaLocation::Online(changer) => {
write!(f, "online-{}", changer)
}
MediaLocation::Vault(vault) => {
write!(f, "vault-{}", vault)
}
}
}
}
impl std::str::FromStr for MediaLocation {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "offline" {
return Ok(MediaLocation::Offline);
}
if let Some(changer) = s.strip_prefix("online-") {
return Ok(MediaLocation::Online(changer.to_string()));
}
if let Some(vault) = s.strip_prefix("vault-") {
return Ok(MediaLocation::Vault(vault.to_string()));
}
bail!("MediaLocation parse error");
}
}

View File

@ -0,0 +1,161 @@
//! Types for tape media pool API
//!
//! Note: Both MediaSetPolicy and RetentionPolicy are complex enums,
//! so we cannot use them directly for the API. Instead, we represent
//! them as String.
use std::str::FromStr;
use anyhow::Error;
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, ApiStringFormat, Schema, StringSchema, Updater};
use proxmox_time::{CalendarEvent, TimeSpan};
use crate::{
PROXMOX_SAFE_ID_FORMAT, SINGLE_LINE_COMMENT_FORMAT, SINGLE_LINE_COMMENT_SCHEMA,
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
};
pub const MEDIA_POOL_NAME_SCHEMA: Schema = StringSchema::new("Media pool name.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(2)
.max_length(32)
.schema();
pub const MEDIA_SET_NAMING_TEMPLATE_SCHEMA: Schema = StringSchema::new(
"Media set naming template (may contain strftime() time format specifications).",
)
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
pub const MEDIA_SET_ALLOCATION_POLICY_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|s| {
MediaSetPolicy::from_str(s)?;
Ok(())
});
pub const MEDIA_SET_ALLOCATION_POLICY_SCHEMA: Schema =
StringSchema::new("Media set allocation policy ('continue', 'always', or a calendar event).")
.format(&MEDIA_SET_ALLOCATION_POLICY_FORMAT)
.schema();
/// Media set allocation policy
pub enum MediaSetPolicy {
/// Try to use the current media set
ContinueCurrent,
/// Each backup job creates a new media set
AlwaysCreate,
/// Create a new set when the specified CalendarEvent triggers
CreateAt(CalendarEvent),
}
impl std::str::FromStr for MediaSetPolicy {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "continue" {
return Ok(MediaSetPolicy::ContinueCurrent);
}
if s == "always" {
return Ok(MediaSetPolicy::AlwaysCreate);
}
let event = s.parse()?;
Ok(MediaSetPolicy::CreateAt(event))
}
}
pub const MEDIA_RETENTION_POLICY_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|s| {
RetentionPolicy::from_str(s)?;
Ok(())
});
pub const MEDIA_RETENTION_POLICY_SCHEMA: Schema =
StringSchema::new("Media retention policy ('overwrite', 'keep', or time span).")
.format(&MEDIA_RETENTION_POLICY_FORMAT)
.schema();
/// Media retention Policy
pub enum RetentionPolicy {
/// Always overwrite media
OverwriteAlways,
/// Protect data for the timespan specified
ProtectFor(TimeSpan),
/// Never overwrite data
KeepForever,
}
impl std::str::FromStr for RetentionPolicy {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "overwrite" {
return Ok(RetentionPolicy::OverwriteAlways);
}
if s == "keep" {
return Ok(RetentionPolicy::KeepForever);
}
let time_span = s.parse()?;
Ok(RetentionPolicy::ProtectFor(time_span))
}
}
#[api(
properties: {
name: {
schema: MEDIA_POOL_NAME_SCHEMA,
},
allocation: {
schema: MEDIA_SET_ALLOCATION_POLICY_SCHEMA,
optional: true,
},
retention: {
schema: MEDIA_RETENTION_POLICY_SCHEMA,
optional: true,
},
template: {
schema: MEDIA_SET_NAMING_TEMPLATE_SCHEMA,
optional: true,
},
encrypt: {
schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
optional: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
},
)]
#[derive(Serialize, Deserialize, Updater)]
/// Media pool configuration
pub struct MediaPoolConfig {
/// The pool name
#[updater(skip)]
pub name: String,
/// Media Set allocation policy
#[serde(skip_serializing_if = "Option::is_none")]
pub allocation: Option<String>,
/// Media retention policy
#[serde(skip_serializing_if = "Option::is_none")]
pub retention: Option<String>,
/// Media set naming template (default "%c")
///
/// The template is UTF8 text, and can include strftime time
/// format specifications.
#[serde(skip_serializing_if = "Option::is_none")]
pub template: Option<String>,
/// Encryption key fingerprint
///
/// If set, encrypt all data using the specified key.
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
}

View File

@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::api;
#[api()]
/// Media status
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Media Status
pub enum MediaStatus {
/// Media is ready to be written
Writable,
/// Media is full (contains data)
Full,
/// Media is marked as unknown, needs rescan
Unknown,
/// Media is marked as damaged
Damaged,
/// Media is marked as retired
Retired,
}

View File

@ -0,0 +1,92 @@
//! Types for tape backup API
mod device;
pub use device::*;
mod changer;
pub use changer::*;
mod drive;
pub use drive::*;
mod media_pool;
pub use media_pool::*;
mod media_status;
pub use media_status::*;
mod media_location;
pub use media_location::*;
mod media;
pub use media::*;
use const_format::concatcp;
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, const_regex, ApiStringFormat, Schema, StringSchema};
use proxmox_uuid::Uuid;
use crate::{
BackupType, BACKUP_ID_SCHEMA, BACKUP_NS_PATH_RE, FINGERPRINT_SHA256_FORMAT,
PROXMOX_SAFE_ID_REGEX_STR, SNAPSHOT_PATH_REGEX_STR,
};
const_regex! {
pub TAPE_RESTORE_SNAPSHOT_REGEX = concatcp!(r"^", PROXMOX_SAFE_ID_REGEX_STR, r":(?:", BACKUP_NS_PATH_RE,")?", SNAPSHOT_PATH_REGEX_STR, r"$");
}
pub const TAPE_RESTORE_SNAPSHOT_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX);
pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA: Schema =
StringSchema::new("Tape encryption key fingerprint (sha256).")
.format(&FINGERPRINT_SHA256_FORMAT)
.schema();
pub const TAPE_RESTORE_SNAPSHOT_SCHEMA: Schema =
StringSchema::new("A snapshot in the format: 'store:[ns/namespace/...]type/id/time")
.format(&TAPE_RESTORE_SNAPSHOT_FORMAT)
.type_text("store:[ns/namespace/...]type/id/time")
.schema();
#[api(
properties: {
pool: {
schema: MEDIA_POOL_NAME_SCHEMA,
optional: true,
},
"label-text": {
schema: MEDIA_LABEL_SCHEMA,
optional: true,
},
"media": {
schema: MEDIA_UUID_SCHEMA,
optional: true,
},
"media-set": {
schema: MEDIA_SET_UUID_SCHEMA,
optional: true,
},
"backup-type": {
type: BackupType,
optional: true,
},
"backup-id": {
schema: BACKUP_ID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Content list filter parameters
pub struct MediaContentListFilter {
pub pool: Option<String>,
pub label_text: Option<String>,
pub media: Option<Uuid>,
pub media_set: Option<Uuid>,
pub backup_type: Option<BackupType>,
pub backup_id: Option<String>,
}

View File

@ -0,0 +1,168 @@
use serde::{Deserialize, Serialize};
use proxmox_human_byte::HumanByte;
use proxmox_schema::{api, ApiType, Schema, StringSchema, Updater};
use crate::{
CIDR_SCHEMA, DAILY_DURATION_FORMAT, PROXMOX_SAFE_ID_FORMAT, SINGLE_LINE_COMMENT_SCHEMA,
};
pub const TRAFFIC_CONTROL_TIMEFRAME_SCHEMA: Schema =
StringSchema::new("Timeframe to specify when the rule is active.")
.format(&DAILY_DURATION_FORMAT)
.schema();
pub const TRAFFIC_CONTROL_ID_SCHEMA: Schema = StringSchema::new("Rule ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
#[api(
properties: {
"rate-in": {
type: HumanByte,
optional: true,
},
"burst-in": {
type: HumanByte,
optional: true,
},
"rate-out": {
type: HumanByte,
optional: true,
},
"burst-out": {
type: HumanByte,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize, Default, Clone, Updater, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Rate Limit Configuration
pub struct RateLimitConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_in: Option<HumanByte>,
#[serde(skip_serializing_if = "Option::is_none")]
pub burst_in: Option<HumanByte>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_out: Option<HumanByte>,
#[serde(skip_serializing_if = "Option::is_none")]
pub burst_out: Option<HumanByte>,
}
impl RateLimitConfig {
pub fn with_same_inout(rate: Option<HumanByte>, burst: Option<HumanByte>) -> Self {
Self {
rate_in: rate,
burst_in: burst,
rate_out: rate,
burst_out: burst,
}
}
/// Create a [RateLimitConfig] from a [ClientRateLimitConfig]
pub fn from_client_config(limit: ClientRateLimitConfig) -> Self {
Self::with_same_inout(limit.rate, limit.burst)
}
}
const CLIENT_RATE_LIMIT_SCHEMA: Schema = HumanByte::API_SCHEMA
.unwrap_string_schema_cloned()
.description("Rate limit (for Token bucket filter) in bytes/s with optional unit (B, KB (base 10), MB, GB, ..., KiB (base 2), MiB, Gib, ...).")
.schema();
const CLIENT_BURST_SCHEMA: Schema = HumanByte::API_SCHEMA
.unwrap_string_schema_cloned()
.description("Size of the token bucket (for Token bucket filter) in bytes with optional unit (B, KB (base 10), MB, GB, ..., KiB (base 2), MiB, Gib, ...).")
.schema();
#[api(
properties: {
rate: {
schema: CLIENT_RATE_LIMIT_SCHEMA,
optional: true,
},
burst: {
schema: CLIENT_BURST_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize, Default, Clone)]
#[serde(rename_all = "kebab-case")]
/// Client Rate Limit Configuration
pub struct ClientRateLimitConfig {
#[serde(skip_serializing_if = "Option::is_none")]
rate: Option<HumanByte>,
#[serde(skip_serializing_if = "Option::is_none")]
burst: Option<HumanByte>,
}
#[api(
properties: {
name: {
schema: TRAFFIC_CONTROL_ID_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
limit: {
type: RateLimitConfig,
},
network: {
type: Array,
items: {
schema: CIDR_SCHEMA,
},
},
timeframe: {
type: Array,
items: {
schema: TRAFFIC_CONTROL_TIMEFRAME_SCHEMA,
},
optional: true,
},
},
)]
#[derive(Clone, Serialize, Deserialize, PartialEq, Updater)]
#[serde(rename_all = "kebab-case")]
/// Traffic control rule
pub struct TrafficControlRule {
#[updater(skip)]
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
/// Rule applies to Source IPs within this networks
pub network: Vec<String>,
#[serde(flatten)]
pub limit: RateLimitConfig,
// fixme: expose this?
// /// Bandwidth is shared across all connections
// #[serde(skip_serializing_if="Option::is_none")]
// pub shared: Option<bool>,
/// Enable the rule at specific times
#[serde(skip_serializing_if = "Option::is_none")]
pub timeframe: Option<Vec<String>>,
}
#[api(
properties: {
config: {
type: TrafficControlRule,
},
},
)]
#[derive(Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// Traffic control rule config with current rates
pub struct TrafficControlCurrentRate {
#[serde(flatten)]
pub config: TrafficControlRule,
/// Current ingress rate in bytes/second
pub cur_rate_in: u64,
/// Current egress rate in bytes/second
pub cur_rate_out: u64,
}

231
pbs-api-types/src/user.rs Normal file
View File

@ -0,0 +1,231 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::{api, BooleanSchema, IntegerSchema, Schema, StringSchema, Updater};
use super::userid::{Authid, Userid, PROXMOX_TOKEN_ID_SCHEMA};
use super::{SINGLE_LINE_COMMENT_FORMAT, SINGLE_LINE_COMMENT_SCHEMA};
pub const ENABLE_USER_SCHEMA: Schema = BooleanSchema::new(
"Enable the account (default). You can set this to '0' to disable the account.",
)
.default(true)
.schema();
pub const EXPIRE_USER_SCHEMA: Schema = IntegerSchema::new(
"Account expiration date (seconds since epoch). '0' means no expiration date.",
)
.default(0)
.minimum(0)
.schema();
pub const REGENERATE_TOKEN_SCHEMA: Schema =
BooleanSchema::new("Regenerate token secret while keeping permissions.")
.default(false)
.schema();
pub const FIRST_NAME_SCHEMA: Schema = StringSchema::new("First name.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
pub const LAST_NAME_SCHEMA: Schema = StringSchema::new("Last name.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
pub const EMAIL_SCHEMA: Schema = StringSchema::new("E-Mail Address.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
#[api(
properties: {
userid: {
type: Userid,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
enable: {
optional: true,
schema: ENABLE_USER_SCHEMA,
},
expire: {
optional: true,
schema: EXPIRE_USER_SCHEMA,
},
firstname: {
optional: true,
schema: FIRST_NAME_SCHEMA,
},
lastname: {
schema: LAST_NAME_SCHEMA,
optional: true,
},
email: {
schema: EMAIL_SCHEMA,
optional: true,
},
tokens: {
type: Array,
optional: true,
description: "List of user's API tokens.",
items: {
type: ApiToken
},
},
"totp-locked": {
type: bool,
optional: true,
default: false,
description: "True if the user is currently locked out of TOTP factors",
},
"tfa-locked-until": {
optional: true,
description: "Contains a timestamp until when a user is locked out of 2nd factors",
},
}
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// User properties with added list of ApiTokens
pub struct UserWithTokens {
pub userid: Userid,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub tokens: Vec<ApiToken>,
#[serde(skip_serializing_if = "bool_is_false", default)]
pub totp_locked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub tfa_locked_until: Option<i64>,
}
fn bool_is_false(b: &bool) -> bool {
!b
}
#[api(
properties: {
tokenid: {
schema: PROXMOX_TOKEN_ID_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
enable: {
optional: true,
schema: ENABLE_USER_SCHEMA,
},
expire: {
optional: true,
schema: EXPIRE_USER_SCHEMA,
},
}
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
/// ApiToken properties.
pub struct ApiToken {
pub tokenid: Authid,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>,
}
impl ApiToken {
pub fn is_active(&self) -> bool {
if !self.enable.unwrap_or(true) {
return false;
}
if let Some(expire) = self.expire {
let now = proxmox_time::epoch_i64();
if expire > 0 && expire <= now {
return false;
}
}
true
}
}
#[api(
properties: {
userid: {
type: Userid,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
enable: {
optional: true,
schema: ENABLE_USER_SCHEMA,
},
expire: {
optional: true,
schema: EXPIRE_USER_SCHEMA,
},
firstname: {
optional: true,
schema: FIRST_NAME_SCHEMA,
},
lastname: {
schema: LAST_NAME_SCHEMA,
optional: true,
},
email: {
schema: EMAIL_SCHEMA,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Updater, PartialEq, Eq)]
/// User properties.
pub struct User {
#[updater(skip)]
pub userid: Userid,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
impl User {
pub fn is_active(&self) -> bool {
if !self.enable.unwrap_or(true) {
return false;
}
if let Some(expire) = self.expire {
let now = proxmox_time::epoch_i64();
if expire > 0 && expire <= now {
return false;
}
}
true
}
}

View File

@ -0,0 +1,190 @@
//! Defines the types for the api version info endpoint
use std::cmp::Ordering;
use std::convert::TryFrom;
use anyhow::{format_err, Context};
use proxmox_schema::api;
#[api(
description: "Api version information",
properties: {
"version": {
description: "Version 'major.minor'",
type: String,
},
"release": {
description: "Version release",
type: String,
},
"repoid": {
description: "Version repository id",
type: String,
},
}
)]
#[derive(serde::Deserialize, serde::Serialize)]
pub struct ApiVersionInfo {
pub version: String,
pub release: String,
pub repoid: String,
}
pub type ApiVersionMajor = u64;
pub type ApiVersionMinor = u64;
pub type ApiVersionRelease = u64;
#[derive(PartialEq, Eq)]
pub struct ApiVersion {
pub major: ApiVersionMajor,
pub minor: ApiVersionMinor,
pub release: ApiVersionRelease,
}
impl TryFrom<ApiVersionInfo> for ApiVersion {
type Error = anyhow::Error;
fn try_from(value: ApiVersionInfo) -> Result<Self, Self::Error> {
let (major, minor) = value
.version
.split_once('.')
.ok_or_else(|| format_err!("malformed API version {}", value.version))?;
let major: ApiVersionMajor = major
.parse()
.with_context(|| "failed to parse major version")?;
let minor: ApiVersionMinor = minor
.parse()
.with_context(|| "failed to parse minor version")?;
let release: ApiVersionRelease = value
.release
.parse()
.with_context(|| "failed to parse release version")?;
Ok(Self {
major,
minor,
release,
})
}
}
impl PartialOrd for ApiVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let ordering = match (
self.major.cmp(&other.major),
self.minor.cmp(&other.minor),
self.release.cmp(&other.release),
) {
(Ordering::Equal, Ordering::Equal, ordering) => ordering,
(Ordering::Equal, ordering, _) => ordering,
(ordering, _, _) => ordering,
};
Some(ordering)
}
}
impl ApiVersion {
pub fn new(major: ApiVersionMajor, minor: ApiVersionMinor, release: ApiVersionRelease) -> Self {
Self {
major,
minor,
release,
}
}
}
#[test]
fn same_level_version_comarison() {
let major_base = ApiVersion::new(2, 0, 0);
let major_less = ApiVersion::new(1, 0, 0);
let major_greater = ApiVersion::new(3, 0, 0);
let minor_base = ApiVersion::new(2, 2, 0);
let minor_less = ApiVersion::new(2, 1, 0);
let minor_greater = ApiVersion::new(2, 3, 0);
let release_base = ApiVersion::new(2, 2, 2);
let release_less = ApiVersion::new(2, 2, 1);
let release_greater = ApiVersion::new(2, 2, 3);
assert!(major_base == major_base);
assert!(minor_base == minor_base);
assert!(release_base == release_base);
assert!(major_base > major_less);
assert!(major_base >= major_less);
assert!(major_base != major_less);
assert!(major_base < major_greater);
assert!(major_base <= major_greater);
assert!(major_base != major_greater);
assert!(minor_base > minor_less);
assert!(minor_base >= minor_less);
assert!(minor_base != minor_less);
assert!(minor_base < minor_greater);
assert!(minor_base <= minor_greater);
assert!(minor_base != minor_greater);
assert!(release_base > release_less);
assert!(release_base >= release_less);
assert!(release_base != release_less);
assert!(release_base < release_greater);
assert!(release_base <= release_greater);
assert!(release_base != release_greater);
}
#[test]
fn mixed_level_version_comarison() {
let major_base = ApiVersion::new(2, 0, 0);
let major_less = ApiVersion::new(1, 0, 0);
let major_greater = ApiVersion::new(3, 0, 0);
let minor_base = ApiVersion::new(2, 2, 0);
let minor_less = ApiVersion::new(2, 1, 0);
let minor_greater = ApiVersion::new(2, 3, 0);
let release_base = ApiVersion::new(2, 2, 2);
let release_less = ApiVersion::new(2, 2, 1);
let release_greater = ApiVersion::new(2, 2, 3);
assert!(major_base < minor_base);
assert!(major_base < minor_less);
assert!(major_base < minor_greater);
assert!(major_base < release_base);
assert!(major_base < release_less);
assert!(major_base < release_greater);
assert!(major_less < minor_base);
assert!(major_less < minor_less);
assert!(major_less < minor_greater);
assert!(major_less < release_base);
assert!(major_less < release_less);
assert!(major_less < release_greater);
assert!(major_greater > minor_base);
assert!(major_greater > minor_less);
assert!(major_greater > minor_greater);
assert!(major_greater > release_base);
assert!(major_greater > release_less);
assert!(major_greater > release_greater);
assert!(minor_base < release_base);
assert!(minor_base < release_less);
assert!(minor_base < release_greater);
assert!(minor_greater > release_base);
assert!(minor_greater > release_less);
assert!(minor_greater > release_greater);
assert!(minor_less < release_base);
assert!(minor_less < release_less);
assert!(minor_less < release_greater);
}

78
pbs-api-types/src/zfs.rs Normal file
View File

@ -0,0 +1,78 @@
use serde::{Deserialize, Serialize};
use proxmox_schema::*;
const_regex! {
pub ZPOOL_NAME_REGEX = r"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
}
pub const ZFS_ASHIFT_SCHEMA: Schema = IntegerSchema::new("Pool sector size exponent.")
.minimum(9)
.maximum(16)
.default(12)
.schema();
pub const ZPOOL_NAME_SCHEMA: Schema = StringSchema::new("ZFS Pool Name")
.format(&ApiStringFormat::Pattern(&ZPOOL_NAME_REGEX))
.schema();
#[api(default: "On")]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// The ZFS compression algorithm to use.
pub enum ZfsCompressionType {
/// Gnu Zip
Gzip,
/// LZ4
Lz4,
/// LZJB
Lzjb,
/// ZLE
Zle,
/// ZStd
ZStd,
/// Enable compression using the default algorithm.
On,
/// Disable compression.
Off,
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// The ZFS RAID level to use.
pub enum ZfsRaidLevel {
/// Single Disk
Single,
/// Mirror
Mirror,
/// Raid10
Raid10,
/// RaidZ
RaidZ,
/// RaidZ2
RaidZ2,
/// RaidZ3
RaidZ3,
}
#[api()]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// zpool list item
pub struct ZpoolListItem {
/// zpool name
pub name: String,
/// Health
pub health: String,
/// Total size
pub size: u64,
/// Used size
pub alloc: u64,
/// Free space
pub free: u64,
/// ZFS fragnentation level
pub frag: u64,
/// ZFS deduplication ratio
pub dedup: f64,
}

View File

@ -0,0 +1,76 @@
use pbs_api_types::{BackupGroup, BackupType, GroupFilter};
use std::str::FromStr;
#[test]
fn test_no_filters() {
let group_filters = vec![];
let do_backup = [
"vm/101", "vm/102", "vm/103", "vm/104", "vm/105", "vm/106", "vm/107", "vm/108", "vm/109",
];
for id in do_backup {
assert!(BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
}
#[test]
fn test_include_filters() {
let group_filters = vec![GroupFilter::from_str("regex:.*10[2-8]").unwrap()];
let do_backup = [
"vm/102", "vm/103", "vm/104", "vm/105", "vm/106", "vm/107", "vm/108",
];
let dont_backup = ["vm/101", "vm/109"];
for id in do_backup {
assert!(BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
for id in dont_backup {
assert!(!BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
}
#[test]
fn test_exclude_filters() {
let group_filters = [
GroupFilter::from_str("exclude:regex:.*10[1-3]").unwrap(),
GroupFilter::from_str("exclude:regex:.*10[5-7]").unwrap(),
];
let do_backup = ["vm/104", "vm/108", "vm/109"];
let dont_backup = ["vm/101", "vm/102", "vm/103", "vm/105", "vm/106", "vm/107"];
for id in do_backup {
assert!(BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
for id in dont_backup {
assert!(!BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
}
#[test]
fn test_include_and_exclude_filters() {
let group_filters = [
GroupFilter::from_str("exclude:regex:.*10[1-3]").unwrap(),
GroupFilter::from_str("regex:.*10[2-8]").unwrap(),
GroupFilter::from_str("exclude:regex:.*10[5-7]").unwrap(),
];
let do_backup = ["vm/104", "vm/108"];
let dont_backup = [
"vm/101", "vm/102", "vm/103", "vm/105", "vm/106", "vm/107", "vm/109",
];
for id in do_backup {
assert!(BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
for id in dont_backup {
assert!(!BackupGroup::new(BackupType::Vm, id).apply_filters(&group_filters));
}
}

View File

@ -0,0 +1,43 @@
[package]
name = "proxmox-access-control"
description = "A collection of utilities to implement access control management."
version = "1.0.0"
authors.workspace = true
edition.workspace = true
exclude.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
anyhow.workspace = true
nix = { workspace = true, optional = true }
openssl = { workspace = true, optional = true }
serde.workspace = true
serde_json = { workspace = true, optional = true }
proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
proxmox-config-digest = { workspace = true, optional = true, features = [ "openssl" ] }
proxmox-product-config = { workspace = true, optional = true }
proxmox-router = { workspace = true, optional = true }
proxmox-schema.workspace = true
proxmox-section-config = { workspace = true, optional = true }
proxmox-shared-memory = { workspace = true, optional = true }
proxmox-sys = { workspace = true, features = [ "crypt" ], optional = true }
proxmox-time = { workspace = true }
[features]
default = []
impl = [
"dep:nix",
"dep:openssl",
"dep:proxmox-config-digest",
"dep:proxmox-product-config",
"dep:proxmox-router",
"dep:proxmox-section-config",
"dep:proxmox-shared-memory",
"dep:proxmox-sys",
"dep:serde_json",
]

View File

@ -0,0 +1,64 @@
rust-proxmox-access-control (1.0.0-1) trixie; urgency=medium
* re-build for Debian Trixie based releases.
-- Proxmox Support Team <support@proxmox.com> Fri, 30 May 2025 18:46:19 +0200
rust-proxmox-access-control (0.2.5-1) bookworm; urgency=medium
* rebuild with section-config 3.0
* clippy fixes
* doc fixes
-- Proxmox Support Team <support@proxmox.com> Tue, 06 May 2025 11:50:08 +0200
rust-proxmox-access-control (0.2.4-1) bookworm; urgency=medium
* rebuild with proxmox-schema 4.0
-- Proxmox Support Team <support@proxmox.com> Wed, 15 Jan 2025 12:47:56 +0100
rust-proxmox-access-control (0.2.3-1) bookworm; urgency=medium
* upgrade to current proxmox-router
* doc fixup
-- Proxmox Support Team <support@proxmox.com> Thu, 05 Sep 2024 14:25:02 +0200
rust-proxmox-access-control (0.2.2-1) bookworm; urgency=medium
* add init_user_config() to AccessControlConfig with a default
implementation so downstream can ensure default users such as 'root@pam'
are enabled if so desired
-- Proxmox Support Team <support@proxmox.com> Mon, 22 Jul 2024 09:04:59 +0200
rust-proxmox-access-control (0.2.1-1) bookworm; urgency=medium
* rebuild with proxmox-sys 6.0
-- Proxmox Support Team <support@proxmox.com> Thu, 11 Jul 2024 14:46:29 +0200
rust-proxmox-access-control (0.2.0-1) bookworm; urgency=medium
* change acl::config() and user::config() to return a ConfigDigest instead
of raw digest bytes
* various clippy fixes
-- Proxmox Support Team <support@proxmox.com> Thu, 04 Jul 2024 14:32:54 +0200
rust-proxmox-access-control (0.1.1-1) bookworm; urgency=medium
* upgrade proxmox-time to 2.0
-- Proxmox Support Team <support@proxmox.com> Thu, 20 Jun 2024 13:47:21 +0200
rust-proxmox-access-control (0.1.0-1) bookworm; urgency=medium
* initial packaging
-- Proxmox Support Team <support@proxmox.com> Wed, 19 Jun 2024 14:44:26 +0200

View File

@ -0,0 +1,70 @@
Source: rust-proxmox-access-control
Section: rust
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-sequence-cargo
Build-Depends-Arch: cargo:native <!nocheck>,
rustc:native (>= 1.82) <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-anyhow-1+default-dev <!nocheck>,
librust-proxmox-auth-api-1+api-types-dev <!nocheck>,
librust-proxmox-auth-api-1+default-dev <!nocheck>,
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-time-2+default-dev (>= 2.1.0-~~) <!nocheck>,
librust-serde-1+default-dev <!nocheck>
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.7.0
Vcs-Git: git://git.proxmox.com/git/proxmox.git
Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
Homepage: https://proxmox.com
X-Cargo-Crate: proxmox-access-control
Rules-Requires-Root: no
Package: librust-proxmox-access-control-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-anyhow-1+default-dev,
librust-proxmox-auth-api-1+api-types-dev,
librust-proxmox-auth-api-1+default-dev,
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~),
librust-proxmox-time-2+default-dev (>= 2.1.0-~~),
librust-serde-1+default-dev
Suggests:
librust-proxmox-access-control+impl-dev (= ${binary:Version})
Provides:
librust-proxmox-access-control+default-dev (= ${binary:Version}),
librust-proxmox-access-control-1-dev (= ${binary:Version}),
librust-proxmox-access-control-1+default-dev (= ${binary:Version}),
librust-proxmox-access-control-1.0-dev (= ${binary:Version}),
librust-proxmox-access-control-1.0+default-dev (= ${binary:Version}),
librust-proxmox-access-control-1.0.0-dev (= ${binary:Version}),
librust-proxmox-access-control-1.0.0+default-dev (= ${binary:Version})
Description: Collection of utilities to implement access control management - Rust source code
Source code for Debianized Rust crate "proxmox-access-control"
Package: librust-proxmox-access-control+impl-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-access-control-dev (= ${binary:Version}),
librust-nix-0.29+default-dev,
librust-openssl-0.10+default-dev,
librust-proxmox-config-digest-1+default-dev,
librust-proxmox-config-digest-1+openssl-dev,
librust-proxmox-product-config-1+default-dev,
librust-proxmox-router-3+default-dev (>= 3.2.0-~~),
librust-proxmox-section-config-3+default-dev (>= 3.1.0-~~),
librust-proxmox-shared-memory-1+default-dev,
librust-proxmox-sys-1+crypt-dev,
librust-proxmox-sys-1+default-dev,
librust-serde-json-1+default-dev
Provides:
librust-proxmox-access-control-1+impl-dev (= ${binary:Version}),
librust-proxmox-access-control-1.0+impl-dev (= ${binary:Version}),
librust-proxmox-access-control-1.0.0+impl-dev (= ${binary:Version})
Description: Collection of utilities to implement access control management - feature "impl"
This metapackage enables feature "impl" for the Rust proxmox-access-control
crate, by pulling in any additional dependencies needed by that feature.

View File

@ -0,0 +1,18 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Files:
*
Copyright: 2024 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/>.

View File

@ -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"

View File

@ -0,0 +1,992 @@
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::io::Write;
use std::path::Path;
use std::sync::{Arc, OnceLock, RwLock};
use anyhow::{bail, Error};
use proxmox_auth_api::types::{Authid, Userid};
use proxmox_config_digest::ConfigDigest;
use proxmox_product_config::{open_api_lockfile, replace_privileged_config, ApiLockGuard};
use crate::init::{access_conf, acl_config, acl_config_lock};
pub fn split_acl_path(path: &str) -> Vec<&str> {
let items = path.split('/');
let mut components = vec![];
for name in items {
if name.is_empty() {
continue;
}
components.push(name);
}
components
}
/// Tree representing a parsed acl.cfg
#[derive(Default)]
pub struct AclTree {
/// Root node of the tree.
///
/// The rest of the tree is available via [find_node()](AclTree::find_node()) or an
/// [`AclTreeNode`]'s [children](AclTreeNode::children) member.
pub root: AclTreeNode,
}
/// Node representing ACLs for a certain ACL path.
#[derive(Default)]
pub struct AclTreeNode {
/// `User` or `Token` ACLs for this node.
pub users: HashMap<Authid, HashMap<String, bool>>,
/// `Group` ACLs for this node (not yet implemented)
pub groups: HashMap<String, HashMap<String, bool>>,
/// `AclTreeNodes` representing ACL paths directly below the current one.
pub children: BTreeMap<String, AclTreeNode>,
}
impl AclTreeNode {
/// Creates a new, empty AclTreeNode.
pub fn new() -> Self {
Self {
users: HashMap::new(),
groups: HashMap::new(),
children: BTreeMap::new(),
}
}
/// Returns applicable role and their propagation status for a given [Authid].
///
/// If the `Authid` is a `User` that has no specific `Roles` configured on this node,
/// applicable `Group` roles will be returned instead.
///
/// If `leaf` is `false`, only those roles where the propagate flag in the ACL is set to `true`
/// are returned. Otherwise, all roles will be returned.
pub fn extract_roles(&self, auth_id: &Authid, leaf: bool) -> HashMap<String, bool> {
let user_roles = self.extract_user_roles(auth_id, leaf);
if !user_roles.is_empty() || auth_id.is_token() {
// user privs always override group privs
return user_roles;
};
self.extract_group_roles(auth_id.user(), leaf)
}
fn extract_user_roles(&self, auth_id: &Authid, leaf: bool) -> HashMap<String, bool> {
let mut map = HashMap::new();
let roles = match self.users.get(auth_id) {
Some(m) => m,
None => return map,
};
for (role, propagate) in roles {
if *propagate || leaf {
if access_conf().role_no_access() == Some(role) {
// return a map with a single role 'NoAccess'
let mut map = HashMap::new();
map.insert(role.to_string(), false);
return map;
}
map.insert(role.to_string(), *propagate);
}
}
map
}
fn extract_group_roles(&self, _user: &Userid, leaf: bool) -> HashMap<String, bool> {
let mut map = HashMap::new();
for roles in self.groups.values() {
let is_member = false; // fixme: check if user is member of the group
if !is_member {
continue;
}
for (role, propagate) in roles {
if *propagate || leaf {
if access_conf().role_no_access() == Some(role) {
// return a map with a single role 'NoAccess'
let mut map = HashMap::new();
map.insert(role.to_string(), false);
return map;
}
map.insert(role.to_string(), *propagate);
}
}
}
map
}
fn delete_group_role(&mut self, group: &str, role: &str) {
let roles = match self.groups.get_mut(group) {
Some(r) => r,
None => return,
};
roles.remove(role);
}
fn delete_user_role(&mut self, auth_id: &Authid, role: &str) {
let roles = match self.users.get_mut(auth_id) {
Some(r) => r,
None => return,
};
roles.remove(role);
}
fn delete_authid(&mut self, auth_id: &Authid) {
for node in self.children.values_mut() {
node.delete_authid(auth_id);
}
self.users.remove(auth_id);
}
fn insert_group_role(&mut self, group: String, role: String, propagate: bool) {
let map = self.groups.entry(group).or_default();
if let Some(no_access) = access_conf().role_no_access() {
if role == no_access {
map.clear();
} else {
map.remove(no_access);
}
}
map.insert(role, propagate);
}
fn insert_user_role(&mut self, auth_id: Authid, role: String, propagate: bool) {
let map = self.users.entry(auth_id).or_default();
if let Some(no_access) = access_conf().role_no_access() {
if role == no_access {
map.clear();
} else {
map.remove(no_access);
}
}
map.insert(role, propagate);
}
fn get_child_paths(
&self,
path: String,
auth_id: &Authid,
paths: &mut Vec<String>,
) -> Result<(), Error> {
for (sub_comp, child_node) in &self.children {
let roles = child_node.extract_roles(auth_id, true);
let child_path = format!("{path}/{sub_comp}");
if !roles.is_empty() {
paths.push(child_path.clone());
}
child_node.get_child_paths(child_path, auth_id, paths)?;
}
Ok(())
}
}
impl AclTree {
/// Create a new, empty ACL tree with a single, empty root [node](AclTreeNode)
pub fn new() -> Self {
Self {
root: AclTreeNode::new(),
}
}
/// Iterates over the tree looking for a node matching `path`.
pub fn find_node(&mut self, path: &str) -> Option<&mut AclTreeNode> {
let path = split_acl_path(path);
self.get_node_mut(&path)
}
fn get_node(&self, path: &[&str]) -> Option<&AclTreeNode> {
let mut node = &self.root;
for outer in path {
for comp in outer.split('/') {
node = node.children.get(comp)?;
}
}
Some(node)
}
fn get_node_mut(&mut self, path: &[&str]) -> Option<&mut AclTreeNode> {
let mut node = &mut self.root;
for outer in path {
for comp in outer.split('/') {
node = node.children.get_mut(comp)?;
}
}
Some(node)
}
fn get_or_insert_node(&mut self, path: &[&str]) -> &mut AclTreeNode {
let mut node = &mut self.root;
for outer in path {
for comp in outer.split('/') {
node = node.children.entry(String::from(comp)).or_default();
}
}
node
}
/// Deletes the specified `role` from the `group`'s ACL on `path`.
///
/// Never fails, even if the `path` has no ACLs configured, or the `group`/`role` combination
/// does not exist on `path`.
pub fn delete_group_role(&mut self, path: &str, group: &str, role: &str) {
let path = split_acl_path(path);
let node = match self.get_node_mut(&path) {
Some(n) => n,
None => return,
};
node.delete_group_role(group, role);
}
/// Deletes the specified `role` from the `user`'s ACL on `path`.
///
/// Never fails, even if the `path` has no ACLs configured, or the `user`/`role` combination
/// does not exist on `path`.
pub fn delete_user_role(&mut self, path: &str, auth_id: &Authid, role: &str) {
let path = split_acl_path(path);
let node = match self.get_node_mut(&path) {
Some(n) => n,
None => return,
};
node.delete_user_role(auth_id, role);
}
/// Deletes the [`AclTreeNode`] at the specified patth
///
/// Never fails, deletes a node iff the specified path exists.
pub fn delete_node(&mut self, path: &str) {
let mut path = split_acl_path(path);
let last = path.pop();
let parent = match self.get_node_mut(&path) {
Some(n) => n,
None => return,
};
if let Some(name) = last {
parent.children.remove(name);
}
}
/// Deletes a user or token from the ACL-tree
///
/// Traverses the tree in-order and removes the given user/token by their Authid
/// from every node in the tree.
pub fn delete_authid(&mut self, auth_id: &Authid) {
self.root.delete_authid(auth_id);
}
/// Inserts the specified `role` into the `group` ACL on `path`.
///
/// The [`AclTreeNode`] representing `path` will be created and inserted into the tree if
/// necessary.
pub fn insert_group_role(&mut self, path: &str, group: &str, role: &str, propagate: bool) {
let path = split_acl_path(path);
let node = self.get_or_insert_node(&path);
node.insert_group_role(group.to_string(), role.to_string(), propagate);
}
/// Inserts the specified `role` into the `user` ACL on `path`.
///
/// The [`AclTreeNode`] representing `path` will be created and inserted into the tree if
/// necessary.
pub fn insert_user_role(&mut self, path: &str, auth_id: &Authid, role: &str, propagate: bool) {
let path = split_acl_path(path);
let node = self.get_or_insert_node(&path);
node.insert_user_role(auth_id.to_owned(), role.to_string(), propagate);
}
fn write_node_config(node: &AclTreeNode, path: &str, w: &mut dyn Write) -> Result<(), Error> {
let mut role_ug_map0: HashMap<_, BTreeSet<_>> = HashMap::new();
let mut role_ug_map1: HashMap<_, BTreeSet<_>> = HashMap::new();
for (auth_id, roles) in &node.users {
// no need to save, because root is always 'Administrator'
if !auth_id.is_token() && auth_id.user() == "root@pam" {
continue;
}
for (role, propagate) in roles {
let role = role.as_str();
let auth_id = auth_id.to_string();
if *propagate {
role_ug_map1.entry(role).or_default().insert(auth_id);
} else {
role_ug_map0.entry(role).or_default().insert(auth_id);
}
}
}
for (group, roles) in &node.groups {
for (role, propagate) in roles {
let group = format!("@{}", group);
if *propagate {
role_ug_map1.entry(role).or_default().insert(group);
} else {
role_ug_map0.entry(role).or_default().insert(group);
}
}
}
fn group_by_property_list(
item_property_map: &HashMap<&str, BTreeSet<String>>,
) -> BTreeMap<String, BTreeSet<String>> {
let mut result_map: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
for (item, property_map) in item_property_map {
let item_list = property_map.iter().fold(String::new(), |mut acc, v| {
if !acc.is_empty() {
acc.push(',');
}
acc.push_str(v);
acc
});
result_map
.entry(item_list)
.or_default()
.insert(item.to_string());
}
result_map
}
let uglist_role_map0 = group_by_property_list(&role_ug_map0);
let uglist_role_map1 = group_by_property_list(&role_ug_map1);
fn role_list(roles: &BTreeSet<String>) -> String {
if let Some(no_access) = access_conf().role_no_access() {
if roles.contains(no_access) {
return String::from(no_access);
}
}
roles.iter().fold(String::new(), |mut acc, v| {
if !acc.is_empty() {
acc.push(',');
}
acc.push_str(v);
acc
})
}
for (uglist, roles) in &uglist_role_map0 {
let role_list = role_list(roles);
writeln!(
w,
"acl:0:{}:{}:{}",
if path.is_empty() { "/" } else { path },
uglist,
role_list
)?;
}
for (uglist, roles) in &uglist_role_map1 {
let role_list = role_list(roles);
writeln!(
w,
"acl:1:{}:{}:{}",
if path.is_empty() { "/" } else { path },
uglist,
role_list
)?;
}
for (name, child) in node.children.iter() {
let child_path = format!("{}/{}", path, name);
Self::write_node_config(child, &child_path, w)?;
}
Ok(())
}
fn write_config(&self, w: &mut dyn Write) -> Result<(), Error> {
Self::write_node_config(&self.root, "", w)
}
fn parse_acl_line(&mut self, line: &str) -> Result<(), Error> {
let items: Vec<&str> = line.split(':').collect();
if items.len() != 5 {
bail!("wrong number of items.");
}
if items[0] != "acl" {
bail!("line does not start with 'acl'.");
}
let propagate = if items[1] == "0" {
false
} else if items[1] == "1" {
true
} else {
bail!("expected '0' or '1' for propagate flag.");
};
let path_str = items[2];
let path = split_acl_path(path_str);
let node = self.get_or_insert_node(&path);
let uglist: Vec<&str> = items[3].split(',').map(|v| v.trim()).collect();
let rolelist: Vec<&str> = items[4].split(',').map(|v| v.trim()).collect();
for user_or_group in &uglist {
for role in &rolelist {
if !access_conf().roles().contains_key(role) {
bail!("unknown role '{}'", role);
}
if let Some(group) = user_or_group.strip_prefix('@') {
node.insert_group_role(group.to_string(), role.to_string(), propagate);
} else {
node.insert_user_role(user_or_group.parse()?, role.to_string(), propagate);
}
}
}
Ok(())
}
fn load(filename: &Path) -> Result<(Self, ConfigDigest), Error> {
let mut tree = Self::new();
let raw = match std::fs::read_to_string(filename) {
Ok(v) => v,
Err(err) => {
if err.kind() == std::io::ErrorKind::NotFound {
String::new()
} else {
bail!("unable to read acl config {:?} - {}", filename, err);
}
}
};
let digest = ConfigDigest::from_slice(raw.as_bytes());
for (linenr, line) in raw.lines().enumerate() {
let line = line.trim();
if line.is_empty() {
continue;
}
if let Err(err) = tree.parse_acl_line(line) {
bail!(
"unable to parse acl config {:?}, line {} - {}",
filename,
linenr + 1,
err
);
}
}
Ok((tree, digest))
}
/// This is used for testing
pub fn from_raw(raw: &str) -> Result<Self, Error> {
let mut tree = Self::new();
for (linenr, line) in raw.lines().enumerate() {
let line = line.trim();
if line.is_empty() {
continue;
}
if let Err(err) = tree.parse_acl_line(line) {
bail!(
"unable to parse acl config data, line {} - {}",
linenr + 1,
err
);
}
}
Ok(tree)
}
/// Returns a map of role name and propagation status for a given `auth_id` and `path`.
///
/// This will collect role mappings according to the following algorithm:
/// - iterate over all intermediate nodes along `path` and collect roles with `propagate` set
/// - get all (propagating and non-propagating) roles for last component of path
/// - more specific role maps replace less specific role maps
/// -- user/token is more specific than group at each level
/// -- roles lower in the tree are more specific than those higher up along the path
pub fn roles(&self, auth_id: &Authid, path: &[&str]) -> HashMap<String, bool> {
let mut node = &self.root;
let mut role_map = node.extract_roles(auth_id, path.is_empty());
let mut comp_iter = path.iter().peekable();
while let Some(comp) = comp_iter.next() {
let last_comp = comp_iter.peek().is_none();
let mut sub_comp_iter = comp.split('/').peekable();
while let Some(sub_comp) = sub_comp_iter.next() {
let last_sub_comp = last_comp && sub_comp_iter.peek().is_none();
node = match node.children.get(sub_comp) {
Some(n) => n,
None => return role_map, // path not found
};
let new_map = node.extract_roles(auth_id, last_sub_comp);
if !new_map.is_empty() {
// overwrite previous mappings
role_map = new_map;
}
}
}
role_map
}
pub fn get_child_paths(&self, auth_id: &Authid, path: &[&str]) -> Result<Vec<String>, Error> {
let mut res = Vec::new();
if let Some(node) = self.get_node(path) {
let path = path.join("/");
node.get_child_paths(path, auth_id, &mut res)?;
}
Ok(res)
}
}
/// Get exclusive lock
pub fn lock_config() -> Result<ApiLockGuard, Error> {
open_api_lockfile(acl_config_lock(), None, true)
}
/// Reads the [`AclTree`] from `acl.cfg` in the configuration directory.
pub fn config() -> Result<(AclTree, ConfigDigest), Error> {
let path = acl_config();
AclTree::load(&path)
}
/// Returns a cached [`AclTree`] or a fresh copy read directly from `acl.cfg` in the configuration
/// directory.
///
/// Since the AclTree is used for every API request's permission check, this caching mechanism
/// allows to skip reading and parsing the file again if it is unchanged.
pub fn cached_config() -> Result<Arc<AclTree>, Error> {
struct ConfigCache {
data: Option<Arc<AclTree>>,
last_mtime: i64,
last_mtime_nsec: i64,
}
static CACHED_CONFIG: OnceLock<RwLock<ConfigCache>> = OnceLock::new();
let cached_conf = CACHED_CONFIG.get_or_init(|| {
RwLock::new(ConfigCache {
data: None,
last_mtime: 0,
last_mtime_nsec: 0,
})
});
let conf = acl_config();
let stat = match nix::sys::stat::stat(&conf) {
Ok(stat) => Some(stat),
Err(nix::errno::Errno::ENOENT) => None,
Err(err) => bail!("unable to stat '{}' - {err}", conf.display()),
};
{
// limit scope
let cache = cached_conf.read().unwrap();
if let Some(ref config) = cache.data {
if let Some(stat) = stat {
if stat.st_mtime == cache.last_mtime && stat.st_mtime_nsec == cache.last_mtime_nsec
{
return Ok(config.clone());
}
} else if cache.last_mtime == 0 && cache.last_mtime_nsec == 0 {
return Ok(config.clone());
}
}
}
let (config, _digest) = config()?;
let config = Arc::new(config);
let mut cache = cached_conf.write().unwrap();
if let Some(stat) = stat {
cache.last_mtime = stat.st_mtime;
cache.last_mtime_nsec = stat.st_mtime_nsec;
}
cache.data = Some(config.clone());
Ok(config)
}
/// Saves an [`AclTree`] to `acl.cfg` in the configuration directory, ensuring proper ownership and
/// file permissions.
pub fn save_config(acl: &AclTree) -> Result<(), Error> {
let mut raw: Vec<u8> = Vec::new();
acl.write_config(&mut raw)?;
let conf = acl_config();
replace_privileged_config(conf, &raw)?;
// increase cache generation so we reload it next time we access it
access_conf().increment_cache_generation()?;
Ok(())
}
#[cfg(test)]
mod test {
use std::{collections::HashMap, sync::OnceLock};
use crate::init::{init_access_config, AccessControlConfig};
use super::AclTree;
use anyhow::Error;
use proxmox_auth_api::types::Authid;
#[derive(Debug)]
struct TestAcmConfig<'a> {
roles: HashMap<&'a str, u64>,
}
impl AccessControlConfig for TestAcmConfig<'_> {
fn roles(&self) -> &HashMap<&str, u64> {
&self.roles
}
fn privileges(&self) -> &HashMap<&str, u64> {
unreachable!("acl tests don't need privileges")
}
fn role_no_access(&self) -> Option<&'static str> {
Some("NoAccess")
}
fn role_admin(&self) -> Option<&'static str> {
Some("Admin")
}
}
fn setup_acl_tree_config() {
static ACL_CONFIG: OnceLock<TestAcmConfig> = OnceLock::new();
let config = ACL_CONFIG.get_or_init(|| {
let mut roles = HashMap::new();
roles.insert("NoAccess", 0);
roles.insert("Admin", u64::MAX);
roles.insert("DatastoreBackup", 4);
roles.insert("DatastoreReader", 8);
TestAcmConfig { roles }
});
// ignore errors here, we don't care if it's initialized already
let _ = init_access_config(config);
}
fn check_roles(tree: &AclTree, auth_id: &Authid, path: &str, expected_roles: &str) {
let path_vec = super::split_acl_path(path);
let mut roles = tree
.roles(auth_id, &path_vec)
.keys()
.cloned()
.collect::<Vec<String>>();
roles.sort();
let roles = roles.join(",");
assert_eq!(
roles, expected_roles,
"\nat check_roles for '{}' on '{}'",
auth_id, path
);
}
#[test]
fn test_acl_line_compression() {
setup_acl_tree_config();
let tree = AclTree::from_raw(
"\
acl:0:/store/store2:user1@pbs:Admin\n\
acl:0:/store/store2:user2@pbs:Admin\n\
acl:0:/store/store2:user1@pbs:DatastoreBackup\n\
acl:0:/store/store2:user2@pbs:DatastoreBackup\n\
",
)
.expect("failed to parse acl tree");
let mut raw: Vec<u8> = Vec::new();
tree.write_config(&mut raw)
.expect("failed to write acl tree");
let raw = std::str::from_utf8(&raw).expect("acl tree is not valid utf8");
assert_eq!(
raw,
"acl:0:/store/store2:user1@pbs,user2@pbs:Admin,DatastoreBackup\n"
);
}
#[test]
fn test_roles_1() -> Result<(), Error> {
setup_acl_tree_config();
let tree = AclTree::from_raw(
"\
acl:1:/storage:user1@pbs:Admin\n\
acl:1:/storage/store1:user1@pbs:DatastoreBackup\n\
acl:1:/storage/store2:user2@pbs:DatastoreBackup\n\
",
)?;
let user1: Authid = "user1@pbs".parse()?;
check_roles(&tree, &user1, "/", "");
check_roles(&tree, &user1, "/storage", "Admin");
check_roles(&tree, &user1, "/storage/store1", "DatastoreBackup");
check_roles(&tree, &user1, "/storage/store2", "Admin");
let user2: Authid = "user2@pbs".parse()?;
check_roles(&tree, &user2, "/", "");
check_roles(&tree, &user2, "/storage", "");
check_roles(&tree, &user2, "/storage/store1", "");
check_roles(&tree, &user2, "/storage/store2", "DatastoreBackup");
Ok(())
}
#[test]
fn test_role_no_access() -> Result<(), Error> {
setup_acl_tree_config();
let tree = AclTree::from_raw(
"\
acl:1:/:user1@pbs:Admin\n\
acl:1:/storage:user1@pbs:NoAccess\n\
acl:1:/storage/store1:user1@pbs:DatastoreBackup\n\
",
)?;
let user1: Authid = "user1@pbs".parse()?;
check_roles(&tree, &user1, "/", "Admin");
check_roles(&tree, &user1, "/storage", "NoAccess");
check_roles(&tree, &user1, "/storage/store1", "DatastoreBackup");
check_roles(&tree, &user1, "/storage/store2", "NoAccess");
check_roles(&tree, &user1, "/system", "Admin");
let tree = AclTree::from_raw(
"\
acl:1:/:user1@pbs:Admin\n\
acl:0:/storage:user1@pbs:NoAccess\n\
acl:1:/storage/store1:user1@pbs:DatastoreBackup\n\
",
)?;
check_roles(&tree, &user1, "/", "Admin");
check_roles(&tree, &user1, "/storage", "NoAccess");
check_roles(&tree, &user1, "/storage/store1", "DatastoreBackup");
check_roles(&tree, &user1, "/storage/store2", "Admin");
check_roles(&tree, &user1, "/system", "Admin");
Ok(())
}
#[test]
fn test_role_add_delete() -> Result<(), Error> {
setup_acl_tree_config();
let mut tree = AclTree::new();
let user1: Authid = "user1@pbs".parse()?;
tree.insert_user_role("/", &user1, "Admin", true);
tree.insert_user_role("/", &user1, "Audit", true);
check_roles(&tree, &user1, "/", "Admin,Audit");
tree.insert_user_role("/", &user1, "NoAccess", true);
check_roles(&tree, &user1, "/", "NoAccess");
let mut raw: Vec<u8> = Vec::new();
tree.write_config(&mut raw)?;
let raw = std::str::from_utf8(&raw)?;
assert_eq!(raw, "acl:1:/:user1@pbs:NoAccess\n");
Ok(())
}
#[test]
fn test_no_access_overwrite() -> Result<(), Error> {
setup_acl_tree_config();
let mut tree = AclTree::new();
let user1: Authid = "user1@pbs".parse()?;
tree.insert_user_role("/storage", &user1, "NoAccess", true);
check_roles(&tree, &user1, "/storage", "NoAccess");
tree.insert_user_role("/storage", &user1, "Admin", true);
tree.insert_user_role("/storage", &user1, "Audit", true);
check_roles(&tree, &user1, "/storage", "Admin,Audit");
tree.insert_user_role("/storage", &user1, "NoAccess", true);
check_roles(&tree, &user1, "/storage", "NoAccess");
Ok(())
}
#[test]
fn test_get_child_paths() -> Result<(), Error> {
setup_acl_tree_config();
let tree = AclTree::from_raw(
"\
acl:0:/store/store2:user1@pbs:Admin\n\
acl:1:/store/store2/store31/store4/store6:user2@pbs:DatastoreReader\n\
acl:0:/store/store2/store3:user1@pbs:Admin\n\
",
)
.expect("failed to parse acl tree");
let user1: Authid = "user1@pbs".parse()?;
let user2: Authid = "user2@pbs".parse()?;
// user1 has admin on "/store/store2/store3" -> return paths
let paths = tree.get_child_paths(&user1, &["store"])?;
assert!(
paths.len() == 2
&& paths.contains(&"store/store2".to_string())
&& paths.contains(&"store/store2/store3".to_string())
);
// user2 has no privileges under "/store/store2/store3" --> return empty
assert!(tree
.get_child_paths(&user2, &["store", "store2", "store3"],)?
.is_empty());
// user2 has DatastoreReader privileges under "/store/store2/store31" --> return paths
let paths = tree.get_child_paths(&user2, &["store/store2/store31"])?;
assert!(
paths.len() == 1 && paths.contains(&"store/store2/store31/store4/store6".to_string())
);
// user2 has no privileges under "/store/store2/foo/bar/baz"
assert!(tree
.get_child_paths(&user2, &["store", "store2", "foo/bar/baz"])?
.is_empty());
// user2 has DatastoreReader privileges on "/store/store2/store31/store4/store6", but not
// on any child paths --> return empty
assert!(tree
.get_child_paths(&user2, &["store/store2/store31/store4/store6"],)?
.is_empty());
Ok(())
}
#[test]
fn test_delete_node() -> Result<(), Error> {
setup_acl_tree_config();
let mut tree = AclTree::new();
let user1: Authid = "user1@pbs".parse()?;
tree.insert_user_role("/storage", &user1, "NoAccess", true);
tree.insert_user_role("/storage/a", &user1, "NoAccess", true);
tree.insert_user_role("/storage/b", &user1, "NoAccess", true);
tree.insert_user_role("/storage/b/a", &user1, "NoAccess", true);
tree.insert_user_role("/storage/b/b", &user1, "NoAccess", true);
tree.insert_user_role("/datastore/c", &user1, "NoAccess", true);
tree.insert_user_role("/datastore/d", &user1, "NoAccess", true);
assert!(tree.find_node("/storage/b/a").is_some());
tree.delete_node("/storage/b/a");
assert!(tree.find_node("/storage/b/a").is_none());
assert!(tree.find_node("/storage/b/b").is_some());
assert!(tree.find_node("/storage/b").is_some());
tree.delete_node("/storage/b");
assert!(tree.find_node("/storage/b/b").is_none());
assert!(tree.find_node("/storage/b").is_none());
assert!(tree.find_node("/storage").is_some());
assert!(tree.find_node("/storage/a").is_some());
tree.delete_node("/storage");
assert!(tree.find_node("/storage").is_none());
assert!(tree.find_node("/storage/a").is_none());
assert!(tree.find_node("/datastore/c").is_some());
tree.delete_node("/datastore/c");
assert!(tree.find_node("/datastore/c").is_none());
assert!(tree.find_node("/datastore/d").is_some());
tree.delete_node("/datastore/d");
assert!(tree.find_node("/datastore/d").is_none());
// '/' should not be deletable
assert!(tree.find_node("/").is_some());
tree.delete_node("/");
assert!(tree.find_node("/").is_some());
Ok(())
}
#[test]
fn test_delete_authid() -> Result<(), Error> {
setup_acl_tree_config();
let mut tree = AclTree::new();
let user1: Authid = "user1@pbs".parse()?;
let user2: Authid = "user2@pbs".parse()?;
let user1_paths = vec![
"/",
"/storage",
"/storage/a",
"/storage/a/b",
"/storage/b",
"/storage/b/a",
"/storage/b/b",
"/storage/a/a",
];
let user2_paths = vec!["/", "/storage", "/storage/a/b", "/storage/a/a"];
for path in &user1_paths {
tree.insert_user_role(path, &user1, "NoAccess", true);
}
for path in &user2_paths {
tree.insert_user_role(path, &user2, "NoAccess", true);
}
tree.delete_authid(&user1);
for path in &user1_paths {
let node = tree.find_node(path);
assert!(node.is_some());
if let Some(node) = node {
assert!(!node.users.contains_key(&user1));
}
}
for path in &user2_paths {
let node = tree.find_node(path);
assert!(node.is_some());
if let Some(node) = node {
assert!(node.users.contains_key(&user2));
}
}
tree.delete_authid(&user2);
for path in &user2_paths {
let node = tree.find_node(path);
assert!(node.is_some());
if let Some(node) = node {
assert!(!node.users.contains_key(&user2));
}
}
Ok(())
}
}

View File

@ -0,0 +1,246 @@
//! Cached user info for fast ACL permission checks
use std::sync::{Arc, OnceLock, RwLock};
use anyhow::{bail, Error};
use proxmox_auth_api::types::{Authid, Userid};
use proxmox_router::UserInformation;
use proxmox_section_config::SectionConfigData;
use proxmox_time::epoch_i64;
use crate::acl::AclTree;
use crate::init::access_conf;
use crate::types::{ApiToken, User};
/// Cache User/Group/Token/Acl configuration data for fast permission tests
pub struct CachedUserInfo {
user_cfg: Arc<SectionConfigData>,
acl_tree: Arc<AclTree>,
}
struct ConfigCache {
data: Option<Arc<CachedUserInfo>>,
last_update: i64,
last_user_cache_generation: usize,
}
impl CachedUserInfo {
/// Returns a cached instance (up to 5 seconds old).
pub fn new() -> Result<Arc<Self>, Error> {
let now = epoch_i64();
let cache_generation = access_conf().cache_generation();
static CACHED_CONFIG: OnceLock<RwLock<ConfigCache>> = OnceLock::new();
let cached_config = CACHED_CONFIG.get_or_init(|| {
RwLock::new(ConfigCache {
data: None,
last_update: 0,
last_user_cache_generation: 0,
})
});
{
// limit scope
let cache = cached_config.read().unwrap();
if let Some(current_generation) = cache_generation {
if (current_generation == cache.last_user_cache_generation)
&& ((now - cache.last_update) < 5)
{
if let Some(ref config) = cache.data {
return Ok(config.clone());
}
}
}
}
let config = Arc::new(CachedUserInfo {
user_cfg: crate::user::cached_config()?,
acl_tree: crate::acl::cached_config()?,
});
let mut cache = cached_config.write().unwrap();
if let Some(current_generation) = cache_generation {
cache.last_user_cache_generation = current_generation;
}
cache.last_update = now;
cache.data = Some(config.clone());
Ok(config)
}
pub fn is_superuser(&self, auth_id: &Authid) -> bool {
access_conf().is_superuser(auth_id)
}
pub fn is_group_member(&self, user_id: &Userid, group: &str) -> bool {
access_conf().is_group_member(user_id, group)
}
/// Test if a user_id is enabled and not expired
pub fn is_active_user_id(&self, userid: &Userid) -> bool {
if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
info.is_active()
} else {
false
}
}
/// Test if a authentication id is enabled and not expired
pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool {
let userid = auth_id.user();
if !self.is_active_user_id(userid) {
return false;
}
if auth_id.is_token() {
if let Ok(info) = self
.user_cfg
.lookup::<ApiToken>("token", &auth_id.to_string())
{
return info.is_active();
} else {
return false;
}
}
true
}
pub fn check_privs(
&self,
auth_id: &Authid,
path: &[&str],
required_privs: u64,
partial: bool,
) -> Result<(), Error> {
let privs = self.lookup_privs(auth_id, path);
let allowed = if partial {
(privs & required_privs) != 0
} else {
(privs & required_privs) == required_privs
};
if !allowed {
// printing the path doesn't leak any information as long as we
// always check privilege before resource existence
let priv_names = privs_to_priv_names(required_privs);
let priv_names = if partial {
priv_names.join("|")
} else {
priv_names.join("&")
};
bail!(
"missing permissions '{priv_names}' on '/{}'",
path.join("/")
);
}
Ok(())
}
pub fn lookup_privs(&self, auth_id: &Authid, path: &[&str]) -> u64 {
let (privs, _) = self.lookup_privs_details(auth_id, path);
privs
}
pub fn lookup_privs_details(&self, auth_id: &Authid, path: &[&str]) -> (u64, u64) {
if self.is_superuser(auth_id) {
let acm_config = access_conf();
if let Some(admin) = acm_config.role_admin() {
if let Some(admin) = acm_config.roles().get(admin) {
return (*admin, *admin);
}
}
}
let roles = self.acl_tree.roles(auth_id, path);
let mut privs: u64 = 0;
let mut propagated_privs: u64 = 0;
for (role, propagate) in roles {
if let Some(role_privs) = access_conf().roles().get(role.as_str()) {
if propagate {
propagated_privs |= role_privs;
}
privs |= role_privs;
}
}
if auth_id.is_token() {
// limit privs to that of owning user
let user_auth_id = Authid::from(auth_id.user().clone());
let (owner_privs, owner_propagated_privs) =
self.lookup_privs_details(&user_auth_id, path);
privs &= owner_privs;
propagated_privs &= owner_propagated_privs;
}
(privs, propagated_privs)
}
/// Checks whether the `auth_id` has any of the privileges `privs` on any object below `path`.
pub fn any_privs_below(
&self,
auth_id: &Authid,
path: &[&str],
privs: u64,
) -> Result<bool, Error> {
// if the anchor path itself has matching propagated privs, we skip checking children
let (_privs, propagated_privs) = self.lookup_privs_details(auth_id, path);
if propagated_privs & privs != 0 {
return Ok(true);
}
// get all sub-paths with roles defined for `auth_id`
let paths = self.acl_tree.get_child_paths(auth_id, path)?;
for path in paths.iter() {
// early return if any sub-path has any of the privs we are looking for
if privs & self.lookup_privs(auth_id, &[path.as_str()]) != 0 {
return Ok(true);
}
}
// no paths or no matching paths
Ok(false)
}
}
impl UserInformation for CachedUserInfo {
fn is_superuser(&self, userid: &str) -> bool {
if let Ok(authid) = userid.parse() {
return self.is_superuser(&authid);
}
false
}
fn is_group_member(&self, userid: &str, group: &str) -> bool {
if let Ok(userid) = userid.parse() {
return self.is_group_member(&userid, group);
}
false
}
fn lookup_privs(&self, auth_id: &str, path: &[&str]) -> u64 {
match auth_id.parse::<Authid>() {
Ok(auth_id) => Self::lookup_privs(self, &auth_id, path),
Err(_) => 0,
}
}
}
pub fn privs_to_priv_names(privs: u64) -> Vec<&'static str> {
access_conf()
.privileges()
.iter()
.fold(Vec::new(), |mut priv_names, (name, value)| {
if value & privs != 0 {
priv_names.push(name);
}
priv_names
})
}

View File

@ -0,0 +1,131 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use anyhow::{format_err, Error};
use proxmox_auth_api::types::{Authid, Userid};
use proxmox_section_config::SectionConfigData;
static ACCESS_CONF: OnceLock<&'static dyn AccessControlConfig> = OnceLock::new();
static ACCESS_CONF_DIR: OnceLock<PathBuf> = OnceLock::new();
/// This trait specifies the functions a product needs to implement to get ACL tree based access
/// control management from this plugin.
pub trait AccessControlConfig: Send + Sync {
/// Returns a mapping of all recognized privileges and their corresponding `u64` value.
fn privileges(&self) -> &HashMap<&str, u64>;
/// Returns a mapping of all recognized roles and their corresponding `u64` value.
fn roles(&self) -> &HashMap<&str, u64>;
/// Checks whether an `Authid` has super user privileges or not.
///
/// Default: Always returns `false`.
fn is_superuser(&self, _auth_id: &Authid) -> bool {
false
}
/// Checks whether a user is part of a group.
///
/// Default: Always returns `false`.
fn is_group_member(&self, _user_id: &Userid, _group: &str) -> bool {
false
}
/// Returns the current cache generation of the user and acl configs. If the generation was
/// incremented since the last time the cache was queried, the configs are loaded again from
/// disk.
///
/// Returning `None` will always reload the cache.
///
/// Default: Always returns `None`.
fn cache_generation(&self) -> Option<usize> {
None
}
/// Increment the cache generation of user and acl configs. This indicates that they were
/// changed on disk.
///
/// Default: Does nothing.
fn increment_cache_generation(&self) -> Result<(), Error> {
Ok(())
}
/// Optionally returns a role that has no access to any resource.
///
/// Default: Returns `None`.
fn role_no_access(&self) -> Option<&str> {
None
}
/// Optionally returns a role that is allowed to access all resources.
///
/// Default: Returns `None`.
fn role_admin(&self) -> Option<&str> {
None
}
/// Called after the user configuration is loaded to potentially re-add fixed users, such as a
/// `root@pam` user.
fn init_user_config(&self, config: &mut SectionConfigData) -> Result<(), Error> {
let _ = config;
Ok(())
}
}
pub fn init<P: AsRef<Path>>(
acm_config: &'static dyn AccessControlConfig,
config_dir: P,
) -> Result<(), Error> {
init_access_config(acm_config)?;
init_access_config_dir(config_dir)
}
pub(crate) fn init_access_config_dir<P: AsRef<Path>>(config_dir: P) -> Result<(), Error> {
ACCESS_CONF_DIR
.set(config_dir.as_ref().to_owned())
.map_err(|_e| format_err!("cannot initialize acl tree config twice!"))
}
pub(crate) fn init_access_config(config: &'static dyn AccessControlConfig) -> Result<(), Error> {
ACCESS_CONF
.set(config)
.map_err(|_e| format_err!("cannot initialize acl tree config twice!"))
}
pub(crate) fn access_conf() -> &'static dyn AccessControlConfig {
*ACCESS_CONF
.get()
.expect("please initialize the acm config before using it!")
}
fn conf_dir() -> &'static PathBuf {
ACCESS_CONF_DIR
.get()
.expect("please initialize acm config dir before using it!")
}
pub(crate) fn acl_config() -> PathBuf {
conf_dir().join("acl.cfg")
}
pub(crate) fn acl_config_lock() -> PathBuf {
conf_dir().join(".acl.lck")
}
pub(crate) fn user_config() -> PathBuf {
conf_dir().join("user.cfg")
}
pub(crate) fn user_config_lock() -> PathBuf {
conf_dir().join(".user.lck")
}
pub(crate) fn token_shadow() -> PathBuf {
conf_dir().join("token.shadow")
}
pub(crate) fn token_shadow_lock() -> PathBuf {
conf_dir().join("token.shadow.lock")
}

View File

@ -0,0 +1,20 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
pub mod types;
#[cfg(feature = "impl")]
pub mod acl;
#[cfg(feature = "impl")]
pub mod init;
#[cfg(feature = "impl")]
pub mod token_shadow;
#[cfg(feature = "impl")]
pub mod user;
#[cfg(feature = "impl")]
mod cached_user_info;
#[cfg(feature = "impl")]
pub use cached_user_info::CachedUserInfo;

View File

@ -0,0 +1,84 @@
use std::collections::HashMap;
use anyhow::{bail, format_err, Error};
use serde::{Deserialize, Serialize};
use serde_json::{from_value, Value};
use proxmox_auth_api::types::Authid;
use proxmox_product_config::{open_api_lockfile, replace_config, ApiLockGuard};
use crate::init::{token_shadow, token_shadow_lock};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// ApiToken id / secret pair
pub struct ApiTokenSecret {
pub tokenid: Authid,
pub secret: String,
}
// Get exclusive lock
fn lock_config() -> Result<ApiLockGuard, Error> {
open_api_lockfile(token_shadow_lock(), None, true)
}
fn read_file() -> Result<HashMap<Authid, String>, Error> {
let json = proxmox_sys::fs::file_get_json(token_shadow(), Some(Value::Null))?;
if json == Value::Null {
Ok(HashMap::new())
} else {
// swallow serde error which might contain sensitive data
from_value(json)
.map_err(|_err| format_err!("unable to parse '{}'", token_shadow().display()))
}
}
fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
let json = serde_json::to_vec(&data)?;
replace_config(token_shadow(), &json)
}
/// Verifies that an entry for given tokenid / API token secret exists
pub fn verify_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> {
if !tokenid.is_token() {
bail!("not an API token ID");
}
let data = read_file()?;
match data.get(tokenid) {
Some(hashed_secret) => proxmox_sys::crypt::verify_crypt_pw(secret, hashed_secret),
None => bail!("invalid API token"),
}
}
/// Adds a new entry for the given tokenid / API token secret. The secret is stored as salted hash.
pub fn set_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> {
if !tokenid.is_token() {
bail!("not an API token ID");
}
let _guard = lock_config()?;
let mut data = read_file()?;
let hashed_secret = proxmox_sys::crypt::encrypt_pw(secret)?;
data.insert(tokenid.clone(), hashed_secret);
write_file(data)?;
Ok(())
}
/// Deletes the entry for the given tokenid.
pub fn delete_secret(tokenid: &Authid) -> Result<(), Error> {
if !tokenid.is_token() {
bail!("not an API token ID");
}
let _guard = lock_config()?;
let mut data = read_file()?;
data.remove(tokenid);
write_file(data)?;
Ok(())
}

View File

@ -0,0 +1,194 @@
use serde::{Deserialize, Serialize};
use proxmox_auth_api::types::{Authid, Userid, PROXMOX_TOKEN_ID_SCHEMA};
use proxmox_schema::{
api,
api_types::{COMMENT_SCHEMA, SINGLE_LINE_COMMENT_FORMAT},
BooleanSchema, IntegerSchema, Schema, StringSchema, Updater,
};
pub const ENABLE_USER_SCHEMA: Schema = BooleanSchema::new(
"Enable the account (default). You can set this to '0' to disable the account.",
)
.default(true)
.schema();
pub const EXPIRE_USER_SCHEMA: Schema = IntegerSchema::new(
"Account expiration date (seconds since epoch). '0' means no expiration date.",
)
.default(0)
.minimum(0)
.schema();
pub const FIRST_NAME_SCHEMA: Schema = StringSchema::new("First name.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
pub const LAST_NAME_SCHEMA: Schema = StringSchema::new("Last name.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
pub const EMAIL_SCHEMA: Schema = StringSchema::new("E-Mail Address.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
#[api(
properties: {
user: {
type: User,
flatten: true,
},
tokens: {
type: Array,
optional: true,
description: "List of user's API tokens.",
items: {
type: ApiToken
},
},
"totp-locked": {
type: bool,
optional: true,
default: false,
description: "True if the user is currently locked out of TOTP factors",
},
"tfa-locked-until": {
optional: true,
description: "Contains a timestamp until when a user is locked out of 2nd factors",
},
}
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
/// User properties with added list of ApiTokens
pub struct UserWithTokens {
#[serde(flatten)]
pub user: User,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub tokens: Vec<ApiToken>,
#[serde(skip_serializing_if = "bool_is_false", default)]
pub totp_locked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub tfa_locked_until: Option<i64>,
}
fn bool_is_false(b: &bool) -> bool {
!b
}
#[api(
properties: {
tokenid: {
schema: PROXMOX_TOKEN_ID_SCHEMA,
},
comment: {
optional: true,
schema: COMMENT_SCHEMA,
},
enable: {
optional: true,
schema: ENABLE_USER_SCHEMA,
},
expire: {
optional: true,
schema: EXPIRE_USER_SCHEMA,
},
}
)]
#[derive(Serialize, Deserialize, Clone, PartialEq)]
/// ApiToken properties.
pub struct ApiToken {
pub tokenid: Authid,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>,
}
impl ApiToken {
pub fn is_active(&self) -> bool {
if !self.enable.unwrap_or(true) {
return false;
}
if let Some(expire) = self.expire {
let now = proxmox_time::epoch_i64();
if expire > 0 && expire <= now {
return false;
}
}
true
}
}
#[api(
properties: {
userid: {
type: Userid,
},
comment: {
optional: true,
schema: COMMENT_SCHEMA,
},
enable: {
optional: true,
schema: ENABLE_USER_SCHEMA,
},
expire: {
optional: true,
schema: EXPIRE_USER_SCHEMA,
},
firstname: {
optional: true,
schema: FIRST_NAME_SCHEMA,
},
lastname: {
schema: LAST_NAME_SCHEMA,
optional: true,
},
email: {
schema: EMAIL_SCHEMA,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Updater, PartialEq, Eq, Clone)]
/// User properties.
pub struct User {
#[updater(skip)]
pub userid: Userid,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
impl User {
pub fn is_active(&self) -> bool {
if !self.enable.unwrap_or(true) {
return false;
}
if let Some(expire) = self.expire {
let now = proxmox_time::epoch_i64();
if expire > 0 && expire <= now {
return false;
}
}
true
}
}

View File

@ -0,0 +1,183 @@
use std::collections::HashMap;
use std::sync::{Arc, OnceLock, RwLock};
use anyhow::{bail, Error};
use proxmox_auth_api::types::Authid;
use proxmox_config_digest::ConfigDigest;
use proxmox_product_config::{open_api_lockfile, replace_privileged_config, ApiLockGuard};
use proxmox_schema::*;
use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
use crate::init::{access_conf, user_config, user_config_lock};
use crate::types::{ApiToken, User};
fn get_or_init_config() -> &'static SectionConfig {
static CONFIG: OnceLock<SectionConfig> = OnceLock::new();
CONFIG.get_or_init(|| {
let mut config = SectionConfig::new(&Authid::API_SCHEMA);
let user_schema = match User::API_SCHEMA {
Schema::Object(ref user_schema) => user_schema,
_ => unreachable!(),
};
let user_plugin =
SectionConfigPlugin::new("user".to_string(), Some("userid".to_string()), user_schema);
config.register_plugin(user_plugin);
let token_schema = match ApiToken::API_SCHEMA {
Schema::Object(ref token_schema) => token_schema,
_ => unreachable!(),
};
let token_plugin = SectionConfigPlugin::new(
"token".to_string(),
Some("tokenid".to_string()),
token_schema,
);
config.register_plugin(token_plugin);
config
})
}
/// Get exclusive lock
pub fn lock_config() -> Result<ApiLockGuard, Error> {
open_api_lockfile(user_config_lock(), None, true)
}
pub fn config() -> Result<(SectionConfigData, ConfigDigest), Error> {
let content = proxmox_sys::fs::file_read_optional_string(user_config())?.unwrap_or_default();
let digest = ConfigDigest::from_slice(content.as_bytes());
let mut data = get_or_init_config().parse(user_config(), &content)?;
access_conf().init_user_config(&mut data)?;
Ok((data, digest))
}
pub fn cached_config() -> Result<Arc<SectionConfigData>, Error> {
struct ConfigCache {
data: Option<Arc<SectionConfigData>>,
last_mtime: i64,
last_mtime_nsec: i64,
}
static CACHED_CONFIG: OnceLock<RwLock<ConfigCache>> = OnceLock::new();
let cached_config = CACHED_CONFIG.get_or_init(|| {
RwLock::new(ConfigCache {
data: None,
last_mtime: 0,
last_mtime_nsec: 0,
})
});
let stat = match nix::sys::stat::stat(&user_config()) {
Ok(stat) => Some(stat),
Err(nix::errno::Errno::ENOENT) => None,
Err(err) => bail!("unable to stat '{}' - {err}", user_config().display()),
};
{
// limit scope
let cache = cached_config.read().unwrap();
if let Some(ref config) = cache.data {
if let Some(stat) = stat {
if stat.st_mtime == cache.last_mtime && stat.st_mtime_nsec == cache.last_mtime_nsec
{
return Ok(config.clone());
}
} else if cache.last_mtime == 0 && cache.last_mtime_nsec == 0 {
return Ok(config.clone());
}
}
}
let (config, _digest) = config()?;
let config = Arc::new(config);
let mut cache = cached_config.write().unwrap();
if let Some(stat) = stat {
cache.last_mtime = stat.st_mtime;
cache.last_mtime_nsec = stat.st_mtime_nsec;
}
cache.data = Some(config.clone());
Ok(config)
}
pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
let config_file = user_config();
let raw = get_or_init_config().write(&config_file, config)?;
replace_privileged_config(config_file, raw.as_bytes())?;
// increase cache generation so we reload it next time we access it
access_conf().increment_cache_generation()?;
Ok(())
}
/// Only exposed for testing
#[doc(hidden)]
pub fn test_cfg_from_str(raw: &str) -> Result<(SectionConfigData, [u8; 32]), Error> {
let cfg = get_or_init_config();
let parsed = cfg.parse("test_user_cfg", raw)?;
Ok((parsed, [0; 32]))
}
// shell completion helper
pub fn complete_userid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data
.sections
.iter()
.filter_map(|(id, (section_type, _))| {
if section_type == "user" {
Some(id.to_string())
} else {
None
}
})
.collect(),
Err(_) => Vec::new(),
}
}
// shell completion helper
pub fn complete_authid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.sections.keys().map(|id| id.to_string()).collect(),
Err(_) => vec![],
}
}
// shell completion helper
pub fn complete_token_name(_arg: &str, param: &HashMap<String, String>) -> Vec<String> {
let data = match config() {
Ok((data, _digest)) => data,
Err(_) => return Vec::new(),
};
match param.get("userid") {
Some(userid) => {
let user = data.lookup::<User>("user", userid);
let tokens = data.convert_to_typed_array("token");
match (user, tokens) {
(Ok(_), Ok(tokens)) => tokens
.into_iter()
.filter_map(|token: ApiToken| {
let tokenid = token.tokenid;
if tokenid.is_token() && tokenid.user() == userid {
Some(tokenid.tokenname().unwrap().as_str().to_string())
} else {
None
}
})
.collect(),
_ => vec![],
}
}
None => vec![],
}
}

View File

@ -0,0 +1,72 @@
[package]
name = "proxmox-acme-api"
description = "ACME API implementation"
version = "1.0.0"
authors.workspace = true
edition.workspace = true
exclude.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
anyhow.workspace = true
futures = { workspace = true, optional = true }
hex = { workspace = true, optional = true }
http = { workspace = true, optional = true }
http-body-util = { workspace = true, optional = true }
hyper = { workspace = true, optional = true }
hyper-util = { workspace = true, optional = true, features = ["server", "http1"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tokio = { workspace = true, optional = true, features = ["fs"] }
foreign-types = { workspace = true, optional = true }
libc = { workspace = true, optional = true }
openssl = { workspace = true, optional = true }
proxmox-acme = { workspace = true, features = ["api-types"] }
proxmox-base64 = { workspace = true, optional = true }
proxmox-config-digest = { workspace = true, optional = true }
proxmox-log = { workspace = true, optional = true }
proxmox-product-config = { workspace = true, optional = true }
proxmox-rest-server = { workspace = true, optional = true }
proxmox-router = { workspace = true, optional = true }
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] }
proxmox-section-config = { workspace = true, optional = true }
proxmox-serde.workspace = true
proxmox-sys = { workspace = true, optional = true }
proxmox-time = { workspace = true, optional = true }
proxmox-uuid = { workspace = true, optional = true }
[features]
default = []
impl = [
"dep:foreign-types",
"dep:futures",
"dep:hex",
"dep:http",
"dep:http-body-util",
"dep:hyper",
"dep:hyper-util",
"dep:libc",
"dep:openssl",
"dep:tokio",
"dep:proxmox-base64",
"dep:proxmox-config-digest",
"dep:proxmox-log",
"dep:proxmox-product-config",
"dep:proxmox-rest-server",
"dep:proxmox-router",
"dep:proxmox-section-config",
"dep:proxmox-sys",
"dep:proxmox-time",
"dep:proxmox-uuid",
"proxmox-acme/async-client",
"proxmox-acme/impl",
"proxmox-config-digest?/openssl",
]

View File

@ -0,0 +1,77 @@
rust-proxmox-acme-api (1.0.0-1) trixie; urgency=medium
* update to hyper 1.0
* switch to proxmox-base64
* re-build for Debian Trixie based releases.
-- Proxmox Support Team <support@proxmox.com> Thu, 22 May 2025 16:37:12 +0200
rust-proxmox-acme-api (0.1.8-1) bookworm; urgency=medium
* rebuild with section-config 3.0
* mark extern "C" blocks as unsafe to prepare for edition 2024
-- Proxmox Support Team <support@proxmox.com> Tue, 06 May 2025 11:48:08 +0200
rust-proxmox-acme-api (0.1.7-1) bookworm; urgency=medium
* rebuild with proxmox-schema 4.0
* use inner mutability for ACME_ACME_CONFIG
-- Proxmox Support Team <support@proxmox.com> Wed, 15 Jan 2025 12:45:05 +0100
rust-proxmox-acme-api (0.1.6-1) bookworm; urgency=medium
* rebuild with new rest-server and router dependencies
-- Proxmox Support Team <support@proxmox.com> Wed, 04 Sep 2024 15:42:27 +0200
rust-proxmox-acme-api (0.1.5-1) bookworm; urgency=medium
* replace lazy_static with std's LazyLock and drop the dependency
* remove unused dependencies
-- Proxmox Support Team <support@proxmox.com> Wed, 14 Aug 2024 11:36:01 +0200
rust-proxmox-acme-api (0.1.4-1) bookworm; urgency=medium
* rebuild with proxmox-log 0.2 and proxmox-rest-server 0.7
-- Proxmox Support Team <support@proxmox.com> Wed, 24 Jul 2024 14:33:06 +0200
rust-proxmox-acme-api (0.1.3-1) bookworm; urgency=medium
* adapt to tracing log infrastructure
* various clippy fixes
* upgrade proxmox-sys to 6.0
-- Proxmox Support Team <support@proxmox.com> Thu, 11 Jul 2024 14:50:02 +0200
rust-proxmox-acme-api (0.1.2-1) bookworm; urgency=medium
* upgrade proxmox-time to 2.0
-- Proxmox Support Team <support@proxmox.com> Thu, 20 Jun 2024 13:57:36 +0200
rust-proxmox-acme-api (0.1.1-1) bookworm; urgency=medium
* add function to create certificate revocation
* add function to create self signed certificates
* add function to get info from PEM formatted certificates
-- Proxmox Support Team <support@proxmox.com> Wed, 19 Jun 2024 12:06:49 +0200
rust-proxmox-acme-api (0.1.0-1) bookworm; urgency=medium
* initial package
-- Proxmox Support Team <support@proxmox.com> Wed, 05 Jun 2024 12:12:42 +0200

View File

@ -0,0 +1,94 @@
Source: rust-proxmox-acme-api
Section: rust
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-sequence-cargo
Build-Depends-Arch: cargo:native <!nocheck>,
rustc:native (>= 1.82) <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-anyhow-1+default-dev <!nocheck>,
librust-proxmox-acme-1+api-types-dev <!nocheck>,
librust-proxmox-schema-4+api-macro-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-schema-4+api-types-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-serde-1+default-dev <!nocheck>,
librust-proxmox-serde-1+serde-json-dev <!nocheck>,
librust-serde-1+default-dev <!nocheck>,
librust-serde-1+derive-dev <!nocheck>,
librust-serde-json-1+default-dev <!nocheck>
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.7.0
Vcs-Git:
Vcs-Browser:
Homepage: https://proxmox.com
X-Cargo-Crate: proxmox-acme-api
Rules-Requires-Root: no
Package: librust-proxmox-acme-api-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-anyhow-1+default-dev,
librust-proxmox-acme-1+api-types-dev,
librust-proxmox-schema-4+api-macro-dev (>= 4.1.0-~~),
librust-proxmox-schema-4+api-types-dev (>= 4.1.0-~~),
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~),
librust-proxmox-serde-1+default-dev,
librust-proxmox-serde-1+serde-json-dev,
librust-serde-1+default-dev,
librust-serde-1+derive-dev,
librust-serde-json-1+default-dev
Suggests:
librust-proxmox-acme-api+impl-dev (= ${binary:Version})
Provides:
librust-proxmox-acme-api+default-dev (= ${binary:Version}),
librust-proxmox-acme-api-1-dev (= ${binary:Version}),
librust-proxmox-acme-api-1+default-dev (= ${binary:Version}),
librust-proxmox-acme-api-1.0-dev (= ${binary:Version}),
librust-proxmox-acme-api-1.0+default-dev (= ${binary:Version}),
librust-proxmox-acme-api-1.0.0-dev (= ${binary:Version}),
librust-proxmox-acme-api-1.0.0+default-dev (= ${binary:Version})
Description: ACME API implementation - Rust source code
Source code for Debianized Rust crate "proxmox-acme-api"
Package: librust-proxmox-acme-api+impl-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-acme-api-dev (= ${binary:Version}),
librust-foreign-types-0.3+default-dev,
librust-futures-0.3+default-dev,
librust-hex-0.4+default-dev,
librust-http-1+default-dev,
librust-http-body-util-0.1+default-dev,
librust-hyper-1+default-dev,
librust-hyper-util-0.1+default-dev (>= 0.1.12-~~),
librust-hyper-util-0.1+http1-dev (>= 0.1.12-~~),
librust-hyper-util-0.1+server-dev (>= 0.1.12-~~),
librust-libc-0.2+default-dev (>= 0.2.107-~~),
librust-openssl-0.10+default-dev,
librust-proxmox-acme-1+api-types-dev,
librust-proxmox-acme-1+async-client-dev,
librust-proxmox-acme-1+impl-dev,
librust-proxmox-base64-1+default-dev,
librust-proxmox-config-digest-1+default-dev,
librust-proxmox-config-digest-1+openssl-dev,
librust-proxmox-log-1+default-dev,
librust-proxmox-product-config-1+default-dev,
librust-proxmox-rest-server-1+default-dev,
librust-proxmox-router-3+default-dev (>= 3.2.0-~~),
librust-proxmox-section-config-3+default-dev (>= 3.1.0-~~),
librust-proxmox-sys-1+default-dev,
librust-proxmox-time-2+default-dev (>= 2.1.0-~~),
librust-proxmox-uuid-1+default-dev (>= 1.1.0-~~),
librust-tokio-1+default-dev (>= 1.6-~~),
librust-tokio-1+fs-dev (>= 1.6-~~)
Provides:
librust-proxmox-acme-api-1+impl-dev (= ${binary:Version}),
librust-proxmox-acme-api-1.0+impl-dev (= ${binary:Version}),
librust-proxmox-acme-api-1.0.0+impl-dev (= ${binary:Version})
Description: ACME API implementation - feature "impl"
This metapackage enables feature "impl" for the Rust proxmox-acme-api crate, by
pulling in any additional dependencies needed by that feature.

View File

@ -1,4 +1,4 @@
Copyright (C) 2019,2020 Proxmox Server Solutions GmbH
Copyright (C) 2020-2024 Proxmox Server Solutions GmbH
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>

View File

@ -0,0 +1,8 @@
overlay = "."
crate_src_path = ".."
maintainer = "Proxmox Support Team <support@proxmox.com>"
[source]
# TODO: update once public
vcs_git = ""
vcs_browser = ""

View File

@ -0,0 +1,118 @@
//! ACME account configuration API implementation
use std::ops::ControlFlow;
use anyhow::Error;
use serde_json::json;
use proxmox_acme::async_client::AcmeClient;
use proxmox_acme::types::AccountData as AcmeAccountData;
use proxmox_log::warn;
use crate::account_config::AccountData;
use crate::config::DEFAULT_ACME_DIRECTORY_ENTRY;
use crate::types::{AccountEntry, AccountInfo, AcmeAccountName};
fn account_contact_from_string(s: &str) -> Vec<String> {
s.split(&[' ', ';', ',', '\0'][..])
.map(|s| format!("mailto:{}", s))
.collect()
}
pub fn list_accounts() -> Result<Vec<AccountEntry>, Error> {
let mut entries = Vec::new();
super::account_config::foreach_acme_account(|name| {
entries.push(AccountEntry { name });
ControlFlow::Continue(())
})?;
Ok(entries)
}
pub async fn get_account(account_name: AcmeAccountName) -> Result<AccountInfo, Error> {
let account_data = super::account_config::load_account_config(&account_name).await?;
Ok(AccountInfo {
location: account_data.location.clone(),
tos: account_data.tos.clone(),
directory: account_data.directory_url.clone(),
account: AcmeAccountData {
only_return_existing: false, // don't actually write this out in case it's set
..account_data.account.clone()
},
})
}
pub async fn get_tos(directory: Option<String>) -> Result<Option<String>, Error> {
let directory = directory.unwrap_or_else(|| DEFAULT_ACME_DIRECTORY_ENTRY.url.to_string());
Ok(AcmeClient::new(directory)
.terms_of_service_url()
.await?
.map(str::to_owned))
}
pub async fn register_account(
name: &AcmeAccountName,
contact: String,
tos_url: Option<String>,
directory_url: Option<String>,
eab_creds: Option<(String, String)>,
) -> Result<String, Error> {
let directory_url =
directory_url.unwrap_or_else(|| DEFAULT_ACME_DIRECTORY_ENTRY.url.to_string());
let mut client = AcmeClient::new(directory_url.clone());
let contact = account_contact_from_string(&contact);
let account = client
.new_account(tos_url.is_some(), contact, None, eab_creds)
.await?;
let account = AccountData::from_account_dir_tos(account, directory_url, tos_url);
super::account_config::create_account_config(name, &account)?;
Ok(account.location)
}
pub async fn deactivate_account(name: &AcmeAccountName, force: bool) -> Result<(), Error> {
let mut account_data = super::account_config::load_account_config(name).await?;
let mut client = account_data.client();
match client
.update_account(&json!({"status": "deactivated"}))
.await
{
Ok(account) => {
account_data.account = account.data.clone();
super::account_config::save_account_config(name, &account_data)?;
}
Err(err) if !force => return Err(err),
Err(err) => {
warn!(
"error deactivating account {}, proceedeing anyway - {}",
name, err,
);
}
}
super::account_config::mark_account_deactivated(name)?;
Ok(())
}
pub async fn update_account(name: &AcmeAccountName, contact: Option<String>) -> Result<(), Error> {
let mut account_data = super::account_config::load_account_config(name).await?;
let mut client = account_data.client();
let data = match contact {
Some(contact) => json!({
"contact": account_contact_from_string(&contact),
}),
None => json!({}),
};
let account = client.update_account(&data).await?;
account_data.account = account.data.clone();
super::account_config::save_account_config(name, &account_data)?;
Ok(())
}

View File

@ -0,0 +1,212 @@
//! ACME account configuration helpers (load/save config)
use std::fs::OpenOptions;
use std::ops::ControlFlow;
use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
use anyhow::{bail, format_err, Error};
use serde::{Deserialize, Serialize};
use proxmox_product_config::replace_secret_config;
use proxmox_sys::error::SysError;
use proxmox_schema::api_types::SAFE_ID_REGEX;
use proxmox_acme::async_client::AcmeClient;
use proxmox_acme::types::AccountData as AcmeAccountData;
use proxmox_acme::Account;
use crate::acme_account_dir;
use crate::types::AcmeAccountName;
#[inline]
fn is_false(b: &bool) -> bool {
!*b
}
// Our on-disk format inherited from PVE's proxmox-acme code.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountData {
/// The account's location URL.
pub location: String,
/// The account data.
pub account: AcmeAccountData,
/// The private key as PEM formatted string.
pub key: String,
/// ToS URL the user agreed to.
#[serde(skip_serializing_if = "Option::is_none")]
pub tos: Option<String>,
#[serde(skip_serializing_if = "is_false", default)]
pub debug: bool,
/// The directory's URL.
pub directory_url: String,
}
impl AccountData {
pub fn from_account_dir_tos(
account: &Account,
directory_url: String,
tos: Option<String>,
) -> Self {
AccountData {
location: account.location.clone(),
key: account.private_key.clone(),
account: AcmeAccountData {
only_return_existing: false, // don't actually write this out in case it's set
..account.data.clone()
},
debug: false,
tos,
directory_url,
}
}
pub fn client(&self) -> AcmeClient {
let mut client = AcmeClient::new(self.directory_url.clone());
client.set_account(Account {
location: self.location.clone(),
private_key: self.key.clone(),
data: self.account.clone(),
});
client
}
}
/// Returns the path to the account configuration file (`$config_dir/accounts/$name`).
pub fn account_config_filename(name: &str) -> PathBuf {
acme_account_dir().join(name)
}
pub(crate) fn foreach_acme_account<F>(mut func: F) -> Result<(), Error>
where
F: FnMut(AcmeAccountName) -> ControlFlow<Result<(), Error>>,
{
match proxmox_sys::fs::scan_subdir(-1, acme_account_dir(), &SAFE_ID_REGEX) {
Ok(files) => {
for file in files {
let file = file?;
let file_name = unsafe { file.file_name_utf8_unchecked() };
if file_name.starts_with('_') {
continue;
}
let account_name = match AcmeAccountName::from_string(file_name.to_owned()) {
Ok(account_name) => account_name,
Err(_) => continue,
};
if let ControlFlow::Break(result) = func(account_name) {
return result;
}
}
Ok(())
}
Err(err) if err.not_found() => Ok(()),
Err(err) => Err(err.into()),
}
}
// Mark account as deactivated
pub(crate) fn mark_account_deactivated(account_name: &str) -> Result<(), Error> {
let from = account_config_filename(account_name);
for i in 0..100 {
let to = account_config_filename(&format!("_deactivated_{}_{}", account_name, i));
if !Path::new(&to).exists() {
return std::fs::rename(&from, &to).map_err(|err| {
format_err!(
"failed to move account path {:?} to {:?} - {}",
from,
to,
err
)
});
}
}
bail!(
"No free slot to rename deactivated account {:?}, please cleanup {:?}",
from,
acme_account_dir()
);
}
// Load an existing ACME account by name.
pub(crate) async fn load_account_config(account_name: &str) -> Result<AccountData, Error> {
let account_config_filename = account_config_filename(account_name);
let data = match tokio::fs::read(&account_config_filename).await {
Ok(data) => data,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
bail!("acme account '{}' does not exist", account_name)
}
Err(err) => bail!(
"failed to load acme account from {:?} - {}",
account_config_filename,
err
),
};
let data: AccountData = serde_json::from_slice(&data).map_err(|err| {
format_err!(
"failed to parse acme account from {:?} - {}",
account_config_filename,
err
)
})?;
Ok(data)
}
// Save an new ACME account (fails if the file already exists).
pub(crate) fn create_account_config(
account_name: &AcmeAccountName,
account: &AccountData,
) -> Result<(), Error> {
let account_config_filename = account_config_filename(account_name.as_ref());
let file = OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open(&account_config_filename)
.map_err(|err| {
format_err!(
"failed to open {:?} for writing: {}",
account_config_filename,
err
)
})?;
serde_json::to_writer_pretty(file, account).map_err(|err| {
format_err!(
"failed to write acme account to {:?}: {}",
account_config_filename,
err
)
})?;
Ok(())
}
// Save ACME account data (overtwrite existing data).
pub(crate) fn save_account_config(
account_name: &AcmeAccountName,
account: &AccountData,
) -> Result<(), Error> {
let account_config_filename = account_config_filename(account_name.as_ref());
let mut data = Vec::<u8>::new();
serde_json::to_writer_pretty(&mut data, account).map_err(|err| {
format_err!(
"failed to serialize acme account to {:?}: {}",
account_config_filename,
err
)
})?;
replace_secret_config(account_config_filename, &data)
}

View File

@ -0,0 +1,340 @@
//! Plugin type definitions.
use std::future::Future;
use std::pin::Pin;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{bail, format_err, Error};
use futures::TryFutureExt;
use http::{Request, Response};
use http_body_util::Full;
use hyper::body::{Bytes, Incoming};
use hyper::server::conn::http1;
use hyper_util::rt::TokioIo;
use std::net::{IpAddr, SocketAddr};
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWriteExt, BufReader};
use tokio::net::TcpListener;
use tokio::process::Command;
use proxmox_acme::async_client::AcmeClient;
use proxmox_acme::{Authorization, Challenge};
use proxmox_rest_server::WorkerTask;
use crate::plugin_config::PluginData;
use crate::types::{AcmeDomain, DnsPlugin};
const PROXMOX_ACME_SH_PATH: &str = "/usr/share/proxmox-acme/proxmox-acme";
pub(crate) fn get_acme_plugin(
plugin_data: &PluginData,
name: &str,
) -> Result<Option<Box<dyn AcmePlugin + Send + Sync + 'static>>, Error> {
let (ty, data) = match plugin_data.get(name) {
Some(plugin) => plugin,
None => return Ok(None),
};
Ok(Some(match ty.as_str() {
"dns" => {
let plugin: DnsPlugin = serde::Deserialize::deserialize(data)?;
Box::new(plugin)
}
"standalone" => {
// this one has no config
Box::<StandaloneServer>::default()
}
other => bail!("missing implementation for plugin type '{}'", other),
}))
}
pub(crate) trait AcmePlugin {
/// Setup everything required to trigger the validation and return the corresponding validation
/// URL.
fn setup<'fut, 'a: 'fut, 'b: 'fut, 'c: 'fut, 'd: 'fut>(
&'a mut self,
client: &'b mut AcmeClient,
authorization: &'c Authorization,
domain: &'d AcmeDomain,
task: Arc<WorkerTask>,
) -> Pin<Box<dyn Future<Output = Result<&'c str, Error>> + Send + 'fut>>;
fn teardown<'fut, 'a: 'fut, 'b: 'fut, 'c: 'fut, 'd: 'fut>(
&'a mut self,
client: &'b mut AcmeClient,
authorization: &'c Authorization,
domain: &'d AcmeDomain,
task: Arc<WorkerTask>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'fut>>;
}
fn extract_challenge<'a>(
authorization: &'a Authorization,
ty: &str,
) -> Result<&'a Challenge, Error> {
authorization
.challenges
.iter()
.find(|ch| ch.ty == ty)
.ok_or_else(|| format_err!("no supported challenge type ({}) found", ty))
}
async fn pipe_to_tasklog<T: AsyncRead + Unpin>(
pipe: T,
task: Arc<WorkerTask>,
) -> Result<(), std::io::Error> {
let mut pipe = BufReader::new(pipe);
let mut line = String::new();
loop {
line.clear();
match pipe.read_line(&mut line).await {
Ok(0) => return Ok(()),
Ok(_) => task.log_message(line.as_str()),
Err(err) => return Err(err),
}
}
}
impl DnsPlugin {
async fn action<'a>(
&self,
client: &mut AcmeClient,
authorization: &'a Authorization,
domain: &AcmeDomain,
task: Arc<WorkerTask>,
action: &str,
) -> Result<&'a str, Error> {
let challenge = extract_challenge(authorization, "dns-01")?;
let mut stdin_data = client
.dns_01_txt_value(
challenge
.token()
.ok_or_else(|| format_err!("missing token in challenge"))?,
)?
.into_bytes();
stdin_data.push(b'\n');
stdin_data.extend(self.data.as_bytes());
if stdin_data.last() != Some(&b'\n') {
stdin_data.push(b'\n');
}
let mut command = Command::new("/usr/bin/setpriv");
#[rustfmt::skip]
command.args([
"--reuid", "nobody",
"--regid", "nogroup",
"--clear-groups",
"--reset-env",
"--",
"/bin/bash",
PROXMOX_ACME_SH_PATH,
action,
&self.core.api,
domain.alias.as_deref().unwrap_or(&domain.domain),
]);
// We could use 1 socketpair, but tokio wraps them all in `File` internally causing `close`
// to be called separately on all of them without exception, so we need 3 pipes :-(
let mut child = command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().expect("Stdio::piped()");
let stdout = child.stdout.take().expect("Stdio::piped() failed?");
let stdout = pipe_to_tasklog(stdout, Arc::clone(&task));
let stderr = child.stderr.take().expect("Stdio::piped() failed?");
let stderr = pipe_to_tasklog(stderr, Arc::clone(&task));
let stdin = async move {
stdin.write_all(&stdin_data).await?;
stdin.flush().await?;
Ok::<_, std::io::Error>(())
};
match futures::try_join!(stdin, stdout, stderr) {
Ok(((), (), ())) => (),
Err(err) => {
if let Err(err) = child.kill().await {
task.log_message(format!(
"failed to kill '{} {}' command: {}",
PROXMOX_ACME_SH_PATH, action, err
));
}
bail!("'{}' failed: {}", PROXMOX_ACME_SH_PATH, err);
}
}
let status = child.wait().await?;
if !status.success() {
bail!(
"'{} {}' exited with error ({})",
PROXMOX_ACME_SH_PATH,
action,
status.code().unwrap_or(-1)
);
}
Ok(&challenge.url)
}
}
impl AcmePlugin for DnsPlugin {
fn setup<'fut, 'a: 'fut, 'b: 'fut, 'c: 'fut, 'd: 'fut>(
&'a mut self,
client: &'b mut AcmeClient,
authorization: &'c Authorization,
domain: &'d AcmeDomain,
task: Arc<WorkerTask>,
) -> Pin<Box<dyn Future<Output = Result<&'c str, Error>> + Send + 'fut>> {
Box::pin(async move {
let result = self
.action(client, authorization, domain, task.clone(), "setup")
.await;
let validation_delay = self.core.validation_delay.unwrap_or(30) as u64;
if validation_delay > 0 {
task.log_message(format!(
"Sleeping {} seconds to wait for TXT record propagation",
validation_delay
));
tokio::time::sleep(Duration::from_secs(validation_delay)).await;
}
result
})
}
fn teardown<'fut, 'a: 'fut, 'b: 'fut, 'c: 'fut, 'd: 'fut>(
&'a mut self,
client: &'b mut AcmeClient,
authorization: &'c Authorization,
domain: &'d AcmeDomain,
task: Arc<WorkerTask>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'fut>> {
Box::pin(async move {
self.action(client, authorization, domain, task, "teardown")
.await
.map(drop)
})
}
}
#[derive(Default)]
struct StandaloneServer {
abort_handle: Option<futures::future::AbortHandle>,
}
// In case the "order_certificates" future gets dropped between setup & teardown, let's also cancel
// the HTTP listener on Drop:
impl Drop for StandaloneServer {
fn drop(&mut self) {
self.stop();
}
}
impl StandaloneServer {
fn stop(&mut self) {
if let Some(abort) = self.abort_handle.take() {
abort.abort();
}
}
}
async fn standalone_respond(
req: Request<Incoming>,
path: Arc<String>,
key_auth: Arc<String>,
) -> Result<Response<Full<Bytes>>, hyper::Error> {
if req.method() == hyper::Method::GET && req.uri().path() == path.as_str() {
Ok(Response::builder()
.status(http::StatusCode::OK)
.body(Full::from(Bytes::from(key_auth.as_bytes().to_owned())))
.unwrap())
} else {
Ok(Response::builder()
.status(http::StatusCode::NOT_FOUND)
.body("Not found.".into())
.unwrap())
}
}
impl AcmePlugin for StandaloneServer {
fn setup<'fut, 'a: 'fut, 'b: 'fut, 'c: 'fut, 'd: 'fut>(
&'a mut self,
client: &'b mut AcmeClient,
authorization: &'c Authorization,
_domain: &'d AcmeDomain,
_task: Arc<WorkerTask>,
) -> Pin<Box<dyn Future<Output = Result<&'c str, Error>> + Send + 'fut>> {
use hyper::service::service_fn;
Box::pin(async move {
self.stop();
let challenge = extract_challenge(authorization, "http-01")?;
let token = challenge
.token()
.ok_or_else(|| format_err!("missing token in challenge"))?;
let key_auth = Arc::new(client.key_authorization(token)?);
let path = Arc::new(format!("/.well-known/acme-challenge/{}", token));
// `[::]:80` first, then `*:80`
let dual = SocketAddr::new(IpAddr::from([0u16; 8]), 80);
let ipv4 = SocketAddr::new(IpAddr::from([0u8; 4]), 80);
let incoming = TcpListener::bind(dual)
.or_else(|_| TcpListener::bind(ipv4))
.await?;
let server = async move {
loop {
let key_auth = Arc::clone(&key_auth);
let path = Arc::clone(&path);
match incoming.accept().await {
Ok((tcp, _)) => {
let io = TokioIo::new(tcp);
let service = service_fn(move |request| {
standalone_respond(
request,
Arc::clone(&path),
Arc::clone(&key_auth),
)
});
tokio::task::spawn(async move {
if let Err(err) =
http1::Builder::new().serve_connection(io, service).await
{
println!("Error serving connection: {err:?}");
}
});
}
Err(err) => println!("Error accepting connection: {err:?}"),
}
}
};
let (future, abort) = futures::future::abortable(server);
self.abort_handle = Some(abort);
tokio::spawn(future);
Ok(challenge.url.as_str())
})
}
fn teardown<'fut, 'a: 'fut, 'b: 'fut, 'c: 'fut, 'd: 'fut>(
&'a mut self,
_client: &'b mut AcmeClient,
_authorization: &'c Authorization,
_domain: &'d AcmeDomain,
_task: Arc<WorkerTask>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'fut>> {
Box::pin(async move {
if let Some(abort) = self.abort_handle.take() {
abort.abort();
}
Ok(())
})
}
}

View File

@ -0,0 +1,400 @@
use std::mem::MaybeUninit;
use std::sync::Arc;
use std::time::Duration;
use foreign_types::ForeignTypeRef;
use anyhow::{bail, format_err, Error};
use openssl::pkey::{PKey, Private};
use openssl::rsa::Rsa;
use openssl::x509::{X509Builder, X509};
use proxmox_acme::async_client::AcmeClient;
use proxmox_log::{info, warn};
use proxmox_rest_server::WorkerTask;
use crate::types::{AcmeConfig, AcmeDomain};
use crate::CertificateInfo;
pub async fn revoke_certificate(acme_config: &AcmeConfig, certificate: &[u8]) -> Result<(), Error> {
let mut acme = super::account_config::load_account_config(&acme_config.account)
.await?
.client();
acme.revoke_certificate(certificate, None).await?;
Ok(())
}
pub struct OrderedCertificate {
pub certificate: Vec<u8>,
pub private_key_pem: Vec<u8>,
}
pub async fn order_certificate(
worker: Arc<WorkerTask>,
acme_config: &AcmeConfig,
domains: &[AcmeDomain],
) -> Result<Option<OrderedCertificate>, Error> {
use proxmox_acme::authorization::Status;
use proxmox_acme::order::Identifier;
let get_domain_config = |domain: &str| {
domains
.iter()
.find(|d| d.domain == domain)
.ok_or_else(|| format_err!("no config for domain '{}'", domain))
};
if domains.is_empty() {
info!("No domains configured to be ordered from an ACME server.");
return Ok(None);
}
let mut acme = super::account_config::load_account_config(&acme_config.account)
.await?
.client();
let (plugins, _) = super::plugin_config::plugin_config()?;
info!("Placing ACME order");
let order = acme
.new_order(domains.iter().map(|d| d.domain.to_ascii_lowercase()))
.await?;
info!("Order URL: {}", order.location);
let identifiers: Vec<String> = order
.data
.identifiers
.iter()
.map(|identifier| match identifier {
Identifier::Dns(domain) => domain.clone(),
})
.collect();
for auth_url in &order.data.authorizations {
info!("Getting authorization details from '{}'", auth_url);
let mut auth = acme.get_authorization(auth_url).await?;
let domain = match &mut auth.identifier {
Identifier::Dns(domain) => domain.to_ascii_lowercase(),
};
if auth.status == Status::Valid {
info!("{} is already validated!", domain);
continue;
}
info!("The validation for {} is pending", domain);
let domain_config: &AcmeDomain = get_domain_config(&domain)?;
let plugin_id = domain_config.plugin.as_deref().unwrap_or("standalone");
let mut plugin_cfg =
crate::acme_plugin::get_acme_plugin(&plugins, plugin_id)?.ok_or_else(|| {
format_err!("plugin '{}' for domain '{}' not found!", plugin_id, domain)
})?;
info!("Setting up validation plugin");
let validation_url = plugin_cfg
.setup(&mut acme, &auth, domain_config, Arc::clone(&worker))
.await?;
let result = request_validation(&mut acme, auth_url, validation_url).await;
if let Err(err) = plugin_cfg
.teardown(&mut acme, &auth, domain_config, Arc::clone(&worker))
.await
{
warn!(
"Failed to teardown plugin '{}' for domain '{}' - {}",
plugin_id, domain, err
);
}
result?;
}
info!("All domains validated");
info!("Creating CSR");
let csr = proxmox_acme::util::Csr::generate(&identifiers, &Default::default())?;
let mut finalize_error_cnt = 0u8;
let order_url = &order.location;
let mut order;
loop {
use proxmox_acme::order::Status;
order = acme.get_order(order_url).await?;
match order.status {
Status::Pending => {
info!("still pending, trying to finalize anyway");
let finalize = order
.finalize
.as_deref()
.ok_or_else(|| format_err!("missing 'finalize' URL in order"))?;
if let Err(err) = acme.finalize(finalize, &csr.data).await {
if finalize_error_cnt >= 5 {
return Err(err);
}
finalize_error_cnt += 1;
}
tokio::time::sleep(Duration::from_secs(5)).await;
}
Status::Ready => {
info!("order is ready, finalizing");
let finalize = order
.finalize
.as_deref()
.ok_or_else(|| format_err!("missing 'finalize' URL in order"))?;
acme.finalize(finalize, &csr.data).await?;
tokio::time::sleep(Duration::from_secs(5)).await;
}
Status::Processing => {
info!("still processing, trying again in 30 seconds");
tokio::time::sleep(Duration::from_secs(30)).await;
}
Status::Valid => {
info!("valid");
break;
}
other => bail!("order status: {:?}", other),
}
}
info!("Downloading certificate");
let certificate = acme
.get_certificate(
order
.certificate
.as_deref()
.ok_or_else(|| format_err!("missing certificate url in finalized order"))?,
)
.await?;
Ok(Some(OrderedCertificate {
certificate: certificate.to_vec(),
private_key_pem: csr.private_key_pem,
}))
}
async fn request_validation(
acme: &mut AcmeClient,
auth_url: &str,
validation_url: &str,
) -> Result<(), Error> {
info!("Triggering validation");
acme.request_challenge_validation(validation_url).await?;
info!("Sleeping for 5 seconds");
tokio::time::sleep(Duration::from_secs(5)).await;
loop {
use proxmox_acme::authorization::Status;
let auth = acme.get_authorization(auth_url).await?;
match auth.status {
Status::Pending => {
info!("Status is still 'pending', trying again in 10 seconds");
tokio::time::sleep(Duration::from_secs(10)).await;
}
Status::Valid => return Ok(()),
other => bail!(
"validating challenge '{}' failed - status: {:?}",
validation_url,
other
),
}
}
}
pub fn create_self_signed_cert(
product_name: &str,
nodename: &str,
domain: Option<&str>,
) -> Result<(PKey<Private>, X509), Error> {
let rsa = Rsa::generate(4096).unwrap();
let mut x509 = X509Builder::new()?;
x509.set_version(2)?;
let today = openssl::asn1::Asn1Time::days_from_now(0)?;
x509.set_not_before(&today)?;
let expire = openssl::asn1::Asn1Time::days_from_now(365 * 1000)?;
x509.set_not_after(&expire)?;
let mut fqdn = nodename.to_owned();
if let Some(domain) = domain {
fqdn.push('.');
fqdn.push_str(domain);
}
// we try to generate an unique 'subject' to avoid browser problems
//(reused serial numbers, ..)
let uuid = proxmox_uuid::Uuid::generate();
let mut subject_name = openssl::x509::X509NameBuilder::new()?;
subject_name.append_entry_by_text("O", product_name)?;
subject_name.append_entry_by_text("OU", &format!("{:X}", uuid))?;
subject_name.append_entry_by_text("CN", &fqdn)?;
let subject_name = subject_name.build();
x509.set_subject_name(&subject_name)?;
x509.set_issuer_name(&subject_name)?;
let bc = openssl::x509::extension::BasicConstraints::new(); // CA = false
let bc = bc.build()?;
x509.append_extension(bc)?;
let usage = openssl::x509::extension::ExtendedKeyUsage::new()
.server_auth()
.build()?;
x509.append_extension(usage)?;
let context = x509.x509v3_context(None, None);
let mut alt_names = openssl::x509::extension::SubjectAlternativeName::new();
alt_names.ip("127.0.0.1");
alt_names.ip("::1");
alt_names.dns("localhost");
if nodename != "localhost" {
alt_names.dns(nodename);
}
if nodename != fqdn {
alt_names.dns(&fqdn);
}
let alt_names = alt_names.build(&context)?;
x509.append_extension(alt_names)?;
let pub_pem = rsa.public_key_to_pem()?;
let pubkey = PKey::public_key_from_pem(&pub_pem)?;
x509.set_pubkey(&pubkey)?;
let context = x509.x509v3_context(None, None);
let ext = openssl::x509::extension::SubjectKeyIdentifier::new().build(&context)?;
x509.append_extension(ext)?;
let context = x509.x509v3_context(None, None);
let ext = openssl::x509::extension::AuthorityKeyIdentifier::new()
.keyid(true)
.build(&context)?;
x509.append_extension(ext)?;
let privkey = PKey::from_rsa(rsa)?;
x509.sign(&privkey, openssl::hash::MessageDigest::sha256())?;
Ok((privkey, x509.build()))
}
impl CertificateInfo {
pub fn from_pem(filename: &str, cert_pem: &[u8]) -> Result<Self, Error> {
let x509 = openssl::x509::X509::from_pem(cert_pem)?;
let cert_pem = String::from_utf8(cert_pem.to_vec())
.map_err(|_| format_err!("certificate in {:?} is not a valid PEM file", filename))?;
let pubkey = x509.public_key()?;
let subject = x509name_to_string(x509.subject_name())?;
let issuer = x509name_to_string(x509.issuer_name())?;
let fingerprint = x509.digest(openssl::hash::MessageDigest::sha256())?;
let fingerprint = hex::encode(fingerprint)
.as_bytes()
.chunks(2)
.map(|v| std::str::from_utf8(v).unwrap())
.collect::<Vec<&str>>()
.join(":");
let public_key_type = openssl::nid::Nid::from_raw(pubkey.id().as_raw())
.long_name()
.unwrap_or("<unsupported key type>")
.to_owned();
let san = x509
.subject_alt_names()
.map(|san| {
san.into_iter()
.filter_map(|name| {
// this is not actually a map and we don't want to break the pattern
#[allow(clippy::manual_map)]
if let Some(name) = name.dnsname() {
Some(format!("DNS: {name}"))
} else if let Some(ip) = name.ipaddress() {
Some(format!("IP: {ip:?}"))
} else if let Some(email) = name.email() {
Some(format!("EMAIL: {email}"))
} else if let Some(uri) = name.uri() {
Some(format!("URI: {uri}"))
} else {
None
}
})
.collect()
})
.unwrap_or_default();
Ok(CertificateInfo {
filename: filename.to_string(),
pem: Some(cert_pem),
subject,
issuer,
fingerprint: Some(fingerprint),
public_key_bits: Some(pubkey.bits()),
notbefore: asn1_time_to_unix(x509.not_before()).ok(),
notafter: asn1_time_to_unix(x509.not_after()).ok(),
public_key_type,
san,
})
}
/// Check if the certificate is expired at or after a specific unix epoch.
pub fn is_expired_after_epoch(&self, epoch: i64) -> Result<bool, Error> {
if let Some(notafter) = self.notafter {
Ok(notafter < epoch)
} else {
Ok(false)
}
}
}
fn x509name_to_string(name: &openssl::x509::X509NameRef) -> Result<String, Error> {
let mut parts = Vec::new();
for entry in name.entries() {
parts.push(format!(
"{} = {}",
entry.object().nid().short_name()?,
entry.data().as_utf8()?
));
}
Ok(parts.join(", "))
}
// C type:
#[allow(non_camel_case_types)]
type ASN1_TIME = <openssl::asn1::Asn1TimeRef as ForeignTypeRef>::CType;
unsafe extern "C" {
fn ASN1_TIME_to_tm(s: *const ASN1_TIME, tm: *mut libc::tm) -> libc::c_int;
}
fn asn1_time_to_unix(time: &openssl::asn1::Asn1TimeRef) -> Result<i64, Error> {
let mut c_tm = MaybeUninit::<libc::tm>::uninit();
let rc = unsafe { ASN1_TIME_to_tm(time.as_ptr(), c_tm.as_mut_ptr()) };
if rc != 1 {
bail!("failed to parse ASN1 time");
}
let mut c_tm = unsafe { c_tm.assume_init() };
proxmox_time::timegm(&mut c_tm)
}

View File

@ -0,0 +1,71 @@
//! Read DNS Challenge schemas.
//!
//! Those schemas are provided by debian package "libproxmox-acme-plugins".
use std::sync::{Arc, LazyLock, Mutex};
use std::time::SystemTime;
use anyhow::Error;
use serde::Serialize;
use serde_json::Value;
use proxmox_sys::fs::file_read_string;
use crate::types::AcmeChallengeSchema;
const ACME_DNS_SCHEMA_FN: &str = "/usr/share/proxmox-acme/dns-challenge-schema.json";
/// Wrapper for efficient Arc use when returning the ACME challenge-plugin schema for serializing.
pub struct ChallengeSchemaWrapper {
inner: Arc<Vec<AcmeChallengeSchema>>,
}
impl Serialize for ChallengeSchemaWrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.serialize(serializer)
}
}
fn load_dns_challenge_schema() -> Result<Vec<AcmeChallengeSchema>, Error> {
let raw = file_read_string(ACME_DNS_SCHEMA_FN)?;
let schemas: serde_json::Map<String, Value> = serde_json::from_str(&raw)?;
Ok(schemas
.iter()
.map(|(id, schema)| AcmeChallengeSchema {
id: id.to_owned(),
name: schema
.get("name")
.and_then(Value::as_str)
.unwrap_or(id)
.to_owned(),
ty: "dns".into(),
schema: schema.to_owned(),
})
.collect())
}
pub fn get_cached_challenge_schemas() -> Result<ChallengeSchemaWrapper, Error> {
#[allow(clippy::type_complexity)]
static CACHE: LazyLock<Mutex<Option<(Arc<Vec<AcmeChallengeSchema>>, SystemTime)>>> =
LazyLock::new(|| Mutex::new(None));
// the actual loading code
let mut last = CACHE.lock().unwrap();
let actual_mtime = std::fs::metadata(ACME_DNS_SCHEMA_FN)?.modified()?;
let schema = match &*last {
Some((schema, cached_mtime)) if *cached_mtime >= actual_mtime => schema.clone(),
_ => {
let new_schema = Arc::new(load_dns_challenge_schema()?);
*last = Some((Arc::clone(&new_schema), actual_mtime));
new_schema
}
};
Ok(ChallengeSchemaWrapper { inner: schema })
}

View File

@ -0,0 +1,18 @@
use std::borrow::Cow;
use crate::types::KnownAcmeDirectory;
/// List of known ACME directorties.
pub const KNOWN_ACME_DIRECTORIES: &[KnownAcmeDirectory] = &[
KnownAcmeDirectory {
name: Cow::Borrowed("Let's Encrypt V2"),
url: Cow::Borrowed("https://acme-v02.api.letsencrypt.org/directory"),
},
KnownAcmeDirectory {
name: Cow::Borrowed("Let's Encrypt V2 Staging"),
url: Cow::Borrowed("https://acme-staging-v02.api.letsencrypt.org/directory"),
},
];
/// Default ACME directorties.
pub const DEFAULT_ACME_DIRECTORY_ENTRY: &KnownAcmeDirectory = &KNOWN_ACME_DIRECTORIES[0];

View File

@ -0,0 +1,55 @@
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use anyhow::Error;
use proxmox_product_config::create_secret_dir;
#[derive(Debug)]
struct AcmeApiConfig {
acme_config_dir: PathBuf,
acme_account_dir: PathBuf,
}
static ACME_ACME_CONFIG: OnceLock<AcmeApiConfig> = OnceLock::new();
/// Initialize the global product configuration.
pub fn init<P: AsRef<Path>>(acme_config_dir: P, create_subdirs: bool) -> Result<(), Error> {
let acme_config_dir = acme_config_dir.as_ref().to_owned();
ACME_ACME_CONFIG
.set(AcmeApiConfig {
acme_account_dir: acme_config_dir.join("accounts"),
acme_config_dir,
})
.expect("cannot set acme configuration twice");
if create_subdirs {
create_secret_dir(self::acme_config_dir(), false)?;
create_secret_dir(acme_account_dir(), false)?;
}
Ok(())
}
fn acme_api_config() -> &'static AcmeApiConfig {
ACME_ACME_CONFIG
.get()
.expect("ProxmoxProductConfig is not initialized!")
}
fn acme_config_dir() -> &'static Path {
acme_api_config().acme_config_dir.as_path()
}
pub(crate) fn acme_account_dir() -> &'static Path {
acme_api_config().acme_account_dir.as_path()
}
pub(crate) fn plugin_cfg_filename() -> PathBuf {
acme_config_dir().join("plugins.cfg")
}
pub(crate) fn plugin_cfg_lockfile() -> PathBuf {
acme_config_dir().join("plugins.lck")
}

View File

@ -0,0 +1,48 @@
//! ACME API crate (API types and API implementation)
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
mod types;
pub use types::*;
#[cfg(feature = "impl")]
mod init;
#[cfg(feature = "impl")]
pub use init::*;
#[cfg(feature = "impl")]
mod config;
#[cfg(feature = "impl")]
pub use config::{DEFAULT_ACME_DIRECTORY_ENTRY, KNOWN_ACME_DIRECTORIES};
#[cfg(feature = "impl")]
mod challenge_schemas;
#[cfg(feature = "impl")]
pub use challenge_schemas::{get_cached_challenge_schemas, ChallengeSchemaWrapper};
#[cfg(feature = "impl")]
mod account_config;
#[cfg(feature = "impl")]
pub use account_config::account_config_filename;
#[cfg(feature = "impl")]
mod plugin_config;
#[cfg(feature = "impl")]
mod account_api_impl;
#[cfg(feature = "impl")]
pub use account_api_impl::{
deactivate_account, get_account, get_tos, list_accounts, register_account, update_account,
};
#[cfg(feature = "impl")]
mod plugin_api_impl;
#[cfg(feature = "impl")]
pub use plugin_api_impl::{add_plugin, delete_plugin, get_plugin, list_plugins, update_plugin};
#[cfg(feature = "impl")]
pub(crate) mod acme_plugin;
#[cfg(feature = "impl")]
mod certificate_helpers;
#[cfg(feature = "impl")]
pub use certificate_helpers::{create_self_signed_cert, order_certificate, revoke_certificate};

View File

@ -0,0 +1,169 @@
//! ACME plugin configuration API implementation
use anyhow::{bail, format_err, Error};
use serde::Deserialize;
use serde_json::Value;
use proxmox_config_digest::ConfigDigest;
use proxmox_schema::param_bail;
use crate::types::{
DeletablePluginProperty, DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PluginConfig,
};
use proxmox_router::{http_bail, RpcEnvironment};
pub fn list_plugins(rpcenv: &mut dyn RpcEnvironment) -> Result<Vec<PluginConfig>, Error> {
let (plugins, digest) = super::plugin_config::plugin_config()?;
rpcenv["digest"] = digest.to_hex().into();
Ok(plugins
.iter()
.map(|(id, (ty, data))| modify_cfg_for_api(id, ty, data))
.collect())
}
pub fn get_plugin(id: String, rpcenv: &mut dyn RpcEnvironment) -> Result<PluginConfig, Error> {
let (plugins, digest) = super::plugin_config::plugin_config()?;
rpcenv["digest"] = digest.to_hex().into();
match plugins.get(&id) {
Some((ty, data)) => Ok(modify_cfg_for_api(&id, ty, data)),
None => http_bail!(NOT_FOUND, "no such plugin"),
}
}
pub fn add_plugin(r#type: String, core: DnsPluginCore, data: String) -> Result<(), Error> {
// Currently we only support DNS plugins and the standalone plugin is "fixed":
if r#type != "dns" {
param_bail!("type", "invalid ACME plugin type: {:?}", r#type);
}
let data = String::from_utf8(proxmox_base64::decode(data)?)
.map_err(|_| format_err!("data must be valid UTF-8"))?;
let id = core.id.clone();
let _lock = super::plugin_config::lock_plugin_config()?;
let (mut plugins, _digest) = super::plugin_config::plugin_config()?;
if plugins.contains_key(&id) {
param_bail!("id", "ACME plugin ID {:?} already exists", id);
}
let plugin = serde_json::to_value(DnsPlugin { core, data })?;
plugins.insert(id, r#type, plugin);
super::plugin_config::save_plugin_config(&plugins)?;
Ok(())
}
pub fn update_plugin(
id: String,
update: DnsPluginCoreUpdater,
data: Option<String>,
delete: Option<Vec<DeletablePluginProperty>>,
digest: Option<ConfigDigest>,
) -> Result<(), Error> {
let data = data
.as_deref()
.map(proxmox_base64::decode)
.transpose()?
.map(String::from_utf8)
.transpose()
.map_err(|_| format_err!("data must be valid UTF-8"))?;
let _lock = super::plugin_config::lock_plugin_config()?;
let (mut plugins, expected_digest) = super::plugin_config::plugin_config()?;
expected_digest.detect_modification(digest.as_ref())?;
match plugins.get_mut(&id) {
Some((ty, ref mut entry)) => {
if ty != "dns" {
bail!("cannot update plugin of type {:?}", ty);
}
let mut plugin = DnsPlugin::deserialize(&*entry)?;
if let Some(delete) = delete {
for delete_prop in delete {
match delete_prop {
DeletablePluginProperty::ValidationDelay => {
plugin.core.validation_delay = None;
}
DeletablePluginProperty::Disable => {
plugin.core.disable = None;
}
}
}
}
if let Some(data) = data {
plugin.data = data;
}
if let Some(api) = update.api {
plugin.core.api = api;
}
if update.validation_delay.is_some() {
plugin.core.validation_delay = update.validation_delay;
}
if update.disable.is_some() {
plugin.core.disable = update.disable;
}
*entry = serde_json::to_value(plugin)?;
}
None => http_bail!(NOT_FOUND, "no such plugin"),
}
super::plugin_config::save_plugin_config(&plugins)?;
Ok(())
}
pub fn delete_plugin(id: String) -> Result<(), Error> {
let _lock = super::plugin_config::lock_plugin_config()?;
let (mut plugins, _digest) = super::plugin_config::plugin_config()?;
if plugins.remove(&id).is_none() {
http_bail!(NOT_FOUND, "no such plugin");
}
super::plugin_config::save_plugin_config(&plugins)?;
Ok(())
}
// See PMG/PVE's $modify_cfg_for_api sub
fn modify_cfg_for_api(id: &str, ty: &str, data: &Value) -> PluginConfig {
let mut entry = data.clone();
let obj = entry.as_object_mut().unwrap();
obj.remove("id");
obj.insert("plugin".to_string(), Value::String(id.to_owned()));
obj.insert("type".to_string(), Value::String(ty.to_owned()));
// FIXME: This needs to go once the `Updater` is fixed.
// None of these should be able to fail unless the user changed the files by hand, in which
// case we leave the unmodified string in the Value for now. This will be handled with an error
// later.
if let Some(Value::String(ref mut data)) = obj.get_mut("data") {
if let Ok(new) = proxmox_base64::url::decode_no_pad(&data) {
if let Ok(utf8) = String::from_utf8(new) {
*data = utf8;
}
}
}
// PVE/PMG do this explicitly for ACME plugins...
// obj.insert("digest".to_string(), Value::String(digest.clone()));
serde_json::from_value(entry).unwrap_or_else(|_| PluginConfig {
plugin: "*Error*".to_string(),
ty: "*Error*".to_string(),
..Default::default()
})
}

View File

@ -0,0 +1,112 @@
//! ACME plugin configuration helpers (SectionConfig implementation)
use std::sync::LazyLock;
use anyhow::Error;
use serde_json::Value;
use proxmox_config_digest::ConfigDigest;
use proxmox_product_config::{open_api_lockfile, replace_secret_config, ApiLockGuard};
use proxmox_schema::{ApiType, Schema};
use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
use crate::types::{DnsPlugin, StandalonePlugin, PLUGIN_ID_SCHEMA};
static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
impl DnsPlugin {
pub fn decode_data(&self, output: &mut Vec<u8>) -> Result<(), Error> {
Ok(proxmox_base64::url::decode_to_vec_no_pad(
&self.data, output,
)?)
}
}
fn init() -> SectionConfig {
let mut config = SectionConfig::new(&PLUGIN_ID_SCHEMA);
let standalone_schema = match &StandalonePlugin::API_SCHEMA {
Schema::Object(schema) => schema,
_ => unreachable!(),
};
let standalone_plugin = SectionConfigPlugin::new(
"standalone".to_string(),
Some("id".to_string()),
standalone_schema,
);
config.register_plugin(standalone_plugin);
let dns_challenge_schema = match DnsPlugin::API_SCHEMA {
Schema::AllOf(ref schema) => schema,
_ => unreachable!(),
};
let dns_challenge_plugin = SectionConfigPlugin::new(
"dns".to_string(),
Some("id".to_string()),
dns_challenge_schema,
);
config.register_plugin(dns_challenge_plugin);
config
}
pub(crate) fn lock_plugin_config() -> Result<ApiLockGuard, Error> {
let plugin_cfg_lockfile = crate::plugin_cfg_lockfile();
open_api_lockfile(plugin_cfg_lockfile, None, true)
}
pub(crate) fn plugin_config() -> Result<(PluginData, ConfigDigest), Error> {
let plugin_cfg_filename = crate::plugin_cfg_filename();
let content =
proxmox_sys::fs::file_read_optional_string(&plugin_cfg_filename)?.unwrap_or_default();
let digest = ConfigDigest::from_slice(content.as_bytes());
let mut data = CONFIG.parse(plugin_cfg_filename, &content)?;
if !data.sections.contains_key("standalone") {
let standalone = StandalonePlugin::default();
data.set_data("standalone", "standalone", &standalone)
.unwrap();
}
Ok((PluginData { data }, digest))
}
pub(crate) fn save_plugin_config(config: &PluginData) -> Result<(), Error> {
let plugin_cfg_filename = crate::plugin_cfg_filename();
let raw = CONFIG.write(&plugin_cfg_filename, &config.data)?;
replace_secret_config(plugin_cfg_filename, raw.as_bytes())
}
pub(crate) struct PluginData {
data: SectionConfigData,
}
// And some convenience helpers.
impl PluginData {
pub fn remove(&mut self, name: &str) -> Option<(String, Value)> {
self.data.sections.remove(name)
}
pub fn contains_key(&mut self, name: &str) -> bool {
self.data.sections.contains_key(name)
}
pub fn get(&self, name: &str) -> Option<&(String, Value)> {
self.data.sections.get(name)
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut (String, Value)> {
self.data.sections.get_mut(name)
}
pub fn insert(&mut self, id: String, ty: String, plugin: Value) {
self.data.sections.insert(id, (ty, plugin));
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &(String, Value))> + Send {
self.data.sections.iter()
}
}

View File

@ -0,0 +1,355 @@
//! ACME API type definitions.
use std::borrow::Cow;
use anyhow::Error;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use proxmox_schema::api_types::{DNS_ALIAS_FORMAT, DNS_NAME_FORMAT, SAFE_ID_FORMAT};
use proxmox_schema::{api, ApiStringFormat, ApiType, Schema, StringSchema, Updater};
use proxmox_acme::types::AccountData as AcmeAccountData;
#[api(
properties: {
san: {
type: Array,
items: {
description: "A SubjectAlternateName entry.",
type: String,
},
},
},
)]
/// Certificate information.
#[derive(PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct CertificateInfo {
/// Certificate file name.
pub filename: String,
/// Certificate subject name.
pub subject: String,
/// List of certificate's SubjectAlternativeName entries.
pub san: Vec<String>,
/// Certificate issuer name.
pub issuer: String,
/// Certificate's notBefore timestamp (UNIX epoch).
#[serde(skip_serializing_if = "Option::is_none")]
pub notbefore: Option<i64>,
/// Certificate's notAfter timestamp (UNIX epoch).
#[serde(skip_serializing_if = "Option::is_none")]
pub notafter: Option<i64>,
/// Certificate in PEM format.
#[serde(skip_serializing_if = "Option::is_none")]
pub pem: Option<String>,
/// Certificate's public key algorithm.
pub public_key_type: String,
/// Certificate's public key size if available.
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key_bits: Option<u32>,
/// The SSL Fingerprint.
#[serde(skip_serializing_if = "Option::is_none")]
pub fingerprint: Option<String>,
}
proxmox_schema::api_string_type! {
#[api(format: &SAFE_ID_FORMAT)]
/// ACME account name.
#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct AcmeAccountName(String);
}
#[api(
properties: {
name: { type: String },
url: { type: String },
},
)]
/// An ACME directory endpoint with a name and URL.
#[derive(Clone, Deserialize, Serialize, PartialEq)]
pub struct KnownAcmeDirectory {
/// The ACME directory's name.
pub name: Cow<'static, str>,
/// The ACME directory's endpoint URL.
pub url: Cow<'static, str>,
}
#[api(
properties: {
schema: {
type: Object,
additional_properties: true,
properties: {},
},
type: {
type: String,
},
},
)]
#[derive(Clone, Deserialize, Serialize, PartialEq)]
/// Schema for an ACME challenge plugin.
pub struct AcmeChallengeSchema {
/// Plugin ID.
pub id: String,
/// Human readable name, falls back to id.
pub name: String,
/// Plugin Type.
#[serde(rename = "type")]
pub ty: String,
/// The plugin's parameter schema.
pub schema: Value,
}
#[api(
properties: {
"domain": { format: &DNS_NAME_FORMAT },
"alias": {
optional: true,
format: &DNS_ALIAS_FORMAT,
},
"plugin": {
optional: true,
format: &SAFE_ID_FORMAT,
},
},
default_key: "domain",
)]
#[derive(Clone, PartialEq, Deserialize, Serialize)]
/// A domain entry for an ACME certificate.
pub struct AcmeDomain {
/// The domain to certify for.
pub domain: String,
/// The domain to use for challenges instead of the default acme challenge domain.
///
/// This is useful if you use CNAME entries to redirect `_acme-challenge.*` domains to a
/// different DNS server.
#[serde(skip_serializing_if = "Option::is_none")]
pub alias: Option<String>,
/// The plugin to use to validate this domain.
///
/// Empty means standalone HTTP validation is used.
#[serde(skip_serializing_if = "Option::is_none")]
pub plugin: Option<String>,
}
/// ACME domain configuration string [Schema].
pub const ACME_DOMAIN_PROPERTY_SCHEMA: Schema =
StringSchema::new("ACME domain configuration string")
.format(&ApiStringFormat::PropertyString(&AcmeDomain::API_SCHEMA))
.schema();
/// Parse [AcmeDomain] from property string.
pub fn parse_acme_domain_string(value_str: &str) -> Result<AcmeDomain, Error> {
let value = AcmeDomain::API_SCHEMA.parse_property_string(value_str)?;
let value: AcmeDomain = serde_json::from_value(value)?;
Ok(value)
}
/// Format [AcmeDomain] as property string.
pub fn create_acme_domain_string(config: &AcmeDomain) -> String {
proxmox_schema::property_string::print::<AcmeDomain>(config).unwrap()
}
#[api()]
#[derive(Clone, PartialEq, Deserialize, Serialize)]
/// ACME Account information.
///
/// This is what we return via the API.
pub struct AccountInfo {
/// Raw account data.
pub account: AcmeAccountData,
/// The ACME directory URL the account was created at.
pub directory: String,
/// The account's own URL within the ACME directory.
pub location: String,
/// The ToS URL, if the user agreed to one.
#[serde(skip_serializing_if = "Option::is_none")]
pub tos: Option<String>,
}
/// An ACME Account entry.
///
/// Currently only contains a 'name' property.
#[api()]
#[derive(Clone, PartialEq, Deserialize, Serialize)]
pub struct AcmeAccountEntry {
pub name: AcmeAccountName,
}
#[api()]
#[derive(Clone, PartialEq, Deserialize, Serialize)]
/// The ACME configuration.
///
/// Currently only contains the name of the account use.
pub struct AcmeConfig {
/// Account to use to acquire ACME certificates.
pub account: String,
}
/// Parse [AcmeConfig] from property string.
pub fn parse_acme_config_string(value_str: &str) -> Result<AcmeConfig, Error> {
let value = AcmeConfig::API_SCHEMA.parse_property_string(value_str)?;
let value: AcmeConfig = serde_json::from_value(value)?;
Ok(value)
}
/// Format [AcmeConfig] as property string.
pub fn create_acme_config_string(config: &AcmeConfig) -> String {
proxmox_schema::property_string::print::<AcmeConfig>(config).unwrap()
}
/// [Schema] for ACME Challenge Plugin ID.
pub const PLUGIN_ID_SCHEMA: Schema = StringSchema::new("ACME Challenge Plugin ID.")
.format(&SAFE_ID_FORMAT)
.min_length(1)
.max_length(32)
.schema();
#[api]
#[derive(Clone, Default, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
/// ACME plugin config. The API's format is inherited from PVE/PMG:
pub struct PluginConfig {
/// Plugin ID.
pub plugin: String,
/// Plugin type.
#[serde(rename = "type")]
pub ty: String,
/// DNS Api name.
#[serde(skip_serializing_if = "Option::is_none", default)]
pub api: Option<String>,
/// Plugin configuration data.
#[serde(skip_serializing_if = "Option::is_none", default)]
pub data: Option<String>,
/// Extra delay in seconds to wait before requesting validation.
///
/// Allows to cope with long TTL of DNS records.
#[serde(skip_serializing_if = "Option::is_none", default)]
pub validation_delay: Option<u32>,
/// Flag to disable the config.
#[serde(skip_serializing_if = "Option::is_none", default)]
pub disable: Option<bool>,
}
#[api(
properties: {
id: { schema: PLUGIN_ID_SCHEMA },
},
)]
#[derive(Deserialize, Serialize)]
/// Standalone ACME Plugin for the http-1 challenge.
pub struct StandalonePlugin {
/// Plugin ID.
id: String,
}
impl Default for StandalonePlugin {
fn default() -> Self {
Self {
id: "standalone".to_string(),
}
}
}
#[api(
properties: {
id: { schema: PLUGIN_ID_SCHEMA },
disable: {
optional: true,
default: false,
},
"validation-delay": {
default: 30,
optional: true,
minimum: 0,
maximum: 2 * 24 * 60 * 60,
},
},
)]
/// DNS ACME Challenge Plugin core data.
#[derive(Deserialize, Serialize, Updater)]
#[serde(rename_all = "kebab-case")]
pub struct DnsPluginCore {
/// Plugin ID.
#[updater(skip)]
pub id: String,
/// DNS API Plugin Id.
pub api: String,
/// Extra delay in seconds to wait before requesting validation.
///
/// Allows to cope with long TTL of DNS records.
#[serde(skip_serializing_if = "Option::is_none", default)]
pub validation_delay: Option<u32>,
/// Flag to disable the config.
#[serde(skip_serializing_if = "Option::is_none", default)]
pub disable: Option<bool>,
}
#[api(
properties: {
core: { type: DnsPluginCore },
},
)]
/// DNS ACME Challenge Plugin.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DnsPlugin {
#[serde(flatten)]
pub core: DnsPluginCore,
// We handle this property separately in the API calls.
/// DNS plugin data (base64url encoded without padding).
#[serde(with = "proxmox_serde::string_as_base64url_nopad")]
pub data: String,
}
#[api()]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Deletable plugin property names.
pub enum DeletablePluginProperty {
/// Delete the disable property
Disable,
/// Delete the validation-delay property
ValidationDelay,
}
#[api(
properties: {
name: { type: AcmeAccountName },
},
)]
/// An ACME Account entry.
///
/// Currently only contains a 'name' property.
#[derive(Clone, PartialEq, Deserialize, Serialize)]
pub struct AccountEntry {
pub name: AcmeAccountName,
}

46
proxmox-acme/Cargo.toml Normal file
View File

@ -0,0 +1,46 @@
[package]
name = "proxmox-acme"
description = "ACME client library"
version = "1.0.0"
exclude = [ "debian" ]
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
# For the ACME implementation
openssl = { workspace = true, optional = true }
# For the client
http = { workspace = true, optional = true }
proxmox-base64 = { workspace = true, features = [ "serde" ] }
proxmox-schema = { workspace = true, optional = true, features = [ "api-macro" ] }
proxmox-http = { workspace = true, optional = true, features = [ "client" ] }
anyhow = { workspace = true, optional = true }
bytes = { workspace = true, optional = true }
http-body-util = { workspace = true, optional = true }
hyper = { workspace = true, optional = true }
[dependencies.ureq]
optional = true
version = "3.0"
default-features = false
features = [ "native-tls", "gzip" ]
[features]
default = [ "impl" ]
api-types = [ "dep:proxmox-schema" ]
impl = [ "api-types", "dep:openssl" ]
client = [ "impl", "dep:http", "dep:ureq"]
async-client = [ "impl", "dep:http-body-util", "dep:hyper", "dep:proxmox-http", "dep:anyhow", "dep:bytes" ]
[dev-dependencies]
anyhow.workspace = true

View File

@ -0,0 +1,141 @@
rust-proxmox-acme (1.0.0-2) trixie; urgency=medium
* update to http/hyper 1.0
-- Proxmox Support Team <support@proxmox.com> Thu, 22 May 2025 15:34:38 +0200
rust-proxmox-acme (1.0.0-1) trixie; urgency=medium
* re-build for Debian Trixie based releases.
* update client to use ureq version 3
-- Proxmox Support Team <support@proxmox.com> Wed, 21 May 2025 14:51:54 +0200
rust-proxmox-acme (0.5.4-1) bookworm; urgency=medium
* rebuild with proxmox-schema 4.0
-- Proxmox Support Team <support@proxmox.com> Wed, 15 Jan 2025 12:27:29 +0100
rust-proxmox-acme (0.5.3) bookworm; urgency=medium
* detect base64 vs base64url encoded eab hmac key
-- Proxmox Support Team <support@proxmox.com> Thu, 03 Oct 2024 09:52:20 +0200
rust-proxmox-acme (0.5.2) bookworm; urgency=medium
* allow to compile/use api types separately.
* add async-client feature
-- Proxmox Support Team <support@proxmox.com> Thu, 16 May 2024 11:31:43 +0200
rust-proxmox-acme (0.5.1) bookworm; urgency=medium
* add api-types feature to provide schemas for api types
* derive PartialEq for api types for integration in rust based ui code
-- Proxmox Support Team <support@proxmox.com> Thu, 07 Mar 2024 13:27:08 +0100
rust-proxmox-acme (0.5.0) bookworm; urgency=medium
* add external account binding support
* add a few more standard fields to Meta
* update deprecated openssl calls
* documentation fixups
* general code improvements and cleanups
-- Proxmox Support Team <support@proxmox.com> Mon, 04 Dec 2023 11:46:26 +0100
rust-proxmox-acme-rs (0.4.0) pve; urgency=medium
* switch from curl to ureq with native-tls
* bump edition to 2021
-- Proxmox Support Team <support@proxmox.com> Tue, 01 Feb 2022 10:19:29 +0100
rust-proxmox-acme-rs (0.3.2) pve; urgency=medium
* rebuild with base64 0.13
-- Proxmox Support Team <support@proxmox.com> Thu, 18 Nov 2021 12:49:25 +0100
rust-proxmox-acme-rs (0.3.1) pve; urgency=medium
* add proxy support
-- Proxmox Support Team <support@proxmox.com> Thu, 18 Nov 2021 09:46:34 +0100
rust-proxmox-acme-rs (0.3.0) pve; urgency=medium
* directory: make metadata optional
-- Proxmox Support Team <support@proxmox.com> Thu, 21 Oct 2021 13:10:27 +0200
rust-proxmox-acme-rs (0.2.2-1) pve; urgency=medium
* improve crate documentation
* mark `Error` as 'must_use'
* make status types `Copy`
* add Client::directory_url() to get the URL without querying the whole
directory
-- Proxmox Support Team <support@proxmox.com> Fri, 07 May 2021 13:53:08 +0200
rust-proxmox-acme-rs (0.2.1-1) pve; urgency=medium
* make revocation workflow accessible without client
-- Proxmox Support Team <support@proxmox.com> Wed, 14 Apr 2021 14:56:49 +0200
rust-proxmox-acme-rs (0.2.0-1) pve; urgency=medium
* add 'status' and 'url' as fixed members to `Challenge`
* expose some workflow helpers in a more consistentw ay
* add `util::Csr` for CSR generation
-- Proxmox Support Team <support@proxmox.com> Mon, 12 Apr 2021 13:06:19 +0200
rust-proxmox-acme-rs (0.1.4-1) pve; urgency=medium
* collect extra account fields (such as 'created' from let's encrypt)
in the AccountData struct
-- Proxmox Support Team <support@proxmox.com> Wed, 17 Mar 2021 15:28:09 +0100
rust-proxmox-acme-rs (0.1.3-1) pve; urgency=medium
* fix padding in ecdsa signatures
-- Proxmox Support Team <support@proxmox.com> Wed, 17 Mar 2021 13:34:10 +0100
rust-proxmox-acme-rs (0.1.2-1) pve; urgency=medium
* include Content-length header in requests
-- Proxmox Support Team <support@proxmox.com> Fri, 12 Mar 2021 15:43:01 +0100
rust-proxmox-acme-rs (0.1.1-1) pve; urgency=medium
* make AccountData fields public
-- Proxmox Support Team <support@proxmox.com> Tue, 09 Mar 2021 13:22:55 +0100
rust-proxmox-acme-rs (0.1.0-1) pve; urgency=medium
* initial release
-- Proxmox Support Team <support@proxmox.com> Tue, 09 Mar 2021 13:01:56 +0100

123
proxmox-acme/debian/control Normal file
View File

@ -0,0 +1,123 @@
Source: rust-proxmox-acme
Section: rust
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-sequence-cargo
Build-Depends-Arch: cargo:native <!nocheck>,
rustc:native <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-openssl-0.10+default-dev <!nocheck>,
librust-proxmox-base64-1+default-dev <!nocheck>,
librust-proxmox-base64-1+serde-dev <!nocheck>,
librust-proxmox-schema-4+api-macro-dev (>= 4.1.0-~~) <!nocheck>,
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~) <!nocheck>,
librust-serde-1+default-dev <!nocheck>,
librust-serde-1+derive-dev <!nocheck>,
librust-serde-json-1+default-dev <!nocheck>
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.7.0
Vcs-Git:
Vcs-Browser:
Homepage: https://proxmox.com
X-Cargo-Crate: proxmox-acme
Rules-Requires-Root: no
Package: librust-proxmox-acme-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-base64-1+default-dev,
librust-proxmox-base64-1+serde-dev,
librust-serde-1+default-dev,
librust-serde-1+derive-dev,
librust-serde-json-1+default-dev
Recommends:
librust-proxmox-acme+impl-dev (= ${binary:Version})
Suggests:
librust-proxmox-acme+api-types-dev (= ${binary:Version}),
librust-proxmox-acme+async-client-dev (= ${binary:Version}),
librust-proxmox-acme+client-dev (= ${binary:Version})
Provides:
librust-proxmox-acme-1-dev (= ${binary:Version}),
librust-proxmox-acme-1.0-dev (= ${binary:Version}),
librust-proxmox-acme-1.0.0-dev (= ${binary:Version})
Description: ACME client library - Rust source code
Source code for Debianized Rust crate "proxmox-acme"
Package: librust-proxmox-acme+api-types-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-acme-dev (= ${binary:Version}),
librust-proxmox-schema-4+api-macro-dev (>= 4.1.0-~~),
librust-proxmox-schema-4+default-dev (>= 4.1.0-~~)
Provides:
librust-proxmox-acme-1+api-types-dev (= ${binary:Version}),
librust-proxmox-acme-1.0+api-types-dev (= ${binary:Version}),
librust-proxmox-acme-1.0.0+api-types-dev (= ${binary:Version})
Description: ACME client library - feature "api-types"
This metapackage enables feature "api-types" for the Rust proxmox-acme crate,
by pulling in any additional dependencies needed by that feature.
Package: librust-proxmox-acme+async-client-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-acme-dev (= ${binary:Version}),
librust-proxmox-acme+impl-dev (= ${binary:Version}),
librust-anyhow-1+default-dev,
librust-bytes-1+default-dev,
librust-http-body-util-0.1+default-dev,
librust-hyper-1+default-dev,
librust-proxmox-http-1+client-dev,
librust-proxmox-http-1+default-dev
Provides:
librust-proxmox-acme-1+async-client-dev (= ${binary:Version}),
librust-proxmox-acme-1.0+async-client-dev (= ${binary:Version}),
librust-proxmox-acme-1.0.0+async-client-dev (= ${binary:Version})
Description: ACME client library - feature "async-client"
This metapackage enables feature "async-client" for the Rust proxmox-acme
crate, by pulling in any additional dependencies needed by that feature.
Package: librust-proxmox-acme+client-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-acme-dev (= ${binary:Version}),
librust-proxmox-acme+impl-dev (= ${binary:Version}),
librust-http-1+default-dev,
librust-ureq-3+gzip-dev,
librust-ureq-3+native-tls-dev
Provides:
librust-proxmox-acme-1+client-dev (= ${binary:Version}),
librust-proxmox-acme-1.0+client-dev (= ${binary:Version}),
librust-proxmox-acme-1.0.0+client-dev (= ${binary:Version})
Description: ACME client library - feature "client"
This metapackage enables feature "client" for the Rust proxmox-acme crate, by
pulling in any additional dependencies needed by that feature.
Package: librust-proxmox-acme+impl-dev
Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
librust-proxmox-acme-dev (= ${binary:Version}),
librust-proxmox-acme+api-types-dev (= ${binary:Version}),
librust-openssl-0.10+default-dev
Provides:
librust-proxmox-acme+default-dev (= ${binary:Version}),
librust-proxmox-acme-1+impl-dev (= ${binary:Version}),
librust-proxmox-acme-1+default-dev (= ${binary:Version}),
librust-proxmox-acme-1.0+impl-dev (= ${binary:Version}),
librust-proxmox-acme-1.0+default-dev (= ${binary:Version}),
librust-proxmox-acme-1.0.0+impl-dev (= ${binary:Version}),
librust-proxmox-acme-1.0.0+default-dev (= ${binary:Version})
Description: ACME client library - feature "impl" and 1 more
This metapackage enables feature "impl" for the Rust proxmox-acme crate, by
pulling in any additional dependencies needed by that feature.
.
Additionally, this package also provides the "default" feature.

View File

@ -0,0 +1,16 @@
Copyright (C) 2020-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]
# TODO: update once public
vcs_git = ""
vcs_browser = ""

View File

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

425
proxmox-acme/src/account.rs Normal file
View File

@ -0,0 +1,425 @@
//! ACME Account management and creation. The [`Account`] type also contains most of the ACME API
//! entry point helpers.
use std::collections::HashMap;
use std::convert::TryFrom;
use openssl::pkey::{PKey, Private};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::authorization::{Authorization, GetAuthorization};
use crate::b64u;
use crate::directory::Directory;
use crate::jws::Jws;
use crate::key::{Jwk, PublicKey};
use crate::order::{NewOrder, Order, OrderData};
use crate::request::Request;
use crate::types::{AccountData, AccountStatus, ExternalAccountBinding};
use crate::Error;
/// An ACME Account.
///
/// This contains the location URL, the account data and the private key for an account.
/// This can directly be serialized via serde to persist the account.
///
/// In order to register a new account with an ACME provider, see the [`Account::creator`] method.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Account {
/// Account location URL.
pub location: String,
/// Acme account data.
pub data: AccountData,
/// base64url encoded PEM formatted private key.
pub private_key: String,
}
impl Account {
/// Rebuild an account from its components.
pub fn from_parts(location: String, private_key: String, data: AccountData) -> Self {
Self {
location,
data,
private_key,
}
}
/// Builds an [`AccountCreator`]. This handles creation of the private key and account data as
/// well as handling the response sent by the server for the registration request.
pub fn creator() -> AccountCreator {
AccountCreator::default()
}
/// Place a new order. This will build a [`NewOrder`] representing an in flight order creation
/// request.
///
/// The returned `NewOrder`'s `request` option is *guaranteed* to be `Some(Request)`.
pub fn new_order(
&self,
order: &OrderData,
directory: &Directory,
nonce: &str,
) -> Result<NewOrder, Error> {
let key = PKey::private_key_from_pem(self.private_key.as_bytes())?;
if order.identifiers.is_empty() {
return Err(Error::EmptyOrder);
}
let url = directory.new_order_url();
let body = serde_json::to_string(&Jws::new(
&key,
Some(self.location.clone()),
url.to_owned(),
nonce.to_owned(),
order,
)?)?;
let request = Request {
url: url.to_owned(),
method: "POST",
content_type: crate::request::JSON_CONTENT_TYPE,
body,
expected: crate::request::CREATED,
};
Ok(NewOrder::new(request))
}
/// Prepare a "POST-as-GET" request to fetch data. Low level helper.
pub fn get_request(&self, url: &str, nonce: &str) -> Result<Request, Error> {
let key = PKey::private_key_from_pem(self.private_key.as_bytes())?;
let body = serde_json::to_string(&Jws::new_full(
&key,
Some(self.location.clone()),
url.to_owned(),
nonce.to_owned(),
String::new(),
)?)?;
Ok(Request {
url: url.to_owned(),
method: "POST",
content_type: crate::request::JSON_CONTENT_TYPE,
body,
expected: 200,
})
}
/// Prepare a JSON POST request. Low level helper.
pub fn post_request<T: Serialize>(
&self,
url: &str,
nonce: &str,
data: &T,
) -> Result<Request, Error> {
let key = PKey::private_key_from_pem(self.private_key.as_bytes())?;
let body = serde_json::to_string(&Jws::new(
&key,
Some(self.location.clone()),
url.to_owned(),
nonce.to_owned(),
data,
)?)?;
Ok(Request {
url: url.to_owned(),
method: "POST",
content_type: crate::request::JSON_CONTENT_TYPE,
body,
expected: 200,
})
}
/// Prepare a JSON POST request.
fn post_request_raw_payload(
&self,
url: &str,
nonce: &str,
payload: String,
) -> Result<Request, Error> {
let key = PKey::private_key_from_pem(self.private_key.as_bytes())?;
let body = serde_json::to_string(&Jws::new_full(
&key,
Some(self.location.clone()),
url.to_owned(),
nonce.to_owned(),
payload,
)?)?;
Ok(Request {
url: url.to_owned(),
method: "POST",
content_type: crate::request::JSON_CONTENT_TYPE,
body,
expected: 200,
})
}
/// Get the "key authorization" for a token.
pub fn key_authorization(&self, token: &str) -> Result<String, Error> {
let key = PKey::private_key_from_pem(self.private_key.as_bytes())?;
let thumbprint = PublicKey::try_from(&*key)?.thumbprint()?;
Ok(format!("{}.{}", token, thumbprint))
}
/// Get the TXT field value for a dns-01 token. This is the base64url encoded sha256 digest of
/// the key authorization value.
pub fn dns_01_txt_value(&self, token: &str) -> Result<String, Error> {
let key_authorization = self.key_authorization(token)?;
let digest = openssl::sha::sha256(key_authorization.as_bytes());
Ok(b64u::encode(&digest))
}
/// Prepare a request to update account data.
///
/// This is a rather low level interface. You should know what you're doing.
pub fn update_account_request<T: Serialize>(
&self,
nonce: &str,
data: &T,
) -> Result<Request, Error> {
self.post_request(&self.location, nonce, data)
}
/// Prepare a request to deactivate this account.
pub fn deactivate_account_request<T: Serialize>(&self, nonce: &str) -> Result<Request, Error> {
self.post_request_raw_payload(
&self.location,
nonce,
r#"{"status":"deactivated"}"#.to_string(),
)
}
/// Prepare a request to query an Authorization for an Order.
///
/// Returns `Ok(None)` if `auth_index` is out of out of range. You can query the number of
/// authorizations from via [`Order::authorization_len`] or by manually inspecting its
/// `.data.authorization` vector.
pub fn get_authorization(
&self,
order: &Order,
auth_index: usize,
nonce: &str,
) -> Result<Option<GetAuthorization>, Error> {
match order.authorization(auth_index) {
None => Ok(None),
Some(url) => Ok(Some(GetAuthorization::new(self.get_request(url, nonce)?))),
}
}
/// Prepare a request to validate a Challenge from an Authorization.
///
/// Returns `Ok(None)` if `challenge_index` is out of out of range. The challenge count is
/// available by inspecting the [`Authorization::challenges`] vector.
///
/// This returns a raw `Request` since validation takes some time and the `Authorization`
/// object has to be re-queried and its `status` inspected.
pub fn validate_challenge(
&self,
authorization: &Authorization,
challenge_index: usize,
nonce: &str,
) -> Result<Option<Request>, Error> {
match authorization.challenges.get(challenge_index) {
None => Ok(None),
Some(challenge) => self
.post_request_raw_payload(&challenge.url, nonce, "{}".to_string())
.map(Some),
}
}
/// Prepare a request to revoke a certificate.
///
/// The certificate can be either PEM or DER formatted.
///
/// Note that this uses the account's key for authorization.
///
/// Revocation using a certificate's private key is not yet implemented.
pub fn revoke_certificate(
&self,
certificate: &[u8],
reason: Option<u32>,
) -> Result<CertificateRevocation, Error> {
let cert = if certificate.starts_with(b"-----BEGIN CERTIFICATE-----") {
b64u::encode(&openssl::x509::X509::from_pem(certificate)?.to_der()?)
} else {
b64u::encode(certificate)
};
let data = match reason {
Some(reason) => serde_json::json!({ "certificate": cert, "reason": reason }),
None => serde_json::json!({ "certificate": cert }),
};
Ok(CertificateRevocation {
account: self,
data,
})
}
}
/// Certificate revocation involves converting the certificate to base64url encoded DER and then
/// embedding it in a json structure. Since we also need a nonce and possibly retry the request if
/// a `BadNonce` error happens, this caches the converted data for efficiency.
pub struct CertificateRevocation<'a> {
account: &'a Account,
data: Value,
}
impl CertificateRevocation<'_> {
/// Create the revocation request using the specified nonce for the given directory.
pub fn request(&self, directory: &Directory, nonce: &str) -> Result<Request, Error> {
self.account
.post_request(&directory.data.revoke_cert, nonce, &self.data)
}
}
/// Helper to create an account.
///
/// This is used to generate a private key and set the contact info for the account. Afterwards the
/// creation request can be created via the [`request`](AccountCreator::request()) method, giving
/// it a nonce and a directory. This can be repeated, if necessary, like when the nonce fails.
///
/// When the server sends a successful response, it should be passed to the
/// [`response`](AccountCreator::response()) method to finish the creation of an [`Account`] which
/// can then be persisted.
#[derive(Default)]
#[must_use = "when creating an account you must pass the response to AccountCreator::response()!"]
pub struct AccountCreator {
contact: Vec<String>,
terms_of_service_agreed: bool,
key: Option<PKey<Private>>,
eab_credentials: Option<(String, PKey<Private>)>,
}
impl AccountCreator {
/// Replace the contact info with the provided ACME compatible data.
pub fn set_contacts(mut self, contact: Vec<String>) -> Self {
self.contact = contact;
self
}
/// Append a contact string.
pub fn contact(mut self, contact: String) -> Self {
self.contact.push(contact);
self
}
/// Append an email address to the contact list.
pub fn email(self, email: String) -> Self {
self.contact(format!("mailto:{}", email))
}
/// Change whether the account agrees to the terms of service. Use the directory's or client's
/// `terms_of_service_url()` method to present the user with the Terms of Service.
pub fn agree_to_tos(mut self, agree: bool) -> Self {
self.terms_of_service_agreed = agree;
self
}
/// Set the EAB credentials for the account registration
pub fn set_eab_credentials(mut self, kid: String, hmac_key: String) -> Result<Self, Error> {
let hmac_key = if hmac_key.contains('+') || hmac_key.contains('/') {
proxmox_base64::decode(hmac_key)?
} else {
b64u::decode(&hmac_key)?
};
let hmac_key = PKey::hmac(&hmac_key)?;
self.eab_credentials = Some((kid, hmac_key));
Ok(self)
}
/// Generate a new RSA key of the specified key size.
pub fn generate_rsa_key(self, bits: u32) -> Result<Self, Error> {
let key = openssl::rsa::Rsa::generate(bits)?;
Ok(self.with_key(PKey::from_rsa(key)?))
}
/// Generate a new P-256 EC key.
pub fn generate_ec_key(self) -> Result<Self, Error> {
let key = openssl::ec::EcKey::generate(
openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1)?.as_ref(),
)?;
Ok(self.with_key(PKey::from_ec_key(key)?))
}
/// Use an existing key. Note that only RSA and EC keys using the `P-256` curve are currently
/// supported, however, this will not be checked at this point.
pub fn with_key(mut self, key: PKey<Private>) -> Self {
self.key = Some(key);
self
}
/// Prepare a HTTP request to create this account.
///
/// Changes to the user data made after this will have no effect on the account generated with
/// the resulting request.
/// Changing the private key between using the request and passing the response to
/// [`response`](AccountCreator::response()) will render the account unusable!
pub fn request(&self, directory: &Directory, nonce: &str) -> Result<Request, Error> {
let key = self.key.as_deref().ok_or(Error::MissingKey)?;
let url = directory.new_account_url();
let external_account_binding = self
.eab_credentials
.as_ref()
.map(|cred| {
ExternalAccountBinding::new(&cred.0, &cred.1, Jwk::try_from(key)?, url.to_string())
})
.transpose()?;
let data = AccountData {
orders: None,
status: AccountStatus::New,
contact: self.contact.clone(),
terms_of_service_agreed: if self.terms_of_service_agreed {
Some(true)
} else {
None
},
external_account_binding,
only_return_existing: false,
extra: HashMap::new(),
};
let body = serde_json::to_string(&Jws::new(
key,
None,
url.to_owned(),
nonce.to_owned(),
&data,
)?)?;
Ok(Request {
url: url.to_owned(),
method: "POST",
content_type: crate::request::JSON_CONTENT_TYPE,
body,
expected: crate::request::CREATED,
})
}
/// After issuing the request from [`request()`](AccountCreator::request()), the response's
/// `Location` header and body must be passed to this for verification and to create an account
/// which is to be persisted!
pub fn response(self, location_header: String, response_body: &[u8]) -> Result<Account, Error> {
let private_key = self
.key
.ok_or(Error::MissingKey)?
.private_key_to_pem_pkcs8()?;
let private_key = String::from_utf8(private_key).map_err(|_| {
Error::Custom("PEM key contained illegal non-utf-8 characters".to_string())
})?;
Ok(Account {
location: location_header,
data: serde_json::from_slice(response_body)
.map_err(|err| Error::BadAccountData(err.to_string()))?,
private_key,
})
}
}

View File

@ -0,0 +1,590 @@
//! Async HTTP Client implementation for the ACME protocol.
use anyhow::format_err;
use bytes::Bytes;
use http_body_util::BodyExt;
use hyper::Request;
use serde::{Deserialize, Serialize};
use proxmox_http::{client::Client, Body};
use crate::account::AccountCreator;
use crate::order::{Order, OrderData};
use crate::Request as AcmeRequest;
use crate::{Account, Authorization, Challenge, Directory, Error, ErrorResponse};
/// A non-blocking Acme client using tokio/hyper.
pub struct AcmeClient {
directory_url: String,
account: Option<Account>,
directory: Option<Directory>,
nonce: Option<String>,
http_client: Client,
}
impl AcmeClient {
/// Create a new ACME client for a given ACME directory URL.
pub fn new(directory_url: String) -> Self {
const USER_AGENT_STRING: &str = "proxmox-acme-client/1.0";
const TCP_KEEPALIVE_TIME: u32 = 120;
let options = proxmox_http::HttpOptions {
proxy_config: None, // fixme???
user_agent: Some(USER_AGENT_STRING.to_string()),
tcp_keepalive: Some(TCP_KEEPALIVE_TIME),
};
let http_client = Client::with_options(options);
Self {
directory_url,
account: None,
directory: None,
nonce: None,
http_client,
}
}
/// Get the current account, if there is one.
pub fn account(&self) -> Option<&Account> {
self.account.as_ref()
}
/// Set the account this client should use.
pub fn set_account(&mut self, account: Account) {
self.account = Some(account);
}
/// Convenience method to create a new account with a list of ACME compatible contact strings
/// (eg. `mailto:someone@example.com`).
///
/// Please remember to persist the returned `Account` structure somewhere to not lose access to
/// the account!
///
/// If an RSA key size is provided, an RSA key will be generated. Otherwise an EC key using the
/// P-256 curve will be generated.
pub async fn new_account(
&mut self,
tos_agreed: bool,
contact: Vec<String>,
rsa_bits: Option<u32>,
eab_creds: Option<(String, String)>,
) -> Result<&Account, anyhow::Error> {
let mut account = Account::creator()
.set_contacts(contact)
.agree_to_tos(tos_agreed);
if let Some((eab_kid, eab_hmac_key)) = eab_creds {
account = account.set_eab_credentials(eab_kid, eab_hmac_key)?;
}
let account = if let Some(bits) = rsa_bits {
account.generate_rsa_key(bits)?
} else {
account.generate_ec_key()?
};
let _ = self.register_account(account).await?;
// unwrap: Setting `self.account` is literally this function's job, we just can't keep
// the borrow from from `self.register_account()` active due to clashes.
Ok(self.account.as_ref().unwrap())
}
/// Shortcut to `account().ok_or_else(...).key_authorization()`.
pub fn key_authorization(&self, token: &str) -> Result<String, anyhow::Error> {
Ok(Self::need_account(&self.account)?.key_authorization(token)?)
}
/// Shortcut to `account().ok_or_else(...).dns_01_txt_value()`.
/// the key authorization value.
pub fn dns_01_txt_value(&self, token: &str) -> Result<String, anyhow::Error> {
Ok(Self::need_account(&self.account)?.dns_01_txt_value(token)?)
}
async fn register_account(
&mut self,
account: AccountCreator,
) -> Result<&Account, anyhow::Error> {
let mut retry = retry();
let mut response = loop {
retry.tick()?;
let (directory, nonce) = Self::get_dir_nonce(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?;
let request = account.request(directory, nonce)?;
match self.run_request(request).await {
Ok(response) => break response,
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err.into()),
}
};
let account = account.response(response.location_required()?, &response.body)?;
self.account = Some(account);
Ok(self.account.as_ref().unwrap())
}
/// Update account data.
///
/// Low-level version: we allow arbitrary data to be passed to the remote here, it's up to the
/// user to know what to do for now.
pub async fn update_account<T: Serialize>(
&mut self,
data: &T,
) -> Result<&Account, anyhow::Error> {
let account = Self::need_account(&self.account)?;
let mut retry = retry();
let response = loop {
retry.tick()?;
let (_directory, nonce) = Self::get_dir_nonce(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?;
let request = account.post_request(&account.location, nonce, data)?;
match Self::execute(&mut self.http_client, request, &mut self.nonce).await {
Ok(response) => break response,
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err.into()),
}
};
// unwrap: we've been keeping an immutable reference to it from the top of the method
let _ = account;
self.account.as_mut().unwrap().data = response.json()?;
// fixme: self.save()?;
Ok(self.account.as_ref().unwrap())
}
/// Method to create a new order for a set of domains.
///
/// Please remember to persist the order somewhere (ideally along with the account data) in
/// order to finish & query it later on.
pub async fn new_order<I>(&mut self, domains: I) -> Result<Order, anyhow::Error>
where
I: IntoIterator<Item = String>,
{
let account = Self::need_account(&self.account)?;
let order = domains
.into_iter()
.fold(OrderData::new(), |order, domain| order.domain(domain));
let mut retry = retry();
loop {
retry.tick()?;
let (directory, nonce) = Self::get_dir_nonce(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?;
let mut new_order = account.new_order(&order, directory, nonce)?;
let mut response = match Self::execute(
&mut self.http_client,
new_order.request.take().unwrap(),
&mut self.nonce,
)
.await
{
Ok(response) => response,
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err.into()),
};
return Ok(
new_order.response(response.location_required()?, response.bytes().as_ref())?
);
}
}
/// Low level "POST-as-GET" request.
async fn post_as_get(&mut self, url: &str) -> Result<AcmeResponse, anyhow::Error> {
let account = Self::need_account(&self.account)?;
let mut retry = retry();
loop {
retry.tick()?;
let (_directory, nonce) = Self::get_dir_nonce(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?;
let request = account.get_request(url, nonce)?;
match Self::execute(&mut self.http_client, request, &mut self.nonce).await {
Ok(response) => return Ok(response),
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err.into()),
}
}
}
/// Low level POST request.
async fn post<T: Serialize>(
&mut self,
url: &str,
data: &T,
) -> Result<AcmeResponse, anyhow::Error> {
let account = Self::need_account(&self.account)?;
let mut retry = retry();
loop {
retry.tick()?;
let (_directory, nonce) = Self::get_dir_nonce(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?;
let request = account.post_request(url, nonce, data)?;
match Self::execute(&mut self.http_client, request, &mut self.nonce).await {
Ok(response) => return Ok(response),
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err.into()),
}
}
}
/// Request challenge validation. Afterwards, the challenge should be polled.
pub async fn request_challenge_validation(
&mut self,
url: &str,
) -> Result<Challenge, anyhow::Error> {
Ok(self
.post(url, &serde_json::Value::Object(Default::default()))
.await?
.json()?)
}
/// Assuming the provided URL is an 'Authorization' URL, get and deserialize it.
pub async fn get_authorization(&mut self, url: &str) -> Result<Authorization, anyhow::Error> {
Ok(self.post_as_get(url).await?.json()?)
}
/// Assuming the provided URL is an 'Order' URL, get and deserialize it.
pub async fn get_order(&mut self, url: &str) -> Result<OrderData, anyhow::Error> {
Ok(self.post_as_get(url).await?.json()?)
}
/// Finalize an Order via its `finalize` URL property and the DER encoded CSR.
pub async fn finalize(&mut self, url: &str, csr: &[u8]) -> Result<(), anyhow::Error> {
let csr = proxmox_base64::url::encode_no_pad(csr);
let data = serde_json::json!({ "csr": csr });
self.post(url, &data).await?;
Ok(())
}
/// Download a certificate via its 'certificate' URL property.
///
/// The certificate will be a PEM certificate chain.
pub async fn get_certificate(&mut self, url: &str) -> Result<Bytes, anyhow::Error> {
Ok(self.post_as_get(url).await?.body)
}
/// Revoke an existing certificate (PEM or DER formatted).
pub async fn revoke_certificate(
&mut self,
certificate: &[u8],
reason: Option<u32>,
) -> Result<(), anyhow::Error> {
// TODO: This can also work without an account.
let account = Self::need_account(&self.account)?;
let revocation = account.revoke_certificate(certificate, reason)?;
let mut retry = retry();
loop {
retry.tick()?;
let (directory, nonce) = Self::get_dir_nonce(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?;
let request = revocation.request(directory, nonce)?;
match Self::execute(&mut self.http_client, request, &mut self.nonce).await {
Ok(_response) => return Ok(()),
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err.into()),
}
}
}
fn need_account(account: &Option<Account>) -> Result<&Account, anyhow::Error> {
account
.as_ref()
.ok_or_else(|| format_err!("cannot use client without an account"))
}
/// Get the directory URL without querying the `Directory` structure.
///
/// The difference to [`directory`](AcmeClient::directory()) is that this does not
/// attempt to fetch the directory data from the ACME server.
pub fn directory_url(&self) -> &str {
&self.directory_url
}
}
struct AcmeResponse {
body: Bytes,
location: Option<String>,
got_nonce: bool,
}
impl AcmeResponse {
/// Convenience helper to assert that a location header was part of the response.
fn location_required(&mut self) -> Result<String, anyhow::Error> {
self.location
.take()
.ok_or_else(|| format_err!("missing Location header"))
}
/// Convenience shortcut to perform json deserialization of the returned body.
fn json<T: for<'a> Deserialize<'a>>(&self) -> Result<T, Error> {
Ok(serde_json::from_slice(&self.body)?)
}
/// Convenience shortcut to get the body as bytes.
fn bytes(&self) -> &[u8] {
&self.body
}
}
impl AcmeClient {
/// Non-self-borrowing run_request version for borrow workarounds.
async fn execute(
http_client: &mut Client,
request: AcmeRequest,
nonce: &mut Option<String>,
) -> Result<AcmeResponse, Error> {
let req_builder = Request::builder().method(request.method).uri(&request.url);
let http_request = if !request.content_type.is_empty() {
req_builder
.header("Content-Type", request.content_type)
.header("Content-Length", request.body.len())
.body(request.body.into())
} else {
req_builder.body(Body::empty())
}
.map_err(|err| Error::Custom(format!("failed to create http request: {}", err)))?;
let response = http_client
.request(http_request)
.await
.map_err(|err| Error::Custom(err.to_string()))?;
let (parts, body) = response.into_parts();
let status = parts.status.as_u16();
let body = body
.collect()
.await
.map_err(|err| Error::Custom(format!("failed to retrieve response body: {}", err)))?
.to_bytes();
let got_nonce = if let Some(new_nonce) = parts.headers.get(crate::REPLAY_NONCE) {
let new_nonce = new_nonce.to_str().map_err(|err| {
Error::Client(format!(
"received invalid replay-nonce header from ACME server: {}",
err
))
})?;
*nonce = Some(new_nonce.to_owned());
true
} else {
false
};
if parts.status.is_success() {
if status != request.expected {
return Err(Error::InvalidApi(format!(
"ACME server responded with unexpected status code: {:?}",
parts.status
)));
}
let location = parts
.headers
.get("Location")
.map(|header| {
header.to_str().map(str::to_owned).map_err(|err| {
Error::Client(format!(
"received invalid location header from ACME server: {}",
err
))
})
})
.transpose()?;
return Ok(AcmeResponse {
body,
location,
got_nonce,
});
}
let error: ErrorResponse = serde_json::from_slice(&body).map_err(|err| {
Error::Client(format!(
"error status with improper error ACME response: {}",
err
))
})?;
if error.ty == crate::error::BAD_NONCE {
if !got_nonce {
return Err(Error::InvalidApi(
"badNonce without a new Replay-Nonce header".to_string(),
));
}
return Err(Error::BadNonce);
}
Err(Error::Api(error))
}
/// Low-level API to run an n API request. This automatically updates the current nonce!
async fn run_request(&mut self, request: AcmeRequest) -> Result<AcmeResponse, Error> {
Self::execute(&mut self.http_client, request, &mut self.nonce).await
}
/// Get the Directory information.
pub async fn directory(&mut self) -> Result<&Directory, Error> {
Ok(Self::get_directory(
&mut self.http_client,
&self.directory_url,
&mut self.directory,
&mut self.nonce,
)
.await?
.0)
}
async fn get_directory<'a, 'b>(
http_client: &mut Client,
directory_url: &str,
directory: &'a mut Option<Directory>,
nonce: &'b mut Option<String>,
) -> Result<(&'a Directory, Option<&'b str>), Error> {
if let Some(d) = directory {
return Ok((d, nonce.as_deref()));
}
let response = Self::execute(
http_client,
AcmeRequest {
url: directory_url.to_string(),
method: "GET",
content_type: "",
body: String::new(),
expected: 200,
},
nonce,
)
.await?;
*directory = Some(Directory::from_parts(
directory_url.to_string(),
response.json()?,
));
Ok((directory.as_ref().unwrap(), nonce.as_deref()))
}
/// Like `get_directory`, but if the directory provides no nonce, also performs a `HEAD`
/// request on the new nonce URL.
async fn get_dir_nonce<'a, 'b>(
http_client: &mut Client,
directory_url: &str,
directory: &'a mut Option<Directory>,
nonce: &'b mut Option<String>,
) -> Result<(&'a Directory, &'b str), Error> {
// this let construct is a lifetime workaround:
let _ = Self::get_directory(http_client, directory_url, directory, nonce).await?;
let dir = directory.as_ref().unwrap(); // the above fails if it couldn't fill this option
if nonce.is_none() {
// this is also a lifetime issue...
let _ = Self::get_nonce(http_client, nonce, dir.new_nonce_url()).await?;
};
Ok((dir, nonce.as_deref().unwrap()))
}
/// Convenience method to get the ToS URL from the contained `Directory`.
///
/// This requires mutable self as the directory information may be lazily loaded, which can
/// fail.
pub async fn terms_of_service_url(&mut self) -> Result<Option<&str>, Error> {
Ok(self.directory().await?.terms_of_service_url())
}
async fn get_nonce<'a>(
http_client: &mut Client,
nonce: &'a mut Option<String>,
new_nonce_url: &str,
) -> Result<&'a str, Error> {
let response = Self::execute(
http_client,
AcmeRequest {
url: new_nonce_url.to_owned(),
method: "HEAD",
content_type: "",
body: String::new(),
expected: 200,
},
nonce,
)
.await?;
if !response.got_nonce {
return Err(Error::InvalidApi(
"no new nonce received from new nonce URL".to_string(),
));
}
nonce
.as_deref()
.ok_or_else(|| Error::Client("failed to update nonce".to_string()))
}
}
/// bad nonce retry count helper
struct Retry(usize);
const fn retry() -> Retry {
Retry(0)
}
impl Retry {
fn tick(&mut self) -> Result<(), Error> {
if self.0 >= 3 {
Err(Error::Client("kept getting a badNonce error!".to_string()))
} else {
self.0 += 1;
Ok(())
}
}
}

View File

@ -0,0 +1,162 @@
//! Authorization and Challenge data.
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::order::Identifier;
use crate::request::Request;
use crate::Error;
/// Status of an [`Authorization`].
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Status {
/// The authorization was deactivated by the client.
Deactivated,
/// The authorization expired.
Expired,
/// The authorization failed and is now invalid.
Invalid,
/// Validation is pending.
Pending,
/// The authorization was revoked by the server.
Revoked,
/// The identifier is authorized.
Valid,
}
impl Status {
/// Convenience method to check if the status is 'pending'.
#[inline]
pub fn is_pending(self) -> bool {
self == Status::Pending
}
/// Convenience method to check if the status is 'valid'.
#[inline]
pub fn is_valid(self) -> bool {
self == Status::Valid
}
}
/// Represents an authorization state for an order. The user is expected to pick a challenge,
/// execute it, and the request validation for it.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Authorization {
/// The identifier (usually domain name) this authorization is for.
pub identifier: Identifier,
/// The current status of this authorization entry.
pub status: Status,
/// Expiration date for the authorization.
#[serde(skip_serializing_if = "Option::is_none")]
pub expires: Option<String>,
/// List of challenges which can be used to complete this authorization.
pub challenges: Vec<Challenge>,
/// The authorization is for a wildcard domain.
#[serde(default, skip_serializing_if = "is_false")]
pub wildcard: bool,
}
/// The state of a challenge.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ChallengeStatus {
/// The challenge is pending and has not been validated yet.
Pending,
/// The validation is in progress.
Processing,
/// The challenge was successfully validated.
Valid,
/// Validation of this challenge failed.
Invalid,
}
impl ChallengeStatus {
/// Convenience method to check if the status is 'pending'.
#[inline]
pub fn is_pending(self) -> bool {
self == ChallengeStatus::Pending
}
/// Convenience method to check if the status is 'valid'.
#[inline]
pub fn is_valid(self) -> bool {
self == ChallengeStatus::Valid
}
}
/// A challenge object contains information on how to complete an authorization for an order.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Challenge {
/// The challenge type (such as `"dns-01"`).
#[serde(rename = "type")]
pub ty: String,
/// The current challenge status.
pub status: ChallengeStatus,
/// The URL used to post to in order to begin the validation for this challenge.
pub url: String,
/// Contains the remaining fields of the Challenge object, such as the `token`.
#[serde(flatten)]
pub data: HashMap<String, Value>,
}
impl Challenge {
/// Most challenges have a `token` used for key authorizations. This is a convenience helper to
/// access it.
pub fn token(&self) -> Option<&str> {
self.data.get("token").and_then(Value::as_str)
}
}
/// Serde helper
#[inline]
fn is_false(b: &bool) -> bool {
!*b
}
/// Represents an in-flight query for an authorization.
///
/// This is created via [`Account::get_authorization`](crate::Account::get_authorization()).
pub struct GetAuthorization {
//order: OrderData,
/// The request to send to the ACME provider. This is wrapped in an option in order to allow
/// moving it out instead of copying the contents.
///
/// When generated via [`Account::get_authorization`](crate::Account::get_authorization()),
/// this is guaranteed to be `Some`.
///
/// The response should be passed to the the [`response`](GetAuthorization::response()) method.
pub request: Option<Request>,
}
impl GetAuthorization {
pub(crate) fn new(request: Request) -> Self {
Self {
request: Some(request),
}
}
/// Deal with the response we got from the server.
pub fn response(self, response_body: &[u8]) -> Result<Authorization, Error> {
Ok(serde_json::from_slice(response_body)?)
}
}

2
proxmox-acme/src/b64u.rs Normal file
View File

@ -0,0 +1,2 @@
pub use proxmox_base64::url::as_base64 as bytes;
pub use proxmox_base64::url::{decode, encode};

634
proxmox-acme/src/client.rs Normal file
View File

@ -0,0 +1,634 @@
//! A blocking higher-level ACME client implementation using 'curl'.
use std::io::Read;
use serde::{Deserialize, Serialize};
use crate::b64u;
use crate::error;
use crate::order::OrderData;
use crate::request::ErrorResponse;
use crate::{Account, Authorization, Challenge, Directory, Error, Order, Request};
macro_rules! format_err {
($($fmt:tt)*) => { Error::Client(format!($($fmt)*)) };
}
macro_rules! bail {
($($fmt:tt)*) => {{ return Err(format_err!($($fmt)*)); }}
}
/// Low level HTTP response structure.
pub struct HttpResponse {
/// The raw HTTP response body as a byte vector.
pub body: Vec<u8>,
/// The http status code.
pub status: u16,
/// The headers relevant to the ACME protocol.
pub headers: Headers,
}
impl HttpResponse {
/// Check the HTTP status code for a success code (200..299).
pub fn is_success(&self) -> bool {
self.status >= 200 && self.status < 300
}
/// Convenience shortcut to perform json deserialization of the returned body.
pub fn json<T: for<'a> Deserialize<'a>>(&self) -> Result<T, Error> {
Ok(serde_json::from_slice(&self.body)?)
}
/// Access the raw body as bytes.
pub fn bytes(&self) -> &[u8] {
&self.body
}
/// Get the returned location header. Borrowing shortcut to `self.headers.location`.
pub fn location(&self) -> Option<&str> {
self.headers.location.as_deref()
}
/// Convenience helper to assert that a location header was part of the response.
pub fn location_required(&mut self) -> Result<String, Error> {
self.headers
.location
.take()
.ok_or_else(|| format_err!("missing Location header"))
}
}
/// Contains headers from the HTTP response which are relevant parts of the Acme API.
///
/// Note that access to the `nonce` header is internal to this crate only, since a nonce will
/// always be moved out of the response into the `Client` whenever a new nonce is received.
#[derive(Default)]
pub struct Headers {
/// The 'Location' header usually encodes the URL where an account or order can be queried from
/// after they were created.
pub location: Option<String>,
nonce: Option<String>,
}
struct Inner {
agent: Option<ureq::Agent>,
nonce: Option<String>,
proxy: Option<String>,
}
impl Inner {
fn agent(&mut self) -> Result<&mut ureq::Agent, Error> {
if self.agent.is_none() {
let mut builder = ureq::Agent::config_builder()
.tls_config(
ureq::tls::TlsConfig::builder()
.provider(ureq::tls::TlsProvider::NativeTls)
.root_certs(ureq::tls::RootCerts::PlatformVerifier)
.build(),
)
.user_agent(concat!(
"proxmox-acme-sync-client/",
env!("CARGO_PKG_VERSION")
));
if let Some(proxy) = self.proxy.as_deref() {
builder = builder.proxy(Some(
ureq::Proxy::new(proxy)
.map_err(|err| format_err!("failed to set proxy: {}", err))?,
));
}
self.agent = Some(builder.build().into());
}
Ok(self.agent.as_mut().unwrap())
}
fn new() -> Self {
Self {
agent: None,
nonce: None,
proxy: None,
}
}
fn execute(
&mut self,
method: &[u8],
url: &str,
request_body: Option<(&str, &[u8])>, // content-type and body
) -> Result<HttpResponse, Error> {
let agent = self.agent()?;
let req = match method {
b"POST" => http::Request::post(url),
b"GET" => http::Request::get(url),
b"HEAD" => http::Request::head(url),
other => bail!("invalid http method: {:?}", other),
};
let response = if let Some((content_type, body)) = request_body {
agent.run(
req.header("Content-Type", content_type)
.body(body)
.map_err(|err| format_err!("error building http request: {err:#}"))?,
)
} else {
agent.run(
req.body(ureq::SendBody::none())
.map_err(|err| format_err!("error building http request: {err:#}"))?,
)
}
.map_err(|err| format_err!("http request failed: {err:#}"))?;
let mut headers = Headers::default();
if let Some(value) = response.headers().get(crate::LOCATION) {
headers.location = Some(
value
.to_str()
.map_err(|_| format_err!("unexpected binary data in location header"))?
.to_owned(),
);
}
if let Some(value) = response.headers().get(crate::REPLAY_NONCE) {
headers.nonce = Some(
value
.to_str()
.map_err(|_| format_err!("unexpected binary data in nonce header"))?
.to_owned(),
);
}
let status = response.status();
let mut body = Vec::new();
response
.into_body()
.into_reader()
.take(16 * 1024 * 1024) // arbitrary limit
.read_to_end(&mut body)
.map_err(|err| format_err!("failed to read response body: {err:#}"))?;
Ok(HttpResponse {
status: status.into(),
headers,
body,
})
}
pub fn set_proxy(&mut self, proxy: String) {
self.proxy = Some(proxy);
self.agent = None;
}
/// Low-level API to run an API request. This automatically updates the current nonce!
fn run_request(&mut self, request: Request) -> Result<HttpResponse, Error> {
let body = if request.body.is_empty() {
None
} else {
Some((request.content_type, request.body.as_bytes()))
};
let mut response = self
.execute(request.method.as_bytes(), &request.url, body)
.map_err({
// borrow fixup:
let method = &request.method;
let url = &request.url;
move |err| format_err!("failed to execute {} request to {}: {}", method, url, err)
})?;
let got_nonce = self.update_nonce(&mut response)?;
if response.is_success() {
if response.status != request.expected {
return Err(Error::InvalidApi(format!(
"API server responded with unexpected status code: {:?}",
response.status
)));
}
return Ok(response);
}
let error: ErrorResponse = response.json().map_err(|err| {
format_err!("error status with improper error ACME response: {}", err)
})?;
if error.ty == error::BAD_NONCE {
if !got_nonce {
return Err(Error::InvalidApi(
"badNonce without a new Replay-Nonce header".to_string(),
));
}
return Err(Error::BadNonce);
}
Err(Error::Api(error))
}
/// If the response contained a nonce, update our nonce and return `true`, otherwise return
/// `false`.
fn update_nonce(&mut self, response: &mut HttpResponse) -> Result<bool, Error> {
match response.headers.nonce.take() {
Some(nonce) => {
self.nonce = Some(nonce);
Ok(true)
}
None => Ok(false),
}
}
/// Update the nonce, if there isn't one it is an error.
fn must_update_nonce(&mut self, response: &mut HttpResponse) -> Result<(), Error> {
if !self.update_nonce(response)? {
bail!("newNonce URL did not return a nonce");
}
Ok(())
}
/// Update the Nonce.
fn new_nonce(&mut self, new_nonce_url: &str) -> Result<(), Error> {
let mut response = self.execute(b"HEAD", new_nonce_url, None).map_err(|err| {
Error::InvalidApi(format!("failed to get HEAD of newNonce URL: {}", err))
})?;
if !response.is_success() {
bail!("HEAD on newNonce URL returned error");
}
self.must_update_nonce(&mut response)?;
Ok(())
}
/// Make sure a nonce is available without forcing renewal.
fn nonce(&mut self, new_nonce_url: &str) -> Result<&str, Error> {
if self.nonce.is_none() {
self.new_nonce(new_nonce_url)?;
}
self.nonce
.as_deref()
.ok_or_else(|| format_err!("failed to get nonce"))
}
}
/// A blocking Acme client using curl's `Easy` interface.
pub struct Client {
inner: Inner,
directory: Option<Directory>,
account: Option<Account>,
directory_url: String,
}
impl Client {
/// Create a new Client. This has no account associated with it yet, so the next step is to
/// either attach an existing `Account` or create a new one.
pub fn new(directory_url: String) -> Self {
Self {
inner: Inner::new(),
directory: None,
account: None,
directory_url,
}
}
/// Get the directory URL without querying the `Directory` structure.
///
/// The difference to [`directory`](Client::directory()) is that this does not
/// attempt to fetch the directory data from the ACME server.
pub fn directory_url(&self) -> &str {
&self.directory_url
}
/// Set the account this client should use.
pub fn set_account(&mut self, account: Account) {
self.account = Some(account);
}
/// Get the Directory information.
pub fn directory(&mut self) -> Result<&Directory, Error> {
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)
}
/// Get the Directory information.
fn get_directory<'a>(
inner: &'_ mut Inner,
directory: &'a mut Option<Directory>,
directory_url: &str,
) -> Result<&'a Directory, Error> {
if let Some(d) = directory {
return Ok(d);
}
let response = inner
.execute(b"GET", directory_url, None)
.map_err(|err| Error::InvalidApi(format!("failed to get directory info: {}", err)))?;
if !response.is_success() {
bail!(
"GET on the directory URL returned error status ({})",
response.status
);
}
*directory = Some(Directory::from_parts(
directory_url.to_string(),
response.json()?,
));
Ok(directory.as_ref().unwrap())
}
/// Get the current account, if there is one.
pub fn account(&self) -> Option<&Account> {
self.account.as_ref()
}
/// Convenience method to get the ToS URL from the contained `Directory`.
///
/// This requires mutable self as the directory information may be lazily loaded, which can
/// fail.
pub fn terms_of_service_url(&mut self) -> Result<Option<&str>, Error> {
Ok(self.directory()?.terms_of_service_url())
}
/// Get a fresh nonce (this should normally not be required as nonces are updated
/// automatically, even when a `badNonce` error occurs, which according to the ACME API
/// specification should include a new valid nonce in its headers anyway).
pub fn new_nonce(&mut self) -> Result<(), Error> {
let was_none = self.inner.nonce.is_none();
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
if was_none && self.inner.nonce.is_some() {
// this was the first call and we already got a nonce from querying the directory
return Ok(());
}
// otherwise actually call up to get a new nonce
self.inner.new_nonce(directory.new_nonce_url())
}
/// borrow helper
fn nonce<'a>(inner: &'a mut Inner, directory: &'_ Directory) -> Result<&'a str, Error> {
inner.nonce(directory.new_nonce_url())
}
/// Convenience method to create a new account with a list of ACME compatible contact strings
/// (eg. `mailto:someone@example.com`).
///
/// Please remember to persist the returned `Account` structure somewhere to not lose access to
/// the account!
///
/// If an RSA key size is provided, an RSA key will be generated. Otherwise an EC key using the
/// P-256 curve will be generated.
pub fn new_account(
&mut self,
contact: Vec<String>,
tos_agreed: bool,
rsa_bits: Option<u32>,
eab_creds: Option<(String, String)>,
) -> Result<&Account, Error> {
let mut account = Account::creator()
.set_contacts(contact)
.agree_to_tos(tos_agreed);
if let Some((eab_kid, eab_hmac_key)) = eab_creds {
account = account.set_eab_credentials(eab_kid, eab_hmac_key)?;
}
let account = if let Some(bits) = rsa_bits {
account.generate_rsa_key(bits)?
} else {
account.generate_ec_key()?
};
self.register_account(account)
}
/// Register an ACME account.
///
/// This uses an [`AccountCreator`](crate::account::AccountCreator) since it may need to build
/// the request multiple times in case the we get a `BadNonce` error.
pub fn register_account(
&mut self,
account: crate::account::AccountCreator,
) -> Result<&Account, Error> {
let mut retry = retry();
let mut response = loop {
retry.tick()?;
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
let nonce = Self::nonce(&mut self.inner, directory)?;
let request = account.request(directory, nonce)?;
match self.run_request(request) {
Ok(response) => break response,
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err),
}
};
let account = account.response(response.location_required()?, response.bytes().as_ref())?;
self.account = Some(account);
Ok(self.account.as_ref().unwrap())
}
fn need_account(account: &Option<Account>) -> Result<&Account, Error> {
account
.as_ref()
.ok_or_else(|| format_err!("cannot use client without an account"))
}
/// Update account data.
///
/// Low-level version: we allow arbitrary data to be passed to the remote here, it's up to the
/// user to know what to do for now.
pub fn update_account<T: Serialize>(&mut self, data: &T) -> Result<&Account, Error> {
let account = Self::need_account(&self.account)?;
let mut retry = retry();
let response = loop {
retry.tick()?;
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
let nonce = Self::nonce(&mut self.inner, directory)?;
let request = account.post_request(&account.location, nonce, data)?;
let response = match self.inner.run_request(request) {
Ok(response) => response,
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err),
};
break response;
};
// unwrap: we asserted we have an account at the top of the method!
let account = self.account.as_mut().unwrap();
account.data = response.json()?;
Ok(account)
}
/// Method to create a new order for a set of domains.
///
/// Please remember to persist the order somewhere (ideally along with the account data) in
/// order to finish & query it later on.
pub fn new_order(&mut self, domains: Vec<String>) -> Result<Order, Error> {
let account = Self::need_account(&self.account)?;
let order = domains
.into_iter()
.fold(OrderData::new(), |order, domain| order.domain(domain));
let mut retry = retry();
loop {
retry.tick()?;
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
let nonce = Self::nonce(&mut self.inner, directory)?;
let mut new_order = account.new_order(&order, directory, nonce)?;
let mut response = match self.inner.run_request(new_order.request.take().unwrap()) {
Ok(response) => response,
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err),
};
return new_order.response(response.location_required()?, response.bytes().as_ref());
}
}
/// Assuming the provided URL is an 'Authorization' URL, get and deserialize it.
pub fn get_authorization(&mut self, url: &str) -> Result<Authorization, Error> {
self.post_as_get(url)?.json()
}
/// Assuming the provided URL is an 'Order' URL, get and deserialize it.
pub fn get_order(&mut self, url: &str) -> Result<OrderData, Error> {
self.post_as_get(url)?.json()
}
/// Low level "POST-as-GET" request.
pub fn post_as_get(&mut self, url: &str) -> Result<HttpResponse, Error> {
let account = Self::need_account(&self.account)?;
let mut retry = retry();
loop {
retry.tick()?;
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
let nonce = Self::nonce(&mut self.inner, directory)?;
let request = account.get_request(url, nonce)?;
match self.inner.run_request(request) {
Ok(response) => return Ok(response),
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err),
}
}
}
/// Low level POST request.
pub fn post<T: Serialize>(&mut self, url: &str, data: &T) -> Result<HttpResponse, Error> {
let account = Self::need_account(&self.account)?;
let mut retry = retry();
loop {
retry.tick()?;
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
let nonce = Self::nonce(&mut self.inner, directory)?;
let request = account.post_request(url, nonce, data)?;
match self.inner.run_request(request) {
Ok(response) => return Ok(response),
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err),
}
}
}
/// Request challenge validation. Afterwards, the challenge should be polled.
pub fn request_challenge_validation(&mut self, url: &str) -> Result<Challenge, Error> {
self.post(url, &serde_json::json!({}))?.json()
}
/// Shortcut to `account().ok_or_else(...).key_authorization()`.
pub fn key_authorization(&self, token: &str) -> Result<String, Error> {
Self::need_account(&self.account)?.key_authorization(token)
}
/// Shortcut to `account().ok_or_else(...).dns_01_txt_value()`.
/// the key authorization value.
pub fn dns_01_txt_value(&self, token: &str) -> Result<String, Error> {
Self::need_account(&self.account)?.dns_01_txt_value(token)
}
/// Low-level API to run an n API request. This automatically updates the current nonce!
pub fn run_request(&mut self, request: Request) -> Result<HttpResponse, Error> {
self.inner.run_request(request)
}
/// Finalize an Order via its `finalize` URL property and the DER encoded CSR.
pub fn finalize(&mut self, url: &str, csr: &[u8]) -> Result<(), Error> {
let csr = b64u::encode(csr);
let data = serde_json::json!({ "csr": csr });
self.post(url, &data)?;
Ok(())
}
/// Download a certificate via its 'certificate' URL property.
///
/// The certificate will be a PEM certificate chain.
pub fn get_certificate(&mut self, url: &str) -> Result<Vec<u8>, Error> {
Ok(self.post_as_get(url)?.body)
}
/// Revoke an existing certificate (PEM or DER formatted).
pub fn revoke_certificate(
&mut self,
certificate: &[u8],
reason: Option<u32>,
) -> Result<(), Error> {
// TODO: This can also work without an account.
let account = Self::need_account(&self.account)?;
let revocation = account.revoke_certificate(certificate, reason)?;
let mut retry = retry();
loop {
retry.tick()?;
let directory =
Self::get_directory(&mut self.inner, &mut self.directory, &self.directory_url)?;
let nonce = Self::nonce(&mut self.inner, directory)?;
let request = revocation.request(directory, nonce)?;
match self.inner.run_request(request) {
Ok(_response) => return Ok(()),
Err(err) if err.is_bad_nonce() => continue,
Err(err) => return Err(err),
}
}
}
/// Set a proxy
pub fn set_proxy(&mut self, proxy: String) {
self.inner.set_proxy(proxy)
}
}
/// bad nonce retry count helper
struct Retry(usize);
const fn retry() -> Retry {
Retry(0)
}
impl Retry {
fn tick(&mut self) -> Result<(), Error> {
if self.0 >= 3 {
bail!("kept getting a badNonce error!");
}
self.0 += 1;
Ok(())
}
}

View File

@ -0,0 +1,107 @@
//! ACME Directory information.
use serde::{Deserialize, Serialize};
/// An ACME Directory. This contains the base URL and the directory data as received via a `GET`
/// request to the URL.
pub struct Directory {
/// The main entry point URL to the ACME directory.
pub url: String,
/// The json structure received via a `GET` request to the directory URL. This contains the
/// URLs for various API entry points.
pub data: DirectoryData,
}
/// The ACME Directory object structure.
///
/// The data in here is typically not relevant to the user of this crate.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectoryData {
/// The entry point to create a new account.
pub new_account: String,
/// The entry point to retrieve a new nonce, should be used with a `HEAD` request.
pub new_nonce: String,
/// URL to post new orders to.
pub new_order: String,
/// URL to use for certificate revocation.
pub revoke_cert: String,
/// Account key rollover URL.
pub key_change: String,
/// Metadata object, for additional information which aren't directly part of the API
/// itself, such as the terms of service.
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Meta>,
}
/// The directory's "meta" object.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Meta {
/// The terms of service. This is typically in the form of an URL.
#[serde(skip_serializing_if = "Option::is_none")]
pub terms_of_service: Option<String>,
/// Flag indicating if EAB is required, None is equivalent to false
#[serde(skip_serializing_if = "Option::is_none")]
pub external_account_required: Option<bool>,
/// Website with information about the ACME Server
#[serde(skip_serializing_if = "Option::is_none")]
pub website: Option<String>,
/// List of hostnames used by the CA, intended for the use with caa dns records
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub caa_identities: Vec<String>,
}
impl Directory {
/// Create a `Directory` given the parsed `DirectoryData` of a `GET` request to the directory
/// URL.
pub fn from_parts(url: String, data: DirectoryData) -> Self {
Self { url, data }
}
/// Get the ToS URL.
pub fn terms_of_service_url(&self) -> Option<&str> {
match &self.data.meta {
Some(meta) => meta.terms_of_service.as_deref(),
None => None,
}
}
/// Get if external account binding is required
pub fn external_account_binding_required(&self) -> bool {
matches!(
&self.data.meta,
Some(Meta {
external_account_required: Some(true),
..
})
)
}
/// Get the "newNonce" URL. Use `HEAD` requests on this to get a new nonce.
pub fn new_nonce_url(&self) -> &str {
&self.data.new_nonce
}
pub(crate) fn new_account_url(&self) -> &str {
&self.data.new_account
}
pub(crate) fn new_order_url(&self) -> &str {
&self.data.new_order
}
/// Access to the in the Acme spec defined metadata structure.
pub fn meta(&self) -> Option<&Meta> {
self.data.meta.as_ref()
}
}

61
proxmox-acme/src/eab.rs Normal file
View File

@ -0,0 +1,61 @@
use openssl::hash::MessageDigest;
use openssl::pkey::{HasPrivate, PKeyRef};
use openssl::sign::Signer;
use serde::Serialize;
use crate::key::Jwk;
use crate::types::ExternalAccountBinding;
use crate::{b64u, Error};
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct Protected {
alg: &'static str,
url: String,
kid: String,
}
impl ExternalAccountBinding {
/// Create a new instance
pub fn new<P>(
eab_kid: &str,
eab_hmac_key: &PKeyRef<P>,
jwk: Jwk,
url: String,
) -> Result<Self, Error>
where
P: HasPrivate,
{
let protected = Protected {
alg: "HS256",
kid: eab_kid.to_string(),
url,
};
let payload = b64u::encode(serde_json::to_string(&jwk)?.as_bytes());
let protected_data = b64u::encode(serde_json::to_string(&protected)?.as_bytes());
let signature = {
let protected = protected_data.as_bytes();
let payload = payload.as_bytes();
Self::sign_hmac(eab_hmac_key, protected, payload)?
};
let signature = b64u::encode(&signature);
Ok(ExternalAccountBinding {
protected: protected_data,
payload,
signature,
})
}
fn sign_hmac<P>(key: &PKeyRef<P>, protected: &[u8], payload: &[u8]) -> Result<Vec<u8>, Error>
where
P: HasPrivate,
{
let mut signer = Signer::new(MessageDigest::sha256(), key)?;
signer.update(protected)?;
signer.update(b".")?;
signer.update(payload)?;
Ok(signer.sign_to_vec()?)
}
}

154
proxmox-acme/src/error.rs Normal file
View File

@ -0,0 +1,154 @@
//! The `Error` type and some ACME error constants for reference.
use std::fmt;
use openssl::error::ErrorStack as SslErrorStack;
/// The ACME error string for a "bad nonce" error.
pub const BAD_NONCE: &str = "urn:ietf:params:acme:error:badNonce";
/// The ACME error string for a "user action required" error.
pub const USER_ACTION_REQUIRED: &str = "urn:ietf:params:acme:error:userActionRequired";
/// Error types returned by this crate.
#[derive(Debug)]
#[must_use = "unused errors have no effect"]
pub enum Error {
/// A `badNonce` API response. The request should be retried with the new nonce received along
/// with this response.
BadNonce,
/// A `userActionRequired` API response. Typically this means there was a change to the ToS and
/// the user has to agree to the new terms.
UserActionRequired(String),
/// Other error responses from the Acme API not handled specially.
Api(crate::request::ErrorResponse),
/// The Acme API behaved unexpectedly.
InvalidApi(String),
/// Tried to use an `Account` or `AccountCreator` without a private key.
MissingKey,
/// Tried to create an `Account` without providing a single contact info.
MissingContactInfo,
/// Tried to use an empty `Order`.
EmptyOrder,
/// A raw `openssl::PKey` containing an unsupported key was passed.
UnsupportedKeyType,
/// A raw `openssl::PKey` or `openssl::EcKey` with an unsupported curve was passed.
UnsupportedGroup,
/// Failed to parse the account data returned by the API upon account creation.
BadAccountData(String),
/// Failed to parse the order data returned by the API from a new-order request.
BadOrderData(String),
/// An openssl error occurred during a crypto operation.
RawSsl(SslErrorStack),
/// An openssl error occurred during a crypto operation.
/// With some textual context.
Ssl(&'static str, SslErrorStack),
/// An otherwise uncaught serde error happened.
Json(serde_json::Error),
/// Failed to parse
BadBase64(proxmox_base64::DecodeError),
/// Can be used by the user for textual error messages without having to downcast to regular
/// acme errors.
Custom(String),
/// If built with the `client` feature, this is where general ureq/network errors end up.
/// This is usually a `ureq::Error`, however in order to provide an API which is not
/// feature-dependent, this variant is always present and contains a boxed `dyn Error`.
HttpClient(Box<dyn std::error::Error + Send + Sync + 'static>),
/// If built with the `client` feature, this is where client specific errors which are not from
/// errors forwarded from `ureq` end up.
Client(String),
/// A non-openssl error occurred while building data for the CSR.
Csr(String),
}
impl Error {
/// Create an `Error` from a custom text.
pub fn custom<T: std::fmt::Display>(s: T) -> Self {
Error::Custom(s.to_string())
}
/// Convenience method to check if this error represents a bad nonce error in which case the
/// request needs to be re-created using a new nonce.
pub fn is_bad_nonce(&self) -> bool {
matches!(self, Error::BadNonce)
}
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Api(err) => match err.detail.as_deref() {
Some(detail) => write!(f, "{}: {}", err.ty, detail),
None => fmt::Display::fmt(&err.ty, f),
},
Error::InvalidApi(err) => write!(f, "Acme Server API misbehaved: {}", err),
Error::BadNonce => f.write_str("bad nonce, please retry with a new nonce"),
Error::UserActionRequired(err) => write!(f, "user action required: {}", err),
Error::MissingKey => f.write_str("cannot build an account without a key"),
Error::MissingContactInfo => f.write_str("account requires contact info"),
Error::EmptyOrder => f.write_str("cannot make an empty order"),
Error::UnsupportedKeyType => f.write_str("unsupported key type"),
Error::UnsupportedGroup => f.write_str("unsupported EC group"),
Error::BadAccountData(err) => {
write!(f, "bad response to account query or creation: {}", err)
}
Error::BadOrderData(err) => {
write!(f, "bad response to new-order query or creation: {}", err)
}
Error::RawSsl(err) => fmt::Display::fmt(err, f),
Error::Ssl(context, err) => {
write!(f, "{}: {}", context, err)
}
Error::Json(err) => fmt::Display::fmt(err, f),
Error::Custom(err) => fmt::Display::fmt(err, f),
Error::HttpClient(err) => fmt::Display::fmt(err, f),
Error::Client(err) => fmt::Display::fmt(err, f),
Error::Csr(err) => fmt::Display::fmt(err, f),
Error::BadBase64(err) => fmt::Display::fmt(err, f),
}
}
}
impl From<SslErrorStack> for Error {
fn from(e: SslErrorStack) -> Self {
Error::RawSsl(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::Json(e)
}
}
impl From<crate::request::ErrorResponse> for Error {
fn from(e: crate::request::ErrorResponse) -> Self {
Error::Api(e)
}
}
impl From<proxmox_base64::DecodeError> for Error {
fn from(e: proxmox_base64::DecodeError) -> Self {
Error::BadBase64(e)
}
}

43
proxmox-acme/src/json.rs Normal file
View File

@ -0,0 +1,43 @@
use openssl::hash::Hasher;
use serde_json::Value;
use crate::Error;
pub fn to_hash_canonical(value: &Value, output: &mut Hasher) -> Result<(), Error> {
match value {
Value::Null | Value::String(_) | Value::Number(_) | Value::Bool(_) => {
serde_json::to_writer(output, &value)?;
}
Value::Array(list) => {
output.update(b"[")?;
let mut iter = list.iter();
if let Some(item) = iter.next() {
to_hash_canonical(item, output)?;
for item in iter {
output.update(b",")?;
to_hash_canonical(item, output)?;
}
}
output.update(b"]")?;
}
Value::Object(map) => {
output.update(b"{")?;
let mut keys: Vec<&str> = map.keys().map(String::as_str).collect();
keys.sort_unstable();
let mut iter = keys.into_iter();
if let Some(key) = iter.next() {
serde_json::to_writer(&mut *output, &key)?;
output.update(b":")?;
to_hash_canonical(&map[key], output)?;
for key in iter {
output.update(b",")?;
serde_json::to_writer(&mut *output, &key)?;
output.update(b":")?;
to_hash_canonical(&map[key], output)?;
}
}
output.update(b"}")?;
}
}
Ok(())
}

168
proxmox-acme/src/jws.rs Normal file
View File

@ -0,0 +1,168 @@
use std::convert::TryFrom;
use openssl::hash::{Hasher, MessageDigest};
use openssl::pkey::{HasPrivate, PKeyRef};
use openssl::sign::Signer;
use serde::Serialize;
use crate::b64u;
use crate::key::{Jwk, PublicKey};
use crate::Error;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Protected {
alg: &'static str,
nonce: String,
url: String,
#[serde(flatten)]
key: KeyId,
}
/// Acme requires to the use of *either* `jwk` *or* `kid` depending on the action taken.
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum KeyId {
/// This is the actual JWK structure.
Jwk(Jwk),
/// This should be the account location.
Kid(String),
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Jws {
protected: String,
payload: String,
signature: String,
}
impl Jws {
pub fn new<P, T>(
key: &PKeyRef<P>,
location: Option<String>,
url: String,
nonce: String,
payload: &T,
) -> Result<Self, Error>
where
P: HasPrivate,
T: Serialize,
{
Self::new_full(
key,
location,
url,
nonce,
b64u::encode(serde_json::to_string(payload)?.as_bytes()),
)
}
pub fn new_full<P: HasPrivate>(
key: &PKeyRef<P>,
location: Option<String>,
url: String,
nonce: String,
payload: String,
) -> Result<Self, Error> {
let jwk = Jwk::try_from(key)?;
let pubkey = jwk.key.clone();
let mut protected = Protected {
alg: "",
nonce,
url,
key: match location {
Some(location) => KeyId::Kid(location),
None => KeyId::Jwk(jwk),
},
};
let (digest, ec_order_bytes): (MessageDigest, usize) = match &pubkey {
PublicKey::Rsa(_) => (Self::prepare_rsa(key, &mut protected), 0),
PublicKey::Ec(_) => Self::prepare_ec(key, &mut protected),
};
let protected_data = b64u::encode(serde_json::to_string(&protected)?.as_bytes());
let signature = {
let prot = protected_data.as_bytes();
let payload = payload.as_bytes();
match &pubkey {
PublicKey::Rsa(_) => Self::sign_rsa(key, digest, prot, payload),
PublicKey::Ec(_) => Self::sign_ec(key, digest, ec_order_bytes, prot, payload),
}?
};
let signature = b64u::encode(&signature);
Ok(Jws {
protected: protected_data,
payload,
signature,
})
}
fn prepare_rsa<P>(_key: &PKeyRef<P>, protected: &mut Protected) -> MessageDigest
where
P: HasPrivate,
{
protected.alg = "RS256";
MessageDigest::sha256()
}
/// Returns the digest and the size of the two signature components 'r' and 's'.
fn prepare_ec<P>(_key: &PKeyRef<P>, protected: &mut Protected) -> (MessageDigest, usize)
where
P: HasPrivate,
{
// Note: if we support >256 bit keys we'll want to also support using ES512 here probably
protected.alg = "ES256";
// 'r' and 's' are each 256 bit numbers:
(MessageDigest::sha256(), 32)
}
fn sign_rsa<P>(
key: &PKeyRef<P>,
digest: MessageDigest,
protected: &[u8],
payload: &[u8],
) -> Result<Vec<u8>, Error>
where
P: HasPrivate,
{
let mut signer = Signer::new(digest, key)?;
signer.set_rsa_padding(openssl::rsa::Padding::PKCS1)?;
signer.update(protected)?;
signer.update(b".")?;
signer.update(payload)?;
Ok(signer.sign_to_vec()?)
}
fn sign_ec<P>(
key: &PKeyRef<P>,
digest: MessageDigest,
ec_order_bytes: usize,
protected: &[u8],
payload: &[u8],
) -> Result<Vec<u8>, Error>
where
P: HasPrivate,
{
let mut hasher = Hasher::new(digest)?;
hasher.update(protected)?;
hasher.update(b".")?;
hasher.update(payload)?;
let sig =
openssl::ecdsa::EcdsaSig::sign(hasher.finish()?.as_ref(), key.ec_key()?.as_ref())?;
let r = sig.r().to_vec();
let s = sig.s().to_vec();
let mut out = Vec::with_capacity(ec_order_bytes * 2);
out.extend(std::iter::repeat(0u8).take(ec_order_bytes - r.len()));
out.extend(r);
out.extend(std::iter::repeat(0u8).take(ec_order_bytes - s.len()));
out.extend(s);
Ok(out)
}
}

129
proxmox-acme/src/key.rs Normal file
View File

@ -0,0 +1,129 @@
use std::convert::{TryFrom, TryInto};
use openssl::hash::{Hasher, MessageDigest};
use openssl::pkey::{HasPublic, Id, PKeyRef};
use serde::Serialize;
use crate::b64u;
use crate::Error;
/// An RSA public key.
#[derive(Clone, Debug, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RsaPublicKey {
#[serde(with = "b64u::bytes")]
e: Vec<u8>,
#[serde(with = "b64u::bytes")]
n: Vec<u8>,
}
/// An EC public key.
#[derive(Clone, Debug, Serialize)]
#[serde(deny_unknown_fields)]
pub struct EcPublicKey {
crv: &'static str,
#[serde(with = "b64u::bytes")]
x: Vec<u8>,
#[serde(with = "b64u::bytes")]
y: Vec<u8>,
}
/// A public key.
///
/// Internally tagged, so this already contains the 'kty' member.
#[derive(Clone, Debug, Serialize)]
#[serde(tag = "kty")]
pub enum PublicKey {
#[serde(rename = "RSA")]
Rsa(RsaPublicKey),
#[serde(rename = "EC")]
Ec(EcPublicKey),
}
impl PublicKey {
/// The thumbprint is the b64u encoded sha256sum of the *canonical* json representation.
pub fn thumbprint(&self) -> Result<String, Error> {
let mut hasher = Hasher::new(MessageDigest::sha256())?;
crate::json::to_hash_canonical(&serde_json::to_value(self)?, &mut hasher)?;
Ok(b64u::encode(hasher.finish()?.as_ref()))
}
}
#[derive(Clone, Debug, Serialize)]
pub struct Jwk {
#[serde(rename = "use", skip_serializing_if = "Option::is_none")]
pub usage: Option<String>,
/// The key data is internally tagged, we can just flatten it.
#[serde(flatten)]
pub key: PublicKey,
}
impl<P: HasPublic> TryFrom<&PKeyRef<P>> for Jwk {
type Error = Error;
fn try_from(key: &PKeyRef<P>) -> Result<Self, Self::Error> {
Ok(Self {
key: key.try_into()?,
usage: None,
})
}
}
impl<P: HasPublic> TryFrom<&PKeyRef<P>> for PublicKey {
type Error = Error;
fn try_from(key: &PKeyRef<P>) -> Result<Self, Self::Error> {
match key.id() {
Id::RSA => Ok(PublicKey::Rsa(RsaPublicKey::try_from(&key.rsa()?)?)),
Id::EC => Ok(PublicKey::Ec(EcPublicKey::try_from(&key.ec_key()?)?)),
_ => Err(Error::UnsupportedKeyType),
}
}
}
impl<P: HasPublic> TryFrom<&openssl::rsa::Rsa<P>> for RsaPublicKey {
type Error = Error;
fn try_from(key: &openssl::rsa::Rsa<P>) -> Result<Self, Self::Error> {
Ok(RsaPublicKey {
e: key.e().to_vec(),
n: key.n().to_vec(),
})
}
}
impl<P: HasPublic> TryFrom<&openssl::ec::EcKey<P>> for EcPublicKey {
type Error = Error;
fn try_from(key: &openssl::ec::EcKey<P>) -> Result<Self, Self::Error> {
let group = key.group();
if group.curve_name() != Some(openssl::nid::Nid::X9_62_PRIME256V1) {
return Err(Error::UnsupportedGroup);
}
let mut ctx = openssl::bn::BigNumContext::new()?;
let mut x = openssl::bn::BigNum::new()?;
let mut y = openssl::bn::BigNum::new()?;
key.public_key()
.affine_coordinates(group, &mut x, &mut y, &mut ctx)?;
Ok(EcPublicKey {
crv: "P-256",
x: x.to_vec(),
y: y.to_vec(),
})
}
}
#[test]
fn test_key_conversion() -> Result<(), Error> {
let key = openssl::ec::EcKey::generate(
openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1)?.as_ref(),
)?;
let _ = EcPublicKey::try_from(&key).expect("failed to jsonify ec key");
Ok(())
}

91
proxmox-acme/src/lib.rs Normal file
View File

@ -0,0 +1,91 @@
//! ACME protocol helper.
//!
//! This is supposed to implement the low level parts of the ACME protocol, providing an [`Account`]
//! and some other helper types which allow interacting with an ACME server by implementing methods
//! which create [`Request`]s the user can then combine with a nonce and send to the the ACME
//! server using whatever http client they choose.
//!
//! This is a rather low level crate, and while it provides an optional synchronous client using
//! curl (for simplicity), users should have basic understanding of the ACME API in order to
//! implement a client using this.
//!
//! The [`Account`] helper supports RSA and ECC keys and provides most of the API methods.
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#[cfg(feature = "api-types")]
pub mod types;
#[cfg(feature = "impl")]
mod b64u;
#[cfg(feature = "impl")]
mod eab;
#[cfg(feature = "impl")]
mod json;
#[cfg(feature = "impl")]
mod jws;
#[cfg(feature = "impl")]
mod key;
#[cfg(feature = "impl")]
mod request;
#[cfg(feature = "impl")]
pub mod account;
#[cfg(feature = "impl")]
pub mod authorization;
#[cfg(feature = "impl")]
pub mod directory;
#[cfg(feature = "impl")]
pub mod error;
#[cfg(feature = "impl")]
pub mod order;
#[cfg(feature = "impl")]
pub mod util;
#[cfg(feature = "impl")]
#[doc(inline)]
pub use account::Account;
#[cfg(feature = "impl")]
#[doc(inline)]
pub use authorization::{Authorization, Challenge};
#[cfg(feature = "impl")]
#[doc(inline)]
pub use directory::Directory;
#[cfg(feature = "impl")]
#[doc(inline)]
pub use error::Error;
#[cfg(feature = "impl")]
#[doc(inline)]
pub use order::Order;
#[cfg(feature = "impl")]
#[doc(inline)]
pub use request::Request;
// we don't inline these:
#[cfg(feature = "impl")]
pub use order::NewOrder;
#[cfg(feature = "impl")]
pub use request::ErrorResponse;
/// Header name for nonces.
pub const REPLAY_NONCE: &str = "Replay-Nonce";
/// Header name for locations.
pub const LOCATION: &str = "Location";
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "client")]
pub use client::Client;
#[cfg(feature = "async-client")]
pub mod async_client;

175
proxmox-acme/src/order.rs Normal file
View File

@ -0,0 +1,175 @@
//! ACME Orders data and identifiers.
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::request::Request;
use crate::Error;
/// Status of an [`Order`].
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[derive(Default)]
pub enum Status {
/// Invalid, used as a place holder for when sending objects as contrary to account creation,
/// the Acme RFC does not require the server to ignore unknown parts of the `Order` object.
#[default]
New,
/// Authorization failed and it is now invalid.
Invalid,
/// The authorization is pending and the user should look through its challenges.
///
/// This is the initial state of a new authorization.
Pending,
/// The ACME provider is processing an authorization validation.
Processing,
/// The requirements for the order have been met and it may be finalized.
Ready,
/// The certificate has been issued and can be downloaded from the URL provided in the
/// [`Order`]'s `certificate` field.
Valid,
}
impl Status {
/// Serde helper
fn is_new(&self) -> bool {
*self == Status::New
}
/// Convenience method to check if the status is 'pending'.
#[inline]
pub fn is_pending(self) -> bool {
self == Status::Pending
}
/// Convenience method to check if the status is 'valid'.
#[inline]
pub fn is_valid(self) -> bool {
self == Status::Valid
}
}
/// An identifier used for a certificate request.
///
/// Currently only supports DNS name identifiers.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(tag = "type", content = "value", rename_all = "lowercase")]
pub enum Identifier {
/// A DNS identifier is used to request a domain name to be added to a certificate.
Dns(String),
}
/// This contains the order data sent to and received from the ACME server.
///
/// This is typically filled with a set of domains and then issued as a new-order request via [`Account::new_order`](crate::Account::new_order).
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
/// The order status.
#[serde(skip_serializing_if = "Status::is_new", default)]
pub status: Status,
/// This order's expiration date as RFC3339 formatted time string.
#[serde(skip_serializing_if = "Option::is_none")]
pub expires: Option<String>,
/// List of identifiers to order for the certificate.
pub identifiers: Vec<Identifier>,
/// An RFC3339 formatted time string. It is up to the user to choose a dev dependency for this
/// shit.
#[serde(skip_serializing_if = "Option::is_none")]
pub not_before: Option<String>,
/// An RFC3339 formatted time string. It is up to the user to choose a dev dependency for this
/// shit.
#[serde(skip_serializing_if = "Option::is_none")]
pub not_after: Option<String>,
/// Possible errors in this order.
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<Value>,
/// List of URL's to authorizations the client needs to complete.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub authorizations: Vec<String>,
/// URL the final CSR needs to be POSTed to in order to complete the order, once all
/// authorizations have been performed.
#[serde(skip_serializing_if = "Option::is_none")]
pub finalize: Option<String>,
/// URL at which the issued certificate can be fetched once it is available.
#[serde(skip_serializing_if = "Option::is_none")]
pub certificate: Option<String>,
}
impl OrderData {
/// Initialize an empty order object.
pub fn new() -> Self {
Default::default()
}
/// Builder-style method to add a domain identifier to the data.
pub fn domain(mut self, domain: String) -> Self {
self.identifiers.push(Identifier::Dns(domain));
self
}
}
/// Represents an order for a new certificate. This combines the order's own location (URL) with
/// the [`OrderData`] received from the ACME server.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Order {
/// Order location URL.
pub location: String,
/// The order's data object.
pub data: OrderData,
}
impl Order {
/// Get an authorization URL (or `None` if the index is out of range).
pub fn authorization(&self, index: usize) -> Option<&str> {
Some(self.data.authorizations.get(index)?)
}
/// Get the number of authorizations in this object.
pub fn authorization_len(&self) -> usize {
self.data.authorizations.len()
}
}
/// Represents a new in-flight order creation.
///
/// This is created via [`Account::new_order`](crate::Account::new_order()).
pub struct NewOrder {
//order: OrderData,
/// The request to execute to place the order. When creating a [`NewOrder`] via
/// [`Account::new_order`](crate::Account::new_order) this is guaranteed to be `Some`.
pub request: Option<Request>,
}
impl NewOrder {
pub(crate) fn new(request: Request) -> Self {
Self {
//order,
request: Some(request),
}
}
/// Deal with the response we got from the server.
pub fn response(self, location_header: String, response_body: &[u8]) -> Result<Order, Error> {
Ok(Order {
location: location_header,
data: serde_json::from_slice(response_body)
.map_err(|err| Error::BadOrderData(err.to_string()))?,
})
}
}

View File

@ -0,0 +1,42 @@
use serde::Deserialize;
pub(crate) const JSON_CONTENT_TYPE: &str = "application/jose+json";
pub(crate) const CREATED: u16 = 201;
/// A request which should be performed on the ACME provider.
pub struct Request {
/// The complete URL to send the request to.
pub url: String,
/// The HTTP method name to use.
pub method: &'static str,
/// The `Content-Type` header to pass along.
pub content_type: &'static str,
/// The body to pass along with request, or an empty string.
pub body: String,
/// The expected status code a compliant ACME provider will return on success.
pub expected: u16,
}
/// An ACME error response contains a specially formatted type string, and can optionally
/// contain textual details and a set of sub problems.
#[derive(Clone, Debug, Deserialize)]
pub struct ErrorResponse {
/// The ACME error type string.
///
/// Most of the time we're only interested in the "bad nonce" or "user action required"
/// errors. When an [`Error`](crate::Error) is built from this error response, it will map
/// to the corresponding enum values (eg. [`Error::BadNonce`](crate::Error::BadNonce)).
#[serde(rename = "type")]
pub ty: String,
/// A textual detail string optionally provided by the ACME provider to inform the user more
/// verbosely about why the error occurred.
pub detail: Option<String>,
/// Additional json data containing information as to why the error occurred.
pub subproblems: Option<serde_json::Value>,
}

126
proxmox-acme/src/types.rs Normal file
View File

@ -0,0 +1,126 @@
//! Define types which are exposed with the proxmox API
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[cfg_attr(feature = "api-types", proxmox_schema::api())]
/// External Account Bindings
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ExternalAccountBinding {
/// JOSE Header (see RFC 7515)
pub protected: String,
/// Payload
pub payload: String,
/// HMAC signature
pub signature: String,
}
/// Status of an ACME account.
#[cfg_attr(feature = "api-types", proxmox_schema::api())]
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum AccountStatus {
/// This is not part of the ACME API, but a temporary marker for us until the ACME provider
/// tells us the account's real status.
#[serde(rename = "<invalid>")]
New,
/// Means the account is valid and can be used.
Valid,
/// The account has been deactivated by its user and cannot be used anymore.
Deactivated,
/// The account has been revoked by the server and cannot be used anymore.
Revoked,
}
impl Default for AccountStatus {
fn default() -> Self {
Self::new()
}
}
impl AccountStatus {
/// Create a new instance with state New.
#[inline]
pub fn new() -> Self {
AccountStatus::New
}
/// Return true if state is New
#[inline]
pub fn is_new(&self) -> bool {
*self == AccountStatus::New
}
}
#[inline]
fn default_true() -> bool {
true
}
#[inline]
fn is_false(b: &bool) -> bool {
!*b
}
#[cfg_attr(feature="api-types", proxmox_schema::api(
properties: {
extra: {
type: Object,
properties: {},
additional_properties: true,
},
contact: {
type: Array,
items: {
type: String,
description: "Contact Info.",
},
},
}
))]
/// ACME Account data. This is the part of the account returned from and possibly sent to the ACME
/// provider. Some fields may be uptdated by the user via a request to the account location, others
/// may not be changed.
#[derive(Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountData {
/// The current account status.
#[serde(
skip_serializing_if = "AccountStatus::is_new",
default = "AccountStatus::new"
)]
pub status: AccountStatus,
/// URLs to currently pending orders.
#[serde(skip_serializing_if = "Option::is_none")]
pub orders: Option<String>,
/// The account's contact info.
///
/// This usually contains a `"mailto:<email address>"` entry but may also contain some other
/// data if the server accepts it.
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub contact: Vec<String>,
/// Indicated whether the user agreed to the ACME provider's terms of service.
#[serde(skip_serializing_if = "Option::is_none")]
pub terms_of_service_agreed: Option<bool>,
/// External account information.
#[serde(skip_serializing_if = "Option::is_none")]
pub external_account_binding: Option<ExternalAccountBinding>,
/// This is only used by the client when querying an account.
#[serde(default = "default_true", skip_serializing_if = "is_false")]
pub only_return_existing: bool,
/// Stores unknown fields if there are any.
#[serde(flatten, default, skip_serializing_if = "HashMap::is_empty")]
pub extra: HashMap<String, Value>,
}

85
proxmox-acme/src/util.rs Normal file
View File

@ -0,0 +1,85 @@
//! Certificate utility methods for convenience (such as CSR generation).
use std::collections::HashMap;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::x509::{self, X509Name, X509Req};
use crate::Error;
/// A certificate signing request.
pub struct Csr {
/// DER encoded certificate request.
pub data: Vec<u8>,
/// PEM formatted PKCS#8 private key.
pub private_key_pem: Vec<u8>,
}
impl Csr {
/// Generate a CSR in DER format with a PEM formatted PKCS8 private key.
///
/// The `identifiers` should be a list of domains. The `attributes` should have standard names
/// recognized by openssl.
pub fn generate(
identifiers: &[impl AsRef<str>],
attributes: &HashMap<String, &str>,
) -> Result<Self, Error> {
if identifiers.is_empty() {
return Err(Error::Csr("cannot generate empty CSR".to_string()));
}
let private_key = Rsa::generate(4096)
.and_then(PKey::from_rsa)
.map_err(|err| Error::Ssl("failed to generate RSA key: {}", err))?;
let private_key_pem = private_key
.private_key_to_pem_pkcs8()
.map_err(|err| Error::Ssl("failed to format private key as PEM pkcs8: {}", err))?;
let mut name = X509Name::builder()?;
if !attributes.contains_key("CN") {
name.append_entry_by_nid(Nid::COMMONNAME, identifiers[0].as_ref())?;
}
for (key, value) in attributes {
name.append_entry_by_text(key, value)?;
}
let name = name.build();
let mut csr = X509Req::builder()?;
csr.set_subject_name(&name)?;
csr.set_pubkey(&private_key)?;
let context = csr.x509v3_context(None);
let mut ext = openssl::stack::Stack::new()?;
ext.push(x509::extension::BasicConstraints::new().build()?)?;
ext.push(
x509::extension::KeyUsage::new()
.digital_signature()
.key_encipherment()
.build()?,
)?;
ext.push(
x509::extension::ExtendedKeyUsage::new()
.server_auth()
.client_auth()
.build()?,
)?;
let mut san = x509::extension::SubjectAlternativeName::new();
for dns in identifiers {
san.dns(dns.as_ref());
}
ext.push({ san }.build(&context)?)?;
csr.add_extensions(&ext)?;
csr.sign(&private_key, MessageDigest::sha256())?;
Ok(Self {
data: csr.build().to_der()?,
private_key_pem,
})
}
}

View File

@ -1,28 +1,37 @@
[package]
name = "proxmox-api-macro"
edition = "2018"
version = "0.3.4"
authors = [ "Wolfgang Bumiller <w.bumiller@proxmox.com>" ]
license = "AGPL-3"
description = "Proxmox API macro"
version = "1.4.0"
exclude = [ "debian" ]
authors.workspace = true
edition.workspace = true
exclude.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
[lib]
proc-macro = true
[dependencies]
anyhow = "1.0"
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = [ "extra-traits", "full", "visit-mut" ] }
anyhow.workspace = true
proc-macro2.workspace = true
quote.workspace = true
syn = { workspace = true , features = [ "extra-traits" ] }
[dev-dependencies]
futures = "0.3"
proxmox = { version = "0.11.0", path = "../proxmox", features = [ "test-harness", "api-macro" ] }
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
futures.workspace = true
serde = { workspace = true, features = [ "derive" ] }
serde_json.workspace = true
proxmox-section-config.workspace = true
[dev-dependencies.proxmox-schema]
workspace = true
features = [ "test-harness", "api-macro" ]
[dev-dependencies.proxmox-router]
workspace = true
features = [ "test-harness" ]
# [features]
# # Used to quickly filter out the serde derive noise when using `cargo expand` for debugging!

View File

@ -1,3 +1,161 @@
rust-proxmox-api-macro (1.4.0-1) trixie; urgency=medium
* re-build for Debian Trixie based releases.
-- Proxmox Support Team <support@proxmox.com> Mon, 12 May 2025 21:16:59 +0200
rust-proxmox-api-macro (1.3.3-1) bookworm; urgency=medium
* rebuild with section-config 3.0
* sort object schema entries
-- Proxmox Support Team <support@proxmox.com> Tue, 06 May 2025 11:46:45 +0200
rust-proxmox-api-macro (1.3.2-1) bookworm; urgency=medium
* mark parameter defaults as `#[allow(dead_code)]`
* sort variants when using `#[api]` on an `enum` to generate a OneOfSchema
-- Proxmox Support Team <support@proxmox.com> Wed, 19 Feb 2025 12:55:02 +0100
rust-proxmox-api-macro (1.3.1-1) bookworm; urgency=medium
* rebuild with proxmox-schema 4.0
-- Proxmox Support Team <support@proxmox.com> Wed, 15 Jan 2025 12:36:26 +0100
rust-proxmox-api-macro (1.3.0-1) bookworm; urgency=medium
* A missing/empty description for enums is now an error.
* Add experimental json_schema!() macro to create a `Schema` in json
notation.
-- Proxmox Support Team <support@proxmox.com> Thu, 09 Jan 2025 14:20:54 +0100
rust-proxmox-api-macro (1.2.1-1) bookworm; urgency=medium
* allow declaring a field meant to collect the 'additional_properties'
-- Proxmox Support Team <support@proxmox.com> Thu, 26 Sep 2024 14:52:43 +0200
rust-proxmox-api-macro (1.2.0-1) bookworm; urgency=medium
* deprecate old "streaming" method attribute
* add "serializing" method attribute to replace the old "streaming" one
* add "stream" method attribute for the *new* streaming API
* fix warnings in tests
-- Proxmox Support Team <support@proxmox.com> Wed, 04 Sep 2024 15:36:05 +0200
rust-proxmox-api-macro (1.1.0-1) stable; urgency=medium
* fix handling of renames when deriving an Updater for structs
* experimental support for newtype-only enums for SectionConfig support
* use const blocks in thread_local calls
* documentation and typo fixe
* code cleanups, warning and clippy fixes
-- Proxmox Support Team <support@proxmox.com> Tue, 06 Aug 2024 14:15:49 +0200
rust-proxmox-api-macro (1.0.8-1) stable; urgency=medium
* update to proxmox-schema 3
* make #[serde(skip_serializing_if)] without #[serde(default)] an error
-- Proxmox Support Team <support@proxmox.com> Fri, 02 Feb 2024 13:44:40 +0100
rust-proxmox-api-macro (1.0.7-1) stable; urgency=medium
* make serde(skip_serializing_if) without serde(default) for non-Option
types an error
* split field and variant attribute parsing
-- Proxmox Support Team <support@proxmox.com> Wed, 06 Dec 2023 16:02:11 +0100
rust-proxmox-api-macro (1.0.6-1) stable; urgency=medium
* clippy fix: this (Default) `impl` can be derived
* update to syn 2, rework attribute parsing
-- Proxmox Support Team <support@proxmox.com> Mon, 02 Oct 2023 09:27:12 +0200
rust-proxmox-api-macro (1.0.5-1) bookworm; urgency=medium
* support non-idents in serde rename attributes on enum variants
-- Proxmox Support Team <support@proxmox.com> Thu, 03 Aug 2023 08:23:42 +0200
rust-proxmox-api-macro (1.0.4-1) stable; urgency=medium
* support #[default] attribute for types which derive Default
* documentation updates
-- Proxmox Support Team <support@proxmox.com> Mon, 12 Dec 2022 11:31:34 +0100
rust-proxmox-api-macro (1.0.3-1) stable; urgency=medium
* allow overriding fiel attributes when deriving an updater
-- Proxmox Support Team <support@proxmox.com> Thu, 19 May 2022 12:03:36 +0200
rust-proxmox-api-macro (1.0.2-1) stable; urgency=medium
* support streaming api handlers
-- Proxmox Support Team <support@proxmox.com> Tue, 12 Apr 2022 14:26:46 +0200
rust-proxmox-api-macro (1.0.1-1) stable; urgency=medium
* stop adding automatically_derived to derived output to please new rustc
-- Proxmox Support Team <support@proxmox.com> Tue, 12 Oct 2021 14:49:35 +0200
rust-proxmox-api-macro (1.0.0-1) stable; urgency=medium
* schema was split out of proxmox into a new proxmox-schema crate
-- Proxmox Support Team <support@proxmox.com> Thu, 07 Oct 2021 14:28:14 +0200
rust-proxmox-api-macro (0.5.1-1) stable; urgency=medium
* allow external `returns` specification on methods, refereincing a
`ReturnType`.
-- Proxmox Support Team <support@proxmox.com> Mon, 30 Aug 2021 10:44:21 +0200
rust-proxmox-api-macro (0.5.0-1) stable; urgency=medium
* for non structs without Updater types and methods, `type: Foo` can now be
omitted for api types
* Adapt to the changes to Updatable in the proxmox crate
* Updaters have no try_build_from or update_from method anymore for now
* #[api] types automatically implement the new ApiType trait
-- Proxmox Support Team <support@proxmox.com> Tue, 24 Aug 2021 15:22:05 +0200
rust-proxmox-api-macro (0.4.0-1) stable; urgency=medium
* update proxmox to 0.12.0
-- Proxmox Support Team <support@proxmox.com> Tue, 20 Jul 2021 17:09:40 +0200
rust-proxmox-api-macro (0.3.4-1) unstable; urgency=medium
* fix path in generated Updatable derive entry to not require explicit

View File

@ -1,22 +1,25 @@
Source: rust-proxmox-api-macro
Section: rust
Priority: optional
Build-Depends: debhelper (>= 11),
dh-cargo (>= 18),
cargo:native <!nocheck>,
Build-Depends: debhelper-compat (= 13),
dh-sequence-cargo
Build-Depends-Arch: cargo:native <!nocheck>,
rustc:native <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-anyhow-1+default-dev <!nocheck>,
librust-proc-macro2-1+default-dev <!nocheck>,
librust-quote-1+default-dev <!nocheck>,
librust-syn-1+default-dev <!nocheck>,
librust-syn-1+extra-traits-dev <!nocheck>,
librust-syn-1+full-dev <!nocheck>,
librust-syn-1+visit-mut-dev <!nocheck>
librust-syn-2+default-dev <!nocheck>,
librust-syn-2+extra-traits-dev <!nocheck>,
librust-syn-2+full-dev <!nocheck>,
librust-syn-2+visit-mut-dev <!nocheck>
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.4.1
Standards-Version: 4.7.0
Vcs-Git: git://git.proxmox.com/git/proxmox.git
Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
Homepage: https://proxmox.com
X-Cargo-Crate: proxmox-api-macro
Rules-Requires-Root: no
Package: librust-proxmox-api-macro-dev
Architecture: any
@ -26,18 +29,17 @@ Depends:
librust-anyhow-1+default-dev,
librust-proc-macro2-1+default-dev,
librust-quote-1+default-dev,
librust-syn-1+default-dev,
librust-syn-1+extra-traits-dev,
librust-syn-1+full-dev,
librust-syn-1+visit-mut-dev
librust-syn-2+default-dev,
librust-syn-2+extra-traits-dev,
librust-syn-2+full-dev,
librust-syn-2+visit-mut-dev
Provides:
librust-proxmox-api-macro+default-dev (= ${binary:Version}),
librust-proxmox-api-macro-0-dev (= ${binary:Version}),
librust-proxmox-api-macro-0+default-dev (= ${binary:Version}),
librust-proxmox-api-macro-0.3-dev (= ${binary:Version}),
librust-proxmox-api-macro-0.3+default-dev (= ${binary:Version}),
librust-proxmox-api-macro-0.3.4-dev (= ${binary:Version}),
librust-proxmox-api-macro-0.3.4+default-dev (= ${binary:Version})
librust-proxmox-api-macro-1-dev (= ${binary:Version}),
librust-proxmox-api-macro-1+default-dev (= ${binary:Version}),
librust-proxmox-api-macro-1.4-dev (= ${binary:Version}),
librust-proxmox-api-macro-1.4+default-dev (= ${binary:Version}),
librust-proxmox-api-macro-1.4.0-dev (= ${binary:Version}),
librust-proxmox-api-macro-1.4.0+default-dev (= ${binary:Version})
Description: Proxmox API macro - Rust source code
This package contains the source for the Rust proxmox-api-macro crate, packaged
by debcargo for use with cargo and dh-cargo.
Source code for Debianized Rust crate "proxmox-api-macro"

View File

@ -1,16 +1,18 @@
Copyright (C) 2019 Proxmox Server Solutions GmbH
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
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/>.
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/>.

View File

@ -0,0 +1,112 @@
use proc_macro2::TokenStream;
use syn::meta::ParseNestedMeta;
use crate::util;
#[derive(Default)]
pub struct UpdaterFieldAttributes {
/// Skip this field in the updater.
skip: Option<syn::LitBool>,
/// Change the type for the updater.
ty: Option<syn::TypePath>,
/// Replace any `#[serde]` attributes on the field with these (accumulates).
serde: Vec<syn::Attribute>,
}
impl UpdaterFieldAttributes {
pub fn from_attributes(input: &mut Vec<syn::Attribute>) -> Self {
let mut this = Self::default();
for attr in std::mem::take(input) {
if attr.style != syn::AttrStyle::Outer || !attr.path().is_ident("updater") {
input.push(attr);
continue;
}
match attr.parse_nested_meta(|meta| this.parse(meta)) {
Ok(()) => (),
Err(err) => crate::add_error(err),
}
}
this
}
fn parse(&mut self, meta: ParseNestedMeta<'_>) -> Result<(), syn::Error> {
let path = &meta.path;
if path.is_ident("skip") {
if !meta.input.is_empty() {
return Err(meta.error("'skip' attribute does not take any data"));
}
util::set_bool(&mut self.skip, path, true);
} else if path.is_ident("type") {
util::parse_str_value_to_option(&mut self.ty, path, meta.value()?);
} else if path.is_ident("serde") {
let content: TokenStream = meta.input.parse()?;
self.serde.push(syn::parse_quote! { # [ #path #content ] });
} else {
return Err(meta.error(format!("invalid updater attribute: {path:?}")));
}
Ok(())
}
pub fn skip(&self) -> bool {
util::default_false(self.skip.as_ref())
}
pub fn ty(&self) -> Option<&syn::TypePath> {
self.ty.as_ref()
}
pub fn replace_serde_attributes(&self, attrs: &mut Vec<syn::Attribute>) {
if !self.serde.is_empty() {
attrs.retain(|attr| !attr.path().is_ident("serde"));
attrs.extend(self.serde.iter().cloned())
}
}
}
#[derive(Default)]
pub struct EnumFieldAttributes {
/// Change the "type-key" for this entry type..
type_key: Option<syn::LitStr>,
}
impl EnumFieldAttributes {
pub fn from_attributes(input: &mut Vec<syn::Attribute>) -> Self {
let mut this = Self::default();
for attr in std::mem::take(input) {
if attr.style != syn::AttrStyle::Outer || !attr.path().is_ident("api") {
input.push(attr);
continue;
}
match attr.parse_nested_meta(|meta| this.parse(meta)) {
Ok(()) => (),
Err(err) => crate::add_error(err),
}
}
this
}
fn parse(&mut self, meta: ParseNestedMeta<'_>) -> Result<(), syn::Error> {
let path = &meta.path;
if path.is_ident("type_key") {
util::duplicate(&self.type_key, path);
self.type_key = Some(meta.value()?.parse()?);
} else {
return Err(meta.error(format!("invalid api attribute: {path:?}")));
}
Ok(())
}
pub fn type_key(&self) -> Option<&syn::LitStr> {
self.type_key.as_ref()
}
}

View File

@ -4,13 +4,55 @@ use anyhow::Error;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote_spanned;
use syn::spanned::Spanned;
use super::attributes::EnumFieldAttributes;
use super::Schema;
use crate::serde;
use crate::util::{self, FieldName, JSONObject, JSONValue, Maybe};
/// Enums, provided they're simple enums, simply get an enum string schema attached to them.
pub fn handle_enum(
pub fn handle_enum(attribs: JSONObject, enum_ty: syn::ItemEnum) -> Result<TokenStream, Error> {
let mut first_unit = None;
let mut first_unnamed = None;
let mut first_named = None;
for variant in &enum_ty.variants {
match &variant.fields {
syn::Fields::Unit => first_unit = Some(variant.fields.span()),
syn::Fields::Unnamed(_) => first_unnamed = Some(variant.fields.span()),
syn::Fields::Named(_) => first_named = Some(variant.fields.span()),
}
}
if first_unit.is_some() {
if let Some(conflict) = first_unnamed.or(first_named) {
bail!(
conflict,
"enums must be either with only unit types or only newtypes"
);
}
return handle_string_enum(attribs, enum_ty);
}
if first_unnamed.is_some() {
if let Some(conflict) = first_unit.or(first_named) {
bail!(
conflict,
"enums must be either with only unit types or only newtypes"
);
}
return handle_section_config_enum(attribs, enum_ty);
}
if let Some(bad) = first_named {
bail!(bad, "api type enums with named fields are not allowed");
}
bail!(enum_ty => "api type enums must not be empty");
}
/// Enums, provided they're simple enums, simply get an enum string schema attached to them.
fn handle_string_enum(
mut attribs: JSONObject,
mut enum_ty: syn::ItemEnum,
) -> Result<TokenStream, Error> {
@ -25,11 +67,19 @@ pub fn handle_enum(
error!(fmt.span(), "illegal key 'format', will be autogenerated");
}
let has_default_attrib = attribs.get("default").map(|def| def.span());
let schema = {
let mut schema: Schema = attribs.try_into()?;
if schema.description.is_none() {
let (comment, span) = util::get_doc_comments(&enum_ty.attrs)?;
if comment.is_empty() {
error!(
Span::call_site(),
"missing doc comment on enum for api-schema description"
);
}
schema.description = Maybe::Derived(syn::LitStr::new(comment.trim(), span));
}
@ -39,6 +89,8 @@ pub fn handle_enum(
};
let container_attrs = serde::ContainerAttrib::try_from(&enum_ty.attrs[..])?;
let derives_default = util::derives_trait(&enum_ty.attrs, "Default");
let mut default_value = None;
let mut variants = TokenStream::new();
for variant in &mut enum_ty.variants {
@ -53,9 +105,9 @@ pub fn handle_enum(
comment = "<missing description>".to_string();
}
let attrs = serde::SerdeAttrib::try_from(&variant.attrs[..])?;
let attrs = serde::VariantAttrib::try_from(&variant.attrs[..])?;
let variant_string = if let Some(renamed) = attrs.rename {
renamed.into_lit_str()
renamed
} else if let Some(rename_all) = container_attrs.rename_all {
let name = rename_all.apply_to_variant(&variant.ident.to_string());
syn::LitStr::new(&name, variant.ident.span())
@ -64,8 +116,23 @@ pub fn handle_enum(
syn::LitStr::new(&name.to_string(), name.span())
};
if derives_default {
if let Some(attr) = variant.attrs.iter().find(|a| a.path().is_ident("default")) {
if let Some(default_value) = &default_value {
error!(attr => "multiple default values defined");
error!(default_value => "default previously defined here");
} else {
default_value = Some(variant_string.clone());
if let Some(span) = has_default_attrib {
error!(attr => "#[default] attribute in use with 'default' #[api] key");
error!(span, "'default' also defined here");
}
}
}
}
variants.extend(quote_spanned! { variant.ident.span() =>
::proxmox::api::schema::EnumEntry {
::proxmox_schema::EnumEntry {
value: #variant_string,
description: #comment,
},
@ -74,13 +141,174 @@ pub fn handle_enum(
let name = &enum_ty.ident;
let default_value = match default_value {
Some(value) => quote_spanned!(value.span() => .default(#value)),
None => TokenStream::new(),
};
Ok(quote_spanned! { name.span() =>
#enum_ty
impl #name {
pub const API_SCHEMA: ::proxmox::api::schema::Schema =
impl ::proxmox_schema::ApiType for #name {
const API_SCHEMA: ::proxmox_schema::Schema =
#schema
.format(&::proxmox::api::schema::ApiStringFormat::Enum(&[#variants]))
.format(&::proxmox_schema::ApiStringFormat::Enum(&[#variants]))
#default_value
.schema();
}
impl ::proxmox_schema::UpdaterType for #name {
type Updater = Option<Self>;
}
})
}
fn handle_section_config_enum(
mut attribs: JSONObject,
mut enum_ty: syn::ItemEnum,
) -> Result<TokenStream, Error> {
let name = &enum_ty.ident;
let description: syn::LitStr = match attribs.remove("description") {
Some(desc) => desc.try_into()?,
None => {
let (comment, span) = util::get_doc_comments(&enum_ty.attrs)?;
syn::LitStr::new(comment.trim(), span)
}
};
let id_schema = {
let schema: Schema = match attribs.remove("id-schema") {
Some(schema) => schema.try_into()?,
None => {
bail!(name => "missing 'id-schema' property for SectionConfig style enum")
}
};
let mut ts = TokenStream::new();
schema.to_typed_schema(&mut ts)?;
ts
};
let id_property: syn::LitStr = match attribs.remove("id-property") {
Some(name) => name.try_into()?,
None => bail!(name => "missing 'id-property' property for SectionConfig style enum"),
};
let with_type_key: TokenStream = match attribs.remove("type-key") {
Some(value) => {
let value: syn::LitStr = value.try_into()?;
quote_spanned!(value.span() => .with_type_key(#value))
}
None => TokenStream::new(),
};
let container_attrs = serde::ContainerAttrib::try_from(&enum_ty.attrs[..])?;
let Some(tag) = container_attrs.tag.as_ref() else {
bail!(name => r#"SectionConfig enum needs a `#[serde(tag = "...")]` container attribute"#);
};
let mut variants = Vec::new();
let mut register_sections = TokenStream::new();
let mut to_type = TokenStream::new();
for variant in &mut enum_ty.variants {
let field = match &variant.fields {
syn::Fields::Unnamed(field) if field.unnamed.len() == 1 => &field.unnamed[0],
_ => bail!(variant => "SectionConfig style enum can only have newtype variants"),
};
let attrs = serde::VariantAttrib::try_from(&variant.attrs[..])?;
let variant_string = if let Some(renamed) = attrs.rename {
renamed
} else if let Some(rename_all) = container_attrs.rename_all {
let name = rename_all.apply_to_variant(&variant.ident.to_string());
syn::LitStr::new(&name, variant.ident.span())
} else {
let name = &variant.ident;
syn::LitStr::new(&name.to_string(), name.span())
};
let field_attrs = EnumFieldAttributes::from_attributes(&mut variant.attrs);
let with_type_key = if let Some(key) = field_attrs.type_key() {
quote_spanned!(key.span() => .with_type_key(#key))
} else {
TokenStream::new()
};
let variant_ident = &variant.ident;
let ty = &field.ty;
variants.push((
variant_string.value(),
quote_spanned! { variant.ident.span() =>
(
#variant_string,
&<#ty as ::proxmox_schema::ApiType>::API_SCHEMA,
),
},
));
register_sections.extend(quote_spanned! { variant.ident.span() =>
this.register_plugin(
::proxmox_section_config::SectionConfigPlugin::new(
#variant_string.to_string(),
Some(#id_property.to_string()),
const {
match &<#ty as ::proxmox_schema::ApiType>::API_SCHEMA {
::proxmox_schema::Schema::Object(schema) => schema,
::proxmox_schema::Schema::OneOf(schema) => schema,
_ => panic!("enum requires an object schema"),
}
}
)
#with_type_key
);
});
to_type.extend(quote_spanned! { variant.ident.span() =>
Self::#variant_ident(_) => #variant_string,
});
}
variants.sort_by(|a, b| a.0.cmp(&b.0));
let variants = variants
.into_iter()
.map(|(_name, def)| def)
.collect::<TokenStream>();
Ok(quote_spanned! { name.span() =>
#enum_ty
impl ::proxmox_schema::ApiType for #name {
const API_SCHEMA: ::proxmox_schema::Schema =
::proxmox_schema::OneOfSchema::new(
#description,
&(#tag, false, &#id_schema.schema()),
&[#variants],
)
.schema();
}
impl ::proxmox_section_config::typed::ApiSectionDataEntry for #name {
const INTERNALLY_TAGGED: Option<&'static str> = Some(#tag);
fn section_config() -> &'static ::proxmox_section_config::SectionConfig {
static CONFIG: ::std::sync::OnceLock<::proxmox_section_config::SectionConfig> =
::std::sync::OnceLock::new();
CONFIG.get_or_init(|| {
let id_schema = const {
<Self as ::proxmox_schema::ApiType>::API_SCHEMA
.unwrap_one_of_schema()
.type_property_entry
.2
};
let mut this = ::proxmox_section_config::SectionConfig::new(id_schema)
#with_type_key;
#register_sections
this
})
}
fn section_type(&self) -> &'static str {
match self {
#to_type
}
}
}
})
}

View File

@ -11,7 +11,7 @@ use std::mem;
use anyhow::Error;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use quote::{quote, quote_spanned, ToTokens};
use syn::ext::IdentExt;
use syn::spanned::Spanned;
use syn::visit_mut::{self, VisitMut};
@ -20,16 +20,53 @@ use syn::Ident;
use super::{ObjectEntry, Schema, SchemaItem, SchemaObject};
use crate::util::{self, FieldName, JSONObject, JSONValue, Maybe};
/// A return type in a schema can have an `optional` flag. Other than that it is just a regular
/// schema, but we also want to be able to reference external `ReturnType` values for this.
pub enum ReturnType {
Explicit(ReturnSchema),
Extern(syn::Expr),
}
impl ReturnType {
fn as_mut_schema(&mut self) -> Option<&mut Schema> {
match self {
ReturnType::Explicit(ReturnSchema { ref mut schema, .. }) => Some(schema),
_ => None,
}
}
}
impl TryFrom<JSONValue> for ReturnType {
type Error = syn::Error;
fn try_from(value: JSONValue) -> Result<Self, syn::Error> {
Ok(match value {
JSONValue::Object(obj) => ReturnType::Explicit(obj.try_into()?),
JSONValue::Expr(ext) => ReturnType::Extern(ext),
})
}
}
impl ReturnType {
fn to_schema(&self, ts: &mut TokenStream) -> Result<(), Error> {
match self {
ReturnType::Explicit(exp) => exp.to_schema(ts)?,
ReturnType::Extern(exp) => exp.to_tokens(ts),
}
Ok(())
}
}
/// A return type in a schema can have an `optional` flag. Other than that it is just a regular
/// schema.
pub struct ReturnType {
pub struct ReturnSchema {
/// If optional, we store `Some(span)`, otherwise `None`.
optional: Option<Span>,
schema: Schema,
}
impl ReturnType {
impl ReturnSchema {
fn to_schema(&self, ts: &mut TokenStream) -> Result<(), Error> {
let optional = match self.optional {
Some(span) => quote_spanned! { span => true },
@ -40,23 +77,15 @@ impl ReturnType {
self.schema.to_schema(&mut out)?;
ts.extend(quote! {
::proxmox::api::router::ReturnType::new( #optional , &#out )
::proxmox_schema::ReturnType::new( #optional , &#out )
});
Ok(())
}
}
impl TryFrom<JSONValue> for ReturnType {
type Error = syn::Error;
fn try_from(value: JSONValue) -> Result<Self, syn::Error> {
Self::try_from(value.into_object("a return type definition")?)
}
}
/// To go from a `JSONObject` to a `ReturnType` we first extract the `optional` flag, then forward
/// To go from a `JSONObject` to a `ReturnSchema` we first extract the `optional` flag, then forward
/// to the `Schema` parser.
impl TryFrom<JSONObject> for ReturnType {
impl TryFrom<JSONObject> for ReturnSchema {
type Error = syn::Error;
fn try_from(mut obj: JSONObject) -> Result<Self, syn::Error> {
@ -80,11 +109,28 @@ impl TryFrom<JSONObject> for ReturnType {
}
}
#[derive(Clone, Copy)]
enum MethodFlavor {
Normal,
Serializing,
Streaming,
}
struct MethodInfo {
input_schema: Schema,
return_type: Option<ReturnType>,
func: syn::ItemFn,
wrapper_ts: TokenStream,
default_consts: TokenStream,
flavor: MethodFlavor,
is_async: bool,
}
/// Parse `input`, `returns` and `protected` attributes out of an function annotated
/// with an `#[api]` attribute and produce a `const ApiMethod` named after the function.
///
/// See the top level macro documentation for a complete example.
pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<TokenStream, Error> {
pub fn handle_method(mut attribs: JSONObject, func: syn::ItemFn) -> Result<TokenStream, Error> {
let input_schema: Schema = match attribs.remove("input") {
Some(input) => input.into_object("input schema definition")?.try_into()?,
None => Schema {
@ -95,7 +141,7 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
},
};
let mut input_schema = if input_schema.as_object().is_some() {
let input_schema = if input_schema.as_object().is_some() {
input_schema
} else {
error!(
@ -108,11 +154,70 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
schema
};
let mut return_type: Option<ReturnType> = attribs
let return_type: Option<ReturnType> = attribs
.remove("returns")
.map(|ret| ret.into_object("return schema definition")?.try_into())
.map(|ret| ret.try_into())
.transpose()?;
/* FIXME: Once the deprecation period is over:
if let Some(streaming) = attribs.remove("streaming") {
error!(
streaming.span(),
"streaming attribute was renamed to 'serializing', as it did not actually stream"
);
}
*/
let streaming: Option<syn::LitBool> = attribs
.remove("streaming")
.map(TryFrom::try_from)
.transpose()?;
let serializing: Option<syn::LitBool> = attribs
.remove("serializing")
.map(TryFrom::try_from)
.transpose()?;
let deprecation_warning = if let Some(streaming) = streaming.clone() {
let deprecation_name = Ident::new(
&format!("attribute_in_{}", func.sig.ident),
streaming.span(),
);
quote! {
mod #deprecation_name {
#[deprecated = "'streaming' attribute is being renamed to 'serializing'"]
fn streaming() {}
fn trigger_deprecation_warning() { streaming() }
}
}
} else {
TokenStream::new()
};
let serializing = streaming
.or(serializing)
.unwrap_or(syn::LitBool::new(false, Span::call_site()));
let streaming: syn::LitBool = attribs
.remove("stream")
.map(TryFrom::try_from)
.transpose()?
.unwrap_or(syn::LitBool::new(false, Span::call_site()));
let mut method_info = MethodInfo {
input_schema,
return_type,
wrapper_ts: TokenStream::new(),
default_consts: TokenStream::new(),
is_async: func.sig.asyncness.is_some(),
flavor: match (serializing.value(), streaming.value()) {
(false, false) => MethodFlavor::Normal,
(true, false) => MethodFlavor::Serializing,
(false, true) => MethodFlavor::Streaming,
(true, true) => {
error!(serializing => "'stream' and 'serializing' attributes are in conflict");
MethodFlavor::Normal
}
},
func,
};
let access_setter = match attribs.remove("access") {
Some(access) => {
let access = Access::try_from(access.into_object("access rules")?)?;
@ -148,28 +253,32 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
);
}
let (doc_comment, doc_span) = util::get_doc_comments(&func.attrs)?;
let (doc_comment, doc_span) = util::get_doc_comments(&method_info.func.attrs)?;
util::derive_descriptions(
&mut input_schema,
return_type.as_mut().map(|rs| &mut rs.schema),
&mut method_info.input_schema,
method_info
.return_type
.as_mut()
.and_then(ReturnType::as_mut_schema),
&doc_comment,
doc_span,
)?;
let mut wrapper_ts = TokenStream::new();
let mut default_consts = TokenStream::new();
let is_async = func.sig.asyncness.is_some();
let api_func_name = handle_function_signature(
&mut input_schema,
&mut return_type,
&mut func,
&mut wrapper_ts,
&mut default_consts,
)?;
let api_func_name = handle_function_signature(&mut method_info)?;
// input schema is done, let's give the method body a chance to extract default parameters:
DefaultParameters(&input_schema).visit_item_fn_mut(&mut func);
DefaultParameters(&method_info.input_schema).visit_item_fn_mut(&mut method_info.func);
let MethodInfo {
input_schema,
func,
wrapper_ts,
default_consts,
return_type,
flavor,
is_async,
..
} = method_info;
let vis = &func.vis;
let func_name = &func.sig.ident;
@ -188,17 +297,32 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
returns_schema_setter = quote! { .returns(#inner) };
}
let api_handler = if is_async {
quote! { ::proxmox::api::ApiHandler::Async(&#api_func_name) }
} else {
quote! { ::proxmox::api::ApiHandler::Sync(&#api_func_name) }
let api_handler = match (flavor, is_async) {
(MethodFlavor::Normal, true) => {
quote! { ::proxmox_router::ApiHandler::Async(&#api_func_name) }
}
(MethodFlavor::Normal, false) => {
quote! { ::proxmox_router::ApiHandler::Sync(&#api_func_name) }
}
(MethodFlavor::Serializing, true) => {
quote! { ::proxmox_router::ApiHandler::SerializingAsync(&#api_func_name) }
}
(MethodFlavor::Serializing, false) => {
quote! { ::proxmox_router::ApiHandler::SerializingSync(&#api_func_name) }
}
(MethodFlavor::Streaming, true) => {
quote! { ::proxmox_router::ApiHandler::StreamAsync(&#api_func_name) }
}
(MethodFlavor::Streaming, false) => {
quote! { ::proxmox_router::ApiHandler::StreamSync(&#api_func_name) }
}
};
Ok(quote_spanned! { func.sig.span() =>
#input_schema_code
#vis const #api_method_name: ::proxmox::api::ApiMethod =
::proxmox::api::ApiMethod::new_full(
#vis const #api_method_name: ::proxmox_router::ApiMethod =
::proxmox_router::ApiMethod::new_full(
&#api_handler,
#input_schema_parameter,
)
@ -212,20 +336,22 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
#wrapper_ts
#func
#deprecation_warning
})
//Ok(quote::quote!(#func))
}
enum ParameterType<'a> {
enum ParameterType {
Value,
ApiMethod,
RpcEnv,
Normal(NormalParameter<'a>),
Normal(NormalParameter),
}
struct NormalParameter<'a> {
ty: &'a syn::Type,
entry: &'a ObjectEntry,
struct NormalParameter {
ty: syn::Type,
entry: ObjectEntry,
}
fn check_input_type(input: &syn::FnArg) -> Result<(&syn::PatType, &syn::PatIdent), syn::Error> {
@ -244,15 +370,8 @@ fn check_input_type(input: &syn::FnArg) -> Result<(&syn::PatType, &syn::PatIdent
Ok((pat_type, pat))
}
fn handle_function_signature(
input_schema: &mut Schema,
return_type: &mut Option<ReturnType>,
func: &mut syn::ItemFn,
wrapper_ts: &mut TokenStream,
default_consts: &mut TokenStream,
) -> Result<Ident, Error> {
let sig = &func.sig;
let is_async = sig.asyncness.is_some();
fn handle_function_signature(method_info: &mut MethodInfo) -> Result<Ident, Error> {
let sig = &method_info.func.sig;
let mut api_method_param = None;
let mut rpc_env_param = None;
@ -270,9 +389,12 @@ fn handle_function_signature(
};
// For any named type which exists on the function signature...
if let Some(entry) = input_schema.find_obj_property_by_ident_mut(&pat.ident.to_string()) {
if let Some(entry) = method_info
.input_schema
.find_obj_property_by_ident_mut(&pat.ident.to_string())
{
// try to infer the type in the schema if it is not specified explicitly:
let is_option = util::infer_type(&mut entry.schema, &*pat_type.ty)?;
let is_option = util::infer_type(&mut entry.schema, &pat_type.ty)?;
let has_default = entry.schema.find_schema_property("default").is_some();
if !is_option && entry.optional.expect_bool() && !has_default {
error!(pat_type => "optional types need a default or be an Option<T>");
@ -318,74 +440,49 @@ fn handle_function_signature(
// bail out with an error.
let pat_ident = pat.ident.unraw();
let mut param_name: FieldName = pat_ident.clone().into();
let param_type =
if let Some(entry) = input_schema.find_obj_property_by_ident(&pat_ident.to_string()) {
if let SchemaItem::Inferred(span) = &entry.schema.item {
bail!(*span, "failed to infer type");
}
param_name = entry.name.clone();
// Found an explicit parameter: extract it:
ParameterType::Normal(NormalParameter {
ty: &pat_type.ty,
entry: &entry,
})
} else if is_api_method_type(&pat_type.ty) {
if api_method_param.is_some() {
error!(pat_type => "multiple ApiMethod parameters found");
continue;
}
api_method_param = Some(param_list.len());
ParameterType::ApiMethod
} else if is_rpc_env_type(&pat_type.ty) {
if rpc_env_param.is_some() {
error!(pat_type => "multiple RpcEnvironment parameters found");
continue;
}
rpc_env_param = Some(param_list.len());
ParameterType::RpcEnv
} else if is_value_type(&pat_type.ty) {
if value_param.is_some() {
error!(pat_type => "multiple additional Value parameters found");
continue;
}
value_param = Some(param_list.len());
ParameterType::Value
} else {
error!(&pat_ident => "unexpected parameter {:?}", pat_ident.to_string());
let param_type = if let Some(entry) = method_info
.input_schema
.find_obj_property_by_ident(&pat_ident.to_string())
{
if let SchemaItem::Inferred(span) = &entry.schema.item {
bail!(*span, "failed to infer type");
}
param_name = entry.name.clone();
// Found an explicit parameter: extract it:
ParameterType::Normal(NormalParameter {
ty: (*pat_type.ty).clone(),
entry: entry.clone(),
})
} else if is_api_method_type(&pat_type.ty) {
if api_method_param.is_some() {
error!(pat_type => "multiple ApiMethod parameters found");
continue;
};
}
api_method_param = Some(param_list.len());
ParameterType::ApiMethod
} else if is_rpc_env_type(&pat_type.ty) {
if rpc_env_param.is_some() {
error!(pat_type => "multiple RpcEnvironment parameters found");
continue;
}
rpc_env_param = Some(param_list.len());
ParameterType::RpcEnv
} else if is_value_type(&pat_type.ty) {
if value_param.is_some() {
error!(pat_type => "multiple additional Value parameters found");
continue;
}
value_param = Some(param_list.len());
ParameterType::Value
} else {
error!(&pat_ident => "unexpected parameter {:?}", pat_ident.to_string());
continue;
};
param_list.push((param_name, param_type));
}
/*
* Doing this is actually unreliable, since we cannot support aliased Result types, or all
* poassible combinations of paths like `result::Result<>` or `std::result::Result<>` or
* `ApiResult`.
// Secondly, take a look at the return type, and then decide what to do:
// If our function has the correct signature we may not even need a wrapper.
if is_default_return_type(&sig.output)
&& (
param_list.len(),
value_param,
api_method_param,
rpc_env_param,
) == (3, Some(0), Some(1), Some(2))
{
return Ok(sig.ident.clone());
}
*/
create_wrapper_function(
input_schema,
return_type,
param_list,
func,
wrapper_ts,
default_consts,
is_async,
)
create_wrapper_function(method_info, param_list)
}
fn is_api_method_type(ty: &syn::Type) -> bool {
@ -435,23 +532,18 @@ fn is_value_type(ty: &syn::Type) -> bool {
}
fn create_wrapper_function(
_input_schema: &Schema,
_returns_schema: &Option<ReturnType>,
method_info: &mut MethodInfo,
param_list: Vec<(FieldName, ParameterType)>,
func: &syn::ItemFn,
wrapper_ts: &mut TokenStream,
default_consts: &mut TokenStream,
is_async: bool,
) -> Result<Ident, Error> {
let api_func_name = Ident::new(
&format!("api_function_{}", &func.sig.ident),
func.sig.ident.span(),
&format!("api_function_{}", &method_info.func.sig.ident),
method_info.func.sig.ident.span(),
);
let mut body = TokenStream::new();
let mut args = TokenStream::new();
let func_uc = func.sig.ident.to_string().to_uppercase();
let func_uc = method_info.func.sig.ident.to_string().to_uppercase();
for (name, param) in param_list {
let span = name.span();
@ -467,61 +559,145 @@ fn create_wrapper_function(
&func_uc,
name,
span,
default_consts,
&mut method_info.default_consts,
)?;
}
}
}
// build the wrapping function:
let func_name = &func.sig.ident;
let func_name = &method_info.func.sig.ident;
let await_keyword = if is_async { Some(quote!(.await)) } else { None };
let await_keyword = if method_info.is_async {
Some(quote!(.await))
} else {
None
};
let question_mark = match func.sig.output {
let question_mark = match method_info.func.sig.output {
syn::ReturnType::Default => None,
_ => Some(quote!(?)),
};
let body = quote! {
if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params {
#body
Ok(::serde_json::to_value(#func_name(#args) #await_keyword #question_mark)?)
} else {
::anyhow::bail!("api function wrapper called with a non-object json value");
let body = match method_info.flavor {
MethodFlavor::Normal => {
quote! {
if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params {
#body
Ok(::serde_json::to_value(#func_name(#args) #await_keyword #question_mark)?)
} else {
::anyhow::bail!("api function wrapper called with a non-object json value");
}
}
}
MethodFlavor::Serializing => {
quote! {
if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params {
#body
let res = #func_name(#args) #await_keyword #question_mark;
let res: ::std::boxed::Box<dyn ::proxmox_router::SerializableReturn + Send> = ::std::boxed::Box::new(res);
Ok(res)
} else {
::anyhow::bail!("api function wrapper called with a non-object json value");
}
}
}
MethodFlavor::Streaming => {
let ty = if method_info.is_async {
quote! { ::proxmox_router::Stream }
} else {
quote! { ::proxmox_router::SyncStream }
};
quote! {
if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params {
#body
let res = #func_name(#args) #await_keyword #question_mark;
let res = #ty::from(res);
Ok(res)
} else {
::anyhow::bail!("api function wrapper called with a non-object json value");
}
}
}
};
if is_async {
wrapper_ts.extend(quote! {
fn #api_func_name<'a>(
mut input_params: ::serde_json::Value,
api_method_param: &'static ::proxmox::api::ApiMethod,
rpc_env_param: &'a mut dyn ::proxmox::api::RpcEnvironment,
) -> ::proxmox::api::ApiFuture<'a> {
//async fn func<'a>(
// mut input_params: ::serde_json::Value,
// api_method_param: &'static ::proxmox::api::ApiMethod,
// rpc_env_param: &'a mut dyn ::proxmox::api::RpcEnvironment,
//) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> {
// #body
//}
//::std::boxed::Box::pin(async move {
// func(input_params, api_method_param, rpc_env_param).await
//})
::std::boxed::Box::pin(async move { #body })
}
});
} else {
wrapper_ts.extend(quote! {
fn #api_func_name(
mut input_params: ::serde_json::Value,
api_method_param: &::proxmox::api::ApiMethod,
rpc_env_param: &mut dyn ::proxmox::api::RpcEnvironment,
) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> {
#body
}
});
match (method_info.flavor, method_info.is_async) {
(MethodFlavor::Normal, true) => {
method_info.wrapper_ts.extend(quote! {
fn #api_func_name<'a>(
mut input_params: ::serde_json::Value,
api_method_param: &'static ::proxmox_router::ApiMethod,
rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
) -> ::proxmox_router::ApiFuture<'a> {
//async fn func<'a>(
// mut input_params: ::serde_json::Value,
// api_method_param: &'static ::proxmox_router::ApiMethod,
// rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
//) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> {
// #body
//}
//::std::boxed::Box::pin(async move {
// func(input_params, api_method_param, rpc_env_param).await
//})
::std::boxed::Box::pin(async move { #body })
}
});
}
(MethodFlavor::Normal, false) => {
method_info.wrapper_ts.extend(quote! {
fn #api_func_name(
mut input_params: ::serde_json::Value,
api_method_param: &::proxmox_router::ApiMethod,
rpc_env_param: &mut dyn ::proxmox_router::RpcEnvironment,
) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> {
#body
}
});
}
(MethodFlavor::Serializing, true) => {
method_info.wrapper_ts.extend(quote! {
fn #api_func_name<'a>(
mut input_params: ::serde_json::Value,
api_method_param: &'static ::proxmox_router::ApiMethod,
rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
) -> ::proxmox_router::SerializingApiFuture<'a> {
::std::boxed::Box::pin(async move { #body })
}
});
}
(MethodFlavor::Serializing, false) => {
method_info.wrapper_ts.extend(quote! {
fn #api_func_name(
mut input_params: ::serde_json::Value,
api_method_param: &::proxmox_router::ApiMethod,
rpc_env_param: &mut dyn ::proxmox_router::RpcEnvironment,
) -> ::std::result::Result<::std::boxed::Box<dyn ::proxmox_router::SerializableReturn + Send>, ::anyhow::Error> {
#body
}
});
}
(MethodFlavor::Streaming, true) => {
method_info.wrapper_ts.extend(quote! {
fn #api_func_name<'a>(
mut input_params: ::serde_json::Value,
api_method_param: &'static ::proxmox_router::ApiMethod,
rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
) -> ::proxmox_router::StreamApiFuture<'a> {
::std::boxed::Box::pin(async move { #body })
}
});
}
(MethodFlavor::Streaming, false) => {
method_info.wrapper_ts.extend(quote! {
fn #api_func_name(
mut input_params: ::serde_json::Value,
api_method_param: &::proxmox_router::ApiMethod,
rpc_env_param: &mut dyn ::proxmox_router::RpcEnvironment,
) -> ::std::result::Result<::proxmox_router::SyncStream, ::anyhow::Error> {
#body
}
});
}
}
Ok(api_func_name)
@ -538,7 +714,7 @@ fn extract_normal_parameter(
) -> Result<(), Error> {
let span = name_span; // renamed during refactorization
let name_str = syn::LitStr::new(name.as_str(), span);
let arg_name = Ident::new(&format!("input_arg_{}", name.as_ident().to_string()), span);
let arg_name = Ident::new(&format!("input_arg_{}", name.as_ident()), span);
let default_value = param.entry.schema.find_schema_property("default");
@ -570,7 +746,7 @@ fn extract_normal_parameter(
});
}
let no_option_type = util::is_option_type(param.ty).is_none();
let no_option_type = util::is_option_type(&param.ty).is_none();
if let Some(def) = &default_value {
let name_uc = name.as_ident().to_string().to_uppercase();
@ -580,8 +756,9 @@ fn extract_normal_parameter(
);
// strip possible Option<> from this type:
let ty = util::is_option_type(param.ty).unwrap_or(param.ty);
let ty = util::is_option_type(&param.ty).unwrap_or(&param.ty);
default_consts.extend(quote_spanned! { span =>
#[allow(dead_code)]
pub const #name: #ty = #def;
});
@ -605,7 +782,7 @@ fn extract_normal_parameter(
body.extend(quote_spanned! { span => ; });
}
Some(flatten_span) => {
// Flattened parameter, we need ot use our special partial-object deserializer.
// Flattened parameter, we need to use our special partial-object deserializer.
// Also note that we do not support simply nesting schemas. We need a referenced type.
// Otherwise the expanded code here gets ugly and we'd need to make sure we pull out
// nested schemas into named variables first... No thanks.
@ -621,7 +798,7 @@ fn extract_normal_parameter(
let ty = param.ty;
body.extend(quote_spanned! { span =>
let #arg_name = <#ty as ::serde::Deserialize>::deserialize(
::proxmox::api::de::ExtractValueDeserializer::try_new(
::proxmox_schema::de::ExtractValueDeserializer::try_new(
input_map,
#schema_ref,
)
@ -674,10 +851,10 @@ fn serialize_input_schema(
input_schema.to_typed_schema(&mut ts)?;
return Ok((
quote_spanned! { func_sig_span =>
pub const #input_schema_name: ::proxmox::api::schema::ObjectSchema = #ts;
pub const #input_schema_name: ::proxmox_schema::ObjectSchema = #ts;
},
quote_spanned! { func_sig_span =>
::proxmox::api::schema::ParameterSchema::Object(&#input_schema_name)
::proxmox_schema::ParameterSchema::Object(&#input_schema_name)
},
));
}
@ -729,7 +906,7 @@ fn serialize_input_schema(
(
quote_spanned!(func_sig_span =>
const #inner_schema_name: ::proxmox::api::schema::Schema = #obj_schema;
const #inner_schema_name: ::proxmox_schema::Schema = #obj_schema;
),
quote_spanned!(func_sig_span => &#inner_schema_name,),
)
@ -742,8 +919,8 @@ fn serialize_input_schema(
quote_spanned! { func_sig_span =>
#inner_schema
pub const #input_schema_name: ::proxmox::api::schema::AllOfSchema =
::proxmox::api::schema::AllOfSchema::new(
pub const #input_schema_name: ::proxmox_schema::AllOfSchema =
::proxmox_schema::AllOfSchema::new(
#description,
&[
#inner_schema_ref
@ -752,14 +929,14 @@ fn serialize_input_schema(
);
},
quote_spanned! { func_sig_span =>
::proxmox::api::schema::ParameterSchema::AllOf(&#input_schema_name)
::proxmox_schema::ParameterSchema::AllOf(&#input_schema_name)
},
))
}
struct DefaultParameters<'a>(&'a Schema);
impl<'a> VisitMut for DefaultParameters<'a> {
impl VisitMut for DefaultParameters<'_> {
fn visit_expr_mut(&mut self, i: &mut syn::Expr) {
if let syn::Expr::Macro(exprmac) = i {
if exprmac.mac.path.is_ident("api_get_default") {
@ -779,7 +956,7 @@ impl<'a> VisitMut for DefaultParameters<'a> {
}
}
impl<'a> DefaultParameters<'a> {
impl DefaultParameters<'_> {
fn get_default(&self, param_tokens: TokenStream) -> Result<syn::Expr, syn::Error> {
let param_name: syn::LitStr = syn::parse2(param_tokens)?;
match self.0.find_obj_property_by_ident(&param_name.value()) {

Some files were not shown because too many files have changed in this diff Show More