Commit Graph

46 Commits

Author SHA1 Message Date
Fabian Grünbichler
df619a8dc2 roles: restrict Permissions.Modify to Administrator
to reduce the chances of accidentally handing out privilege modification
privileges. the old default setup of having Permissions.Modify in PVESysAdmin
and PVEAdmin weakened the distinction between those roles and Administrator.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-06-07 11:13:16 +02:00
Wolfgang Bumiller
9036621e28 tfa: enable lockout of users via tfa.cfg
This will be accompanied by a change in pve-rs to finally
enable this.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-05 12:59:32 +02:00
Wolfgang Bumiller
cfd8636b5e drop support for old login API
The new-format parameter for the ticket call is now ignored
and assumed 1.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-06-05 08:53:24 +02:00
Wolfgang Bumiller
d9f02efe49 use TFA authentication api v2
Previously `authentication_verify` just `die`d on error and
would only return a boolean whether `priv/tfa.cfg` needs
updating as a positive result.

Since we want to support locking TOTP as well as a general
TFA lock-out via the config, we also want to be able to tell
when this occurs. Most of it is handled by the TFA rust
crate already, but notifying users needs to be done on this
end instead.

In pve-rs we now have a different API for this:
`authentication_verify2`, which, instead of die()ing on
errors, always returns a hash containing the result as well
as the flags 'tfa-limit-reached' and 'totp-limit-reached'
which, if set, tell us to notify the user.

However, doing so will introduce new fields in the
`priv/tfa.cfg` in a struct marked as `deny_unknown_fields`,
so in a cluster, the limits & notification handling should
only be done once we can be sure that all nodes are up to
date.

These fields are only introduced on login errors, so for
now, handle a failed result early without saving
`priv/tfa.cfg`.
The only case where saving the file was previously required
was when *successfully* logging in with a recovery key, by
which we cannot be reaching a limit, so this should still be
safe.

Once we can validate that all cluster nodes are up to date,
we can implement the notification system.
A commented-out code structure for this is included in this
patch.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2023-05-15 09:06:28 +02:00
Fabian Grünbichler
170cf17bf7 fix #4518: improve ACL computation performance
by switching to a tree-based in-memory structure, like we do in PBS.

instead of parsing ACL entries into a hash using the full ACL path as key for
each entry, parse them into a tree-like nested hash. when evaluating ACLs,
iterating over all path prefixes starting at '/' is needed anyway, so this is a
more natural way to store and access the parsed configuration.

some performance data, timing `pveum user permissions $user > /dev/null` for
various amounts of ACL entries in user.cfg

entries | stock  | patched  | speedup
-------------------------------------
     1k | 1.234s |   0.241s |    5.12
     2k | 4.480s |   0.262s |   17.09
    20k |  7m25s |   0.987s |  450.86

similarly, an /access/ticket request such as the one happening on login goes
down from 4.27s to 109ms with 2k entries (testing with 20k entries fails
because the request times out after 30s, but with the patch it takes 336ms).

the underlying issue is that these two code paths not only iterate over *all
defined ACL paths* to get a complete picture of a user's/token's privileges,
but the fact that that ACL computation for each checked path itself did another
such loop in PVE::AccessControl::roles().

it is enough to iterate over the to-be-checked ACL path in a component-wise
fashion in order to handle role propagation, e.g., when looking at /a/b/c/d,
iterate over

/
/a
/a/b
/a/b/c
/a/b/c/d

in turn instead of all defined ACL paths.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2023-03-06 10:37:51 +01:00
Fabian Grünbichler
881dce13d5 privs: add Sys.Incoming
for guarding cross-cluster data streams like guest migrations and
storage migrations.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-11-07 16:38:21 +01:00
Dominik Csapak
965b2418ee authenticate_user: pass undef instead of empty $tfa_challenge to authenticate_2nd_new
just above, we check & return if $tfa_challenge is set, so there is no
way that it would be set here. To make it clearer that it must be undef
here pass it as such.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-10-21 10:44:08 +02:00
Dominik Csapak
61565fb2c5 authenticate_2nd_new: rename $otp to $tfa_response
since that is what it really is, not only a otp

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-10-21 10:44:06 +02:00
Dominik Csapak
6809537127 authenticate_2nd_new: only lock tfa config for recovery keys
we currently only need to lock the tfa config when we got a recovery key
as a tfa challenge response, since that's the only thing that can
actually change the tfa config (every other method only reads from
there).

so to do that, factor out the code that was inside the lock, and call it
with/without lock depending on the tfa challenge response

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2022-10-21 10:44:03 +02:00
Wolfgang Bumiller
28ec897247 tfa: pass whole webauthn config to 'set_webauthn_config'
the field names are being kept compatible

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2022-07-25 13:52:58 +02:00
Fabian Grünbichler
a74d5080b3 auth key: fix double rotation in clusters
there is a (hard to trigger) race that can cause a double rotation of
the auth key, with potentially confusing fallout (various processes on
different nodes having an inconsistent view of the current and previous
auth keys, resulting in "random" invalid ticket errors until the next
proper key rotation 24h later).

the underlying cause is that `stat()` calls are excempt from our
otherwise non-cached/direct_io handling of pmxcfs I/O, which allows the
following sequence of events to take place:

LAST: mtime of current auth key

- current epoch advances to LAST + 24h

the following can be arbitrarly interleaved between node A and B:
- LAST+24h node A: pvedaemon/pvestatd on node A calls check_authkey(1)
- LAST+24h node A: it returns 0 (rotation required)
- LAST+24h node A: rotate_key() is called
- LAST+24h node A: cfs_lock_authkey is called
- LAST+24h node B: pvedaemon/pvestatd calls check_authkey(1)
- LAST+24h node B: key is not yet cached in-memory by current process
- LAST+24h node B: key file is opened, stat-ed, read, parsed, and content+mtime
  is cached (the kernel will now cache this stat result for 1s unless
  the path is opened)
- LAST+24h node B: it returns 0 (rotation required)
- LAST+24h node B: rotate_key() is called
- LAST+24h node B: cfs_lock_authkey is called

the following is mutex-ed via a cfs_lock:
- LAST+24h node A: lock is obtained
- LAST+24h node A: check_authkey() is called
- LAST+24h node A: key is stat-ed, mtime is still (correctly) LAST,
  cached mtime and content are returned
- LAST+24h node A: it returns 0 (rotation still required)
- LAST+24h node A: get_pubkey() is called and returns current auth key
- LAST+24h node A: new keypair is generated and persisted
- LAST+24h node A: cfs_lock is released
- LAST+24h node B: changes by node A are processed by pmxcfs
- LAST+24h node B: lock is obtained
- LAST+24h node B: check_authkey() is called
- LAST+24h node B: key is stat-ed, mtime is (incorrectly!) still LAST
  since the stat call is handled by the kernel/page cache, not by
  pmxcfs, cached mtime and content are returned
- LAST+24h node B: it returns 0 (rotation still required)
- LAST+24h node B: get_pubkey() is called and returns either previous or
  key written by node A (depending on whether page cache or pmxcfs
  answers stat call)
- LAST+24h node B: new keypair is generated, key returned by last
  get_pubkey call is written as old key

the end result is that some nodes and process will treat the key
generated by node A as "current", while others will treat the one
generated by nodoe B as "current". both have the same mtime, so the
in-memory cache hash won't be updated unless the service is restarted or
another rotation happens. depending on who generated the ticket and who
attempts validating it, a ticket might be rejected as invalid even
though the generating party would treat it as valid, and time on all
nodes is properly synced.

there seems to be now way for pmxcfs to pro-actively invalidate the page
cache entry safely (since we'd need to do so while writes to the same
path can happen concurrently), so work around by forcing an open/close
at the (stat) call site which does the work for us. regular reads are
not affected since those already bypass the page cache entirely anyway.

thankfully in almost all cases, the following sequence has enough
synchronization overhead baked in to avoid triggering the issue almost
entirely:

- cfs_lock
- generate key
- create tmp file for old key
- write tmp file
- rename tmp file into proper place
- create tmp file for new pub key
- write tmp file
- rename tmp file into proper place
- create tmp file for new priv key
- write tmp file
- rename tmp file into proper place
- release lock

that being said, there has been at least one report where this was
triggered in the wild from time to time.

it is easy to reproduce by increasing the attr_timeout and entry_timeout
fuse settings inside pmxcfs to increase the time stat results are
treated as valid/retained in the page cache:

-----8<-----
 diff --git a/data/src/pmxcfs.c b/data/src/pmxcfs.c
 index d78a248..e3e807b 100644
 --- a/data/src/pmxcfs.c
 +++ b/data/src/pmxcfs.c
 @@ -935,7 +935,7 @@ int main(int argc, char *argv[])

  	mkdir(CFSDIR, 0755);

 -	char *fa[] = { "-f", "-odefault_permissions", "-oallow_other", NULL};
 +	char *fa[] = { "-f", "-odefault_permissions", "-oallow_other", "-oentry_timeout=5", "-oattr_timeout=5", NULL};

  	struct fuse_args fuse_args = FUSE_ARGS_INIT(sizeof (fa)/sizeof(gpointer) - 1, fa);

----->8-----

in which case it's even easy to trigger more than double rotation in a
bigger test cluster (stopping all PVE services except for pve-cluster
helps avoiding interference):

on a single node:
$ touch --date yesterday /etc/pve/authkey.pub

in parallel (i.e., via tmux synchronized panes):
-----8<-----
use strict;
use warnings;
use PVE::Cluster;
use PVE::AccessControl;
PVE::Cluster::cfs_update();

# ensure page cache entry is there
PVE::AccessControl::check_authkey(1);
PVE::AccessControl::check_authkey(1);
# now attempt rotation
PVE::AccessControl::rotate_authkey();
----->8-----

Thanks to Wolfgang Bumiller for assistance in triaging and exploring
various avenues of fixing.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-07-14 07:39:05 +02:00
Fabian Grünbichler
37d3c16b25 perm check: forbid undefined/empty ACL path
to detect similar issues to that fixed in the previous commit early on
and without the potential for dangerous side-effects.

root@pam is intentionally still allowed before the check in case such
situations can be triggered by misconfiguration - root@pam can then
still clean up the affected configs via the GUI/API, and not just via
manual editing.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2022-06-20 15:47:03 +02:00
Thomas Lamprecht
7d23b7cac8 tree wide: typo fixes
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-06-03 13:58:07 +02:00
Thomas Lamprecht
aaacf4c311 access check: include user/token id in expired exception
not that relevant for the user as the daemon auth log already
contains that info, but for token it can be nice.

The API response is always just a plain "401 auth failure" in any
case (expired or wrong creds)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-31 13:37:51 +02:00
Thomas Lamprecht
5ff516de53 check user: re-order enable check first again and adhere noerr flag
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-31 13:31:23 +02:00
Oguz Bektas
d0cce79f8b user check: fix expiration/enable order
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2022-05-31 13:22:17 +02:00
Wolfgang Bumiller
280d0edd2c fix #3768: warn on bad u2f or webauthn settings
but don't bail out of the entire auth process, otherwise
not even totp or recovery keys will work anymore in this
case

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-06 13:55:14 +01:00
Wolfgang Bumiller
7262f24391 fix a 'use of undefined...' warning
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-12-06 13:55:14 +01:00
Wolfgang Bumiller
d12f247edc fill origin into webauthn config if not provided
in order to allow subdomains to work, the wa config should
only specify 'id' and 'rp', the 'origin' gets filled in by
the node

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-22 13:55:21 +01:00
Wolfgang Bumiller
93c1d74a62 catch incompatible tfa entries with a nice error
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-17 12:52:56 +01:00
Fabian Grünbichler
06b4a3b381 ticket: normalize path for verification
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-11 16:19:08 +01:00
Fabian Grünbichler
3760a33cc8 tickets: add tunnel ticket
just like VNC ticket, but different prefix to prevent confusion.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2021-11-11 16:19:08 +01:00
Thomas Lamprecht
6c6a9ce00f tfa: upgrade check: early return if no cluster members
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-11 12:30:02 +01:00
Thomas Lamprecht
b649f3b40e tfa: upgrade check: more info in error message(s)
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-11 12:27:58 +01:00
Thomas Lamprecht
9f34502077 tfa: upgrade check: be less strict about version format
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-11 12:05:57 +01:00
Wolfgang Bumiller
72f4c73feb implement version checks for tfa
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-11 11:56:34 +01:00
Wolfgang Bumiller
0fe62fa87f merge old user.cfg keys to tfa config when adding entries
this happens when the first new tfa entry is added and the
'keys' entry is replaced by "x"

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-10 14:09:49 +01:00
Wolfgang Bumiller
a0374ad0ee check enforced realm tfa type in new auth
this way we could also add webauthn as a required tfa type
to the realm configs later on

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 13:52:40 +01:00
Wolfgang Bumiller
2ee46655a5 assert tfa/user config lock order
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 13:52:40 +01:00
Wolfgang Bumiller
d168ab34d6 update tfa cleanup when deleting users
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Wolfgang Bumiller
8c1e3ab3e9 support registering yubico otp keys
In PBS we don't support this, so the current TFA API in rust
does not support this either (although the config does know
about its *existence*).

For now, yubico authentication will be done in perl. Adding
it to rust the rust TFA crate would not make much sense
anyway as we'd likely not want to use the same http client
crate in pve and pbs anyway (since pve is all blocking code
and pbs is async...)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Wolfgang Bumiller
07692c72ff add pbs-style TFA API implementation
implements the same api paths as in pbs by forwarding the
api methods to the rust implementation after performing the
product-specific checks

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Wolfgang Bumiller
dc547a1339 move TFA api path into its own module
and remove old modification api

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Wolfgang Bumiller
6adcb18c01 handle yubico authentication in new path
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Wolfgang Bumiller
afb10353d1 use PBS-like auth api call flow
The main difference here is that we have no separate api
path for verification. Instead, the ticket api call gets an
optional 'tfa-challenge' parameter.

This is opt-in: old pve-manager UI with new
pve-access-control will still work as expected, but users
won't be able to *update* their TFA settings.

Since the new tfa config parser will build a compatible
in-perl tfa config object as well, the old authentication
code is left unchanged for compatibility and will be removed
with pve-8, where the `new-format` parameter in the ticket
call will change its default to `1`.

The `change_tfa` call will simply die in this commit. It is
removed later when adding the pbs-style TFA API calls.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Wolfgang Bumiller
57098eb8fb use rust parser for TFA config
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-10 11:13:21 +01:00
Dominik Csapak
4aa4f0b3d7 fix user deletion when realm does not enforce TFA
here the existance of the user is only interesting if we want to set
data, not if we delete it.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-10-21 12:27:42 +02:00
Thomas Lamprecht
8ecf1a490d fix #2302: allow deletion of users when realm enforces TFA
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-09-27 15:32:05 +02:00
Alexandre Derumier
4100ba8d65 check_path: add /sdn/vnets/* path
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
2021-08-23 18:19:15 +02:00
Dietmar Maurer
52d1c1b966 add OpenId configuration 2021-07-01 13:13:59 +02:00
Dietmar Maurer
8a724f7b3a check_user_enabled: also check if user is expired 2021-07-01 13:13:59 +02:00
Thomas Lamprecht
d7e8d24ef5 access control: style: register configs in single line each
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-06-17 14:41:02 +02:00
Alexandre Derumier
0a06acb128 check_path : add sdn zone path
This was missing in commit20c60513b2a6b2d7c7aae0dcc0391889b9cb7ecf,
so user can't assign permisson on a zone currently

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-06-17 13:15:36 +02:00
Dominik Csapak
8737ff3718 add missing paths in check_path
* /access/realm/<realm>
* /access/groups/<group>

were overlooked when fixing #1500

see: https://forum.proxmox.com/threads/are-group-acls-broken-in-v6-4.91000/

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2021-06-16 16:19:33 +02:00
Lorenz Stechauner
6d048ad6fc fix #3402: add Pool.Audit permission
add new user "PVEPoolUser" and add Pool.Audit to "PVEAuditor".

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
2021-06-01 10:31:16 +02:00
Thomas Lamprecht
197d1016fd buildsys: split packaging and source build-systems
Much nicer to handle and work with than entangling all together in a
single spaghetti pile.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-05-09 19:48:50 +02:00