my $pve_auth_key_cache = {};
my $ticket_lifetime = 3600 * 2; # 2 hours
+my $auth_graceperiod = 60 * 5; # 5 minutes
my $authkey_lifetime = 3600 * 24; # rotate every 24 hours
Crypt::OpenSSL::RSA->import_random_seed();
warn "auth key pair missing, generating new one..\n" if !$quiet;
return 0;
} else {
- if (time() - $mtime >= $authkey_lifetime) {
+ my $now = time();
+ if ($now - $mtime >= $authkey_lifetime) {
warn "auth key pair too old, rotating..\n" if !$quiet;;
return 0;
+ } elsif ($mtime > $now + $auth_graceperiod) {
+ # a nodes RTC had a time set in the future during key generation -> ticket
+ # validity is clamped to 0+5 min grace period until now >= mtime again
+ my (undef, $old_mtime) = get_pubkey(1);
+ if ($old_mtime && $mtime >= $old_mtime && $mtime - $old_mtime < $ticket_lifetime) {
+ warn "auth key pair generated in the future (key $mtime > host $now),"
+ ." but old key still exists and in valid grace period so avoid automatic"
+ ." fixup. Cluster time not in sync?\n" if !$quiet;
+ return 1;
+ }
+ warn "auth key pair generated in the future (key $mtime > host $now), rotating..\n" if !$quiet;
+ return 0;
} else {
warn "auth key new enough, skipping rotation\n" if !$quiet;;
return 1;
}
return PVE::Ticket::verify_csrf_prevention_token(
- $secret, $username, $token, -300, $ticket_lifetime, $noerr);
+ $secret, $username, $token, -$auth_graceperiod, $ticket_lifetime, $noerr);
}
my $get_ticket_age_range = sub {
my $key_age = $now - $mtime;
$key_age = 0 if $key_age < 0;
- my $min = -300;
+ my $min = -$auth_graceperiod;
my $max = $ticket_lifetime;
if ($rotated) {
# ticket creation after rotation is not allowed
- $min = $key_age - 300;
+ $min = $key_age - $auth_graceperiod;
} else {
if ($key_age > $authkey_lifetime && $authkey_lifetime > 0) {
if (PVE::Cluster::check_cfs_quorum(1)) {
}
}
- $max = $key_age + 300 if $key_age < $ticket_lifetime;
+ $max = $key_age + $auth_graceperiod if $key_age < $ticket_lifetime;
}
return undef if $min > $ticket_lifetime;
],
user => [
'VM.Config.CDROM', # change CDROM media
+ 'VM.Config.Cloudinit',
'VM.Console',
'VM.Backup',
'VM.PowerMgmt',
}
}
+sub lookup_username {
+ my ($username, $noerr) = @_;
+
+ $username =~ m!^(${PVE::Auth::Plugin::user_regex})\@(${PVE::Auth::Plugin::realm_regex})$!;
+
+ my $realm = $2;
+ my $domain_cfg = cfs_read_file("domains.cfg");
+ my $casesensitive = $domain_cfg->{ids}->{$realm}->{'case-sensitive'} // 1;
+ my $usercfg = cfs_read_file('user.cfg');
+
+ if (!$casesensitive) {
+ my @matches = grep { lc $username eq lc $_ } (keys %{$usercfg->{users}});
+
+ die "ambiguous case insensitive match of username '$username', cannot safely grant access!\n"
+ if scalar @matches > 1 && !$noerr;
+
+ return $matches[0]
+ }
+
+ return $username;
+}
+
sub normalize_path {
my $path = shift;
if ($cfg->{users}->{$user}) { # user exists
$cfg->{users}->{$user}->{groups}->{$group} = 1;
- $cfg->{groups}->{$group}->{users}->{$user} = 1;
} else {
warn "user config - ignore invalid group member '$user'\n";
}
+ $cfg->{groups}->{$group}->{users}->{$user} = 1;
}
} elsif ($et eq 'role') {
next;
}
+ if (!$cfg->{roles}->{$role}) {
+ warn "user config - ignore invalid acl role '$role'\n";
+ next;
+ }
+
foreach my $ug (split_list($uglist)) {
my ($group) = $ug =~ m/^@(\S+)$/;
if ($group && verify_groupname($group, 1)) {
- if ($cfg->{groups}->{$group}) { # group exists
- $cfg->{acl}->{$path}->{groups}->{$group}->{$role} = $propagate;
- } else {
+ if (!$cfg->{groups}->{$group}) { # group does not exist
warn "user config - ignore invalid acl group '$group'\n";
}
+ $cfg->{acl}->{$path}->{groups}->{$group}->{$role} = $propagate;
} elsif (PVE::Auth::Plugin::verify_username($ug, 1)) {
- if ($cfg->{users}->{$ug}) { # user exists
- $cfg->{acl}->{$path}->{users}->{$ug}->{$role} = $propagate;
- } else {
+ if (!$cfg->{users}->{$ug}) { # user does not exist
warn "user config - ignore invalid acl member '$ug'\n";
}
+ $cfg->{acl}->{$path}->{users}->{$ug}->{$role} = $propagate;
} elsif (my ($user, $token) = split_tokenid($ug, 1)) {
if (check_token_exist($cfg, $user, $token, 1)) {
$cfg->{acl}->{$path}->{tokens}->{$ug}->{$role} = $propagate;