2020-10-16 00:32:37 +08:00
|
|
|
#! /usr/bin/perl -T
|
|
|
|
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
|
|
|
|
# NEEDGROUP osh-accountCreate
|
|
|
|
# SUDOERS %osh-accountCreate ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountCreate --type normal *
|
|
|
|
# FILEMODE 0700
|
2020-11-17 18:12:53 +08:00
|
|
|
# FILEOWN 0 0
|
2020-10-16 00:32:37 +08:00
|
|
|
|
|
|
|
#>HEADER
|
|
|
|
use common::sense;
|
2021-12-09 20:54:33 +08:00
|
|
|
use Getopt::Long qw(:config no_auto_abbrev no_ignore_case);
|
2020-10-16 00:32:37 +08:00
|
|
|
use Sys::Hostname ();
|
2021-07-16 00:36:13 +08:00
|
|
|
use JSON;
|
|
|
|
use POSIX ();
|
2020-10-16 00:32:37 +08:00
|
|
|
|
|
|
|
use File::Basename;
|
|
|
|
use lib dirname(__FILE__) . '/../../lib/perl';
|
|
|
|
use OVH::Bastion;
|
2021-11-30 19:20:28 +08:00
|
|
|
use OVH::Bastion::Helper;
|
2020-10-16 00:32:37 +08:00
|
|
|
|
|
|
|
# Fetch command options
|
|
|
|
my $fnret;
|
|
|
|
my ($result, @optwarns);
|
2022-06-30 21:00:29 +08:00
|
|
|
my (
|
|
|
|
$forceKeyFrom, $type, $account, $realmFrom, $uid,
|
|
|
|
@pubKeys, $comment, $alwaysActive, $uidAuto, $oshOnly,
|
|
|
|
$maxInactiveDays, $immutableKey, $ttl
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
eval {
|
|
|
|
local $SIG{__WARN__} = sub { push @optwarns, shift };
|
|
|
|
$result = GetOptions(
|
2022-06-17 21:40:39 +08:00
|
|
|
"force-key-from=s" => sub { $forceKeyFrom //= $_[1] }, # only to be used by the install script
|
2021-09-06 18:29:05 +08:00
|
|
|
"type=s" => sub { $type //= $_[1] },
|
|
|
|
"from=s" => sub { $realmFrom //= $_[1] },
|
|
|
|
"uid=s" => sub { $uid //= $_[1] },
|
|
|
|
"account=s" => sub { $account //= $_[1] },
|
|
|
|
"always-active" => sub { $alwaysActive //= $_[1] },
|
|
|
|
"pubKey=s" => \@pubKeys,
|
|
|
|
"comment=s" => sub { $comment //= $_[1] },
|
|
|
|
'uid-auto' => sub { $uidAuto //= $_[1] },
|
|
|
|
'osh-only' => sub { $oshOnly //= $_[1] },
|
|
|
|
'max-inactive-days=i' => sub { $maxInactiveDays //= $_[1] },
|
|
|
|
'immutable-key' => sub { $immutableKey //= $_[1] },
|
|
|
|
'ttl=i' => sub { $ttl //= $_[1] },
|
2020-10-16 00:32:37 +08:00
|
|
|
);
|
|
|
|
};
|
|
|
|
if ($@) { die $@ }
|
|
|
|
|
2021-12-16 01:11:03 +08:00
|
|
|
OVH::Bastion::Helper::check_spurious_args();
|
|
|
|
|
2020-10-16 00:32:37 +08:00
|
|
|
if (!$result) {
|
|
|
|
local $" = ", ";
|
|
|
|
HEXIT('ERR_BAD_OPTIONS', msg => "Error parsing options: @optwarns");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$account || !$type) {
|
|
|
|
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'account' or 'type'");
|
|
|
|
}
|
|
|
|
|
|
|
|
#<HEADER
|
|
|
|
|
|
|
|
#>PARAMS:TYPE
|
|
|
|
osh_debug("Checking type");
|
|
|
|
if (not grep { $type eq $_ } qw{ normal realm }) {
|
|
|
|
HEXIT('ERR_INVALID_PARAMETER', "Expected type 'normal' or 'realm'");
|
|
|
|
}
|
|
|
|
|
|
|
|
#<PARAMS:TYPE
|
|
|
|
|
|
|
|
#>PARAMS:ACCOUNT
|
|
|
|
osh_debug("Checking account");
|
|
|
|
$fnret = OVH::Bastion::is_account_valid(account => $account);
|
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
# get returned untainted value
|
|
|
|
$account = $fnret->value->{'account'};
|
|
|
|
|
|
|
|
$fnret = OVH::Bastion::is_account_existing(account => $account);
|
|
|
|
$fnret->is_err and HEXIT($fnret);
|
|
|
|
$fnret->is_ok and HEXIT('KO_ALREADY_EXISTING', msg => "The account $account already exists");
|
|
|
|
|
|
|
|
$fnret = OVH::Bastion::is_group_existing(group => $account);
|
|
|
|
$fnret->is_err and HEXIT($fnret);
|
|
|
|
$fnret->is_ok and HEXIT('KO_ALREADY_EXISTING', msg => "The group $account already exists");
|
|
|
|
|
|
|
|
if ($type eq 'realm') {
|
|
|
|
$account = "realm_$account";
|
2020-11-23 05:05:45 +08:00
|
|
|
$fnret = OVH::Bastion::is_account_valid(account => $account, accountType => "realm");
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
$fnret = OVH::Bastion::is_account_existing(account => $account, accountType => "realm");
|
|
|
|
$fnret->is_err and HEXIT($fnret);
|
|
|
|
$fnret->is_ok and HEXIT('KO_ALREADY_EXISTING', msg => "The realm $account already exists");
|
|
|
|
|
|
|
|
$fnret = OVH::Bastion::is_group_existing(group => $account);
|
|
|
|
$fnret->is_err and HEXIT($fnret);
|
|
|
|
$fnret->is_ok and HEXIT('KO_ALREADY_EXISTING', msg => "The group $account already exists");
|
|
|
|
}
|
|
|
|
|
|
|
|
#<PARAMS:ACCOUNT
|
|
|
|
|
|
|
|
#>PARAMS:UID
|
|
|
|
if (not $uidAuto and not defined $uid) {
|
|
|
|
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing one of 'uid-auto' or 'uid' argument");
|
|
|
|
}
|
|
|
|
if (defined $uid and $uidAuto) {
|
|
|
|
HEXIT('ERR_INCOMPATIBLE_PARAMETERS', msg => "Incompatible parameters 'uid' and 'uid-auto' specified");
|
|
|
|
}
|
|
|
|
if (defined $uid) {
|
|
|
|
$fnret = OVH::Bastion::is_valid_uid(uid => $uid, type => 'user');
|
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
$uid = $fnret->value;
|
|
|
|
getpwuid($uid) and HEXIT('ERR_UID_COLLISION', msg => "This UID ($uid) is already taken");
|
|
|
|
|
|
|
|
$fnret = OVH::Bastion::is_valid_uid(uid => $uid, type => 'group');
|
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
getgrgid($uid) and HEXIT('ERR_GID_COLLISION', msg => "This GID ($uid) is already taken");
|
|
|
|
}
|
|
|
|
elsif ($uidAuto) {
|
2021-07-13 19:42:28 +08:00
|
|
|
$fnret = OVH::Bastion::get_next_available_uid(available_gid => 1, available_gid_ttyrec => 1);
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
$uid = $fnret->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
#<PARAMS:UID
|
|
|
|
|
2021-09-06 18:29:05 +08:00
|
|
|
if (defined $maxInactiveDays && $maxInactiveDays < 0) {
|
|
|
|
HEXIT('ERR_INVALID_PARAMETER', msg => "Expected a >= 0 amount of days for --max-inactive-days");
|
|
|
|
}
|
|
|
|
|
2020-10-16 00:32:37 +08:00
|
|
|
#>PARAMS
|
|
|
|
my $ttygroup = "$account-tty";
|
|
|
|
$fnret = OVH::Bastion::is_group_existing(group => $ttygroup);
|
|
|
|
$fnret and HEXIT('ERR_TTY_GROUP_ALREADY_EXISTS', msg => "The TTY group for this account ($ttygroup) already exists!");
|
|
|
|
|
|
|
|
#<PARAMS
|
|
|
|
|
|
|
|
#>RIGHTSCHECK
|
|
|
|
if ($self eq 'root') {
|
|
|
|
osh_debug "Real root, skipping checks of permissions";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
# need to perform another security check
|
|
|
|
if ($type eq 'realm') {
|
|
|
|
$fnret = OVH::Bastion::is_user_in_group(user => $self, group => "osh-realmCreate");
|
|
|
|
$fnret or HEXIT('ERR_SECURITY_VIOLATION', msg => "You're not allowed to run this, dear $self");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$fnret = OVH::Bastion::is_user_in_group(user => $self, group => "osh-accountCreate");
|
|
|
|
$fnret or HEXIT('ERR_SECURITY_VIOLATION', msg => "You're not allowed to run this, dear $self");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#<RIGHTSCHECK
|
|
|
|
|
|
|
|
#>CODE
|
|
|
|
$fnret = OVH::Bastion::load_configuration();
|
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
my $config = $fnret->value;
|
|
|
|
|
|
|
|
my $ttygid = $uid + $config->{'ttyrecGroupIdOffset'};
|
|
|
|
getgrgid($ttygid) and HEXIT('ERR_GID_COLLISION', msg => "This GID ($ttygid) is already taken");
|
|
|
|
|
|
|
|
if ($uid < $config->{'accountUidMin'} or $uid > $config->{'accountUidMax'}) {
|
2022-06-30 21:00:29 +08:00
|
|
|
HEXIT('ERR_UID_INVALID_RANGE',
|
|
|
|
msg => "UID must be < " . $config->{'accountUidMin'} . " and > " . $config->{'accountUidMax'});
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
my @vettedKeys;
|
|
|
|
foreach my $key (@pubKeys) {
|
|
|
|
$fnret = OVH::Bastion::is_valid_public_key(pubKey => $key, way => 'ingress');
|
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
$key = $fnret->value->{'typecode'} . ' ' . $fnret->value->{'base64'};
|
|
|
|
if ($fnret->value->{'comment'}) {
|
|
|
|
$key .= ' ' . $fnret->value->{'comment'};
|
|
|
|
}
|
|
|
|
push @vettedKeys, $key;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $prefix = $fnret->value->{'prefix'};
|
|
|
|
my @userProvidedIpList = ();
|
|
|
|
if ($prefix) {
|
|
|
|
my ($fromString) = $prefix =~ m{from=["']([^"']+)["']};
|
|
|
|
if ($fromString) {
|
|
|
|
@userProvidedIpList = split /,/, $fromString;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::get_from_for_user_key(
|
|
|
|
userProvidedIpList => \@userProvidedIpList,
|
|
|
|
forcedList => ($forceKeyFrom ? [$forceKeyFrom] : [])
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
my $from = $fnret->value->{'from'};
|
|
|
|
my $ipList = $fnret->value->{'ipList'};
|
|
|
|
my $homedir = "/home/$account";
|
|
|
|
|
|
|
|
osh_info "Creating group $account with GID $uid...";
|
|
|
|
$fnret = OVH::Bastion::sys_groupadd(noisy_stderr => 1, gid => $uid, group => $account);
|
|
|
|
$fnret->err eq 'OK'
|
2022-06-30 21:00:29 +08:00
|
|
|
or HEXIT('ERR_GROUPADD_FAILED',
|
|
|
|
msg => "Error while running groupadd with UID $uid and group $account (" . $fnret->msg . ")");
|
2020-10-16 00:32:37 +08:00
|
|
|
osh_debug('ok, group was created');
|
|
|
|
|
|
|
|
osh_info "Creating user $account with UID $uid...";
|
|
|
|
$fnret = OVH::Bastion::sys_useradd(
|
|
|
|
noisy_stderr => 1,
|
|
|
|
user => $account,
|
|
|
|
uid => $uid,
|
|
|
|
gid => $uid,
|
|
|
|
shell => $OVH::Bastion::BASEPATH . '/bin/shell/osh.pl',
|
|
|
|
home => $homedir
|
|
|
|
);
|
|
|
|
$fnret->err eq 'OK'
|
|
|
|
or HEXIT('ERR_USERADD_FAILED', msg => "Error while running useradd for $account UID/GID $uid (" . $fnret->msg . ")");
|
|
|
|
osh_debug('user created');
|
|
|
|
|
|
|
|
chmod 0750, $homedir;
|
|
|
|
|
|
|
|
mkdir $homedir . "/.ssh" if (!-d "$homedir/.ssh");
|
|
|
|
chmod 0750, $homedir . "/.ssh";
|
|
|
|
chown $uid, $uid, "$homedir/.ssh";
|
|
|
|
|
2020-11-26 18:40:14 +08:00
|
|
|
my $akfile = $homedir . '/' . OVH::Bastion::AK_FILE;
|
|
|
|
if (!OVH::Bastion::touch_file($akfile)) {
|
2020-10-16 00:32:37 +08:00
|
|
|
HEXIT('ERR_CANNOT_CREATE_FILE', msg => "Failed to create authorized_keys file");
|
|
|
|
}
|
2020-11-26 18:40:14 +08:00
|
|
|
chmod 0640, $akfile;
|
|
|
|
chown $uid, $uid, $akfile;
|
2020-10-16 00:32:37 +08:00
|
|
|
|
|
|
|
osh_info "Creating tty group of account...";
|
|
|
|
$fnret = OVH::Bastion::sys_groupadd(noisy_stderr => 1, group => $ttygroup, gid => $ttygid);
|
|
|
|
$fnret->err eq 'OK'
|
2022-06-30 21:00:29 +08:00
|
|
|
or HEXIT('ERR_GROUPADD_FAILED',
|
|
|
|
msg => "Error while running groupadd with UID $ttygid and group $ttygroup (" . $fnret->msg . ")");
|
2020-10-16 00:32:37 +08:00
|
|
|
osh_debug('ok, group was created');
|
|
|
|
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::add_user_to_group(
|
|
|
|
user => $account,
|
|
|
|
group => $ttygroup,
|
|
|
|
groupType => 'tty',
|
|
|
|
accountType => ($type eq 'realm' ? 'realm' : 'normal')
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
# adding account to bastion-users group
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::add_user_to_group(
|
|
|
|
user => $account,
|
|
|
|
group => "bastion-users",
|
|
|
|
accountType => ($type eq 'realm' ? 'realm' : 'normal')
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
if ($type ne 'realm') {
|
|
|
|
osh_info "Adding account to potential supplementary groups...";
|
|
|
|
if ($config->{'accountCreateSupplementaryGroups'}) {
|
|
|
|
foreach my $suppGroup (@{$config->{'accountCreateSupplementaryGroups'}}) {
|
|
|
|
$fnret = OVH::Bastion::add_user_to_group(user => $account, group => $suppGroup, groupType => 'osh');
|
|
|
|
if ($fnret) {
|
|
|
|
osh_info "Account added to group $suppGroup";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
osh_warn "Couldn't add account $account to group $suppGroup";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
osh_info "Creating needed files and directories with proper permissions in home...";
|
|
|
|
my $ttyrecdir = $homedir . "/ttyrec";
|
|
|
|
mkdir $ttyrecdir;
|
|
|
|
if (!chown $uid, $uid, $ttyrecdir) {
|
|
|
|
HEXIT('ERR_CANNOT_CHOWN', msg => "Couldn't chown ttyrec directory ($!)");
|
|
|
|
}
|
|
|
|
if (!chmod 0700, $ttyrecdir) {
|
|
|
|
HEXIT('ERR_CANNOT_CHMOD', msg => "Couldn't chmod ttyrec directory ($!)");
|
|
|
|
}
|
|
|
|
|
|
|
|
osh_debug('applying an acl for group ' . $ttygroup);
|
|
|
|
OVH::Bastion::sys_setfacl(target => $ttyrecdir, clear => 1, perms => "g:$ttygroup:rX")
|
|
|
|
or HEXIT('ERR_SETFACL_FAILED', msg => "Error setting ACL on $ttyrecdir");
|
|
|
|
|
|
|
|
OVH::Bastion::sys_setfacl(target => $ttyrecdir, default => 1, perms => "g:$ttygroup:rX")
|
|
|
|
or HEXIT('ERR_SETFACL_FAILED', msg => "Error setting default ACL on $ttyrecdir");
|
|
|
|
|
|
|
|
OVH::Bastion::sys_setfacl(target => $homedir, clear => 1, perms => "g:$ttygroup:x,g:osh-auditor:x")
|
|
|
|
or HEXIT('ERR_SETFACL_FAILED', msg => "Error setting ACL on $homedir");
|
|
|
|
|
|
|
|
OVH::Bastion::sys_setfacl(target => "$homedir/.ssh", clear => 1, perms => "g:osh-auditor:x")
|
|
|
|
or HEXIT('ERR_SETFACL_FAILED', msg => "Error setting ACL on $homedir/.ssh");
|
|
|
|
|
|
|
|
osh_info "Creating some more directories...";
|
|
|
|
mkdir "/home/allowkeeper/$account";
|
|
|
|
OVH::Bastion::touch_file("/home/allowkeeper/$account/allowed.ip");
|
|
|
|
OVH::Bastion::touch_file("/home/allowkeeper/$account/allowed.private");
|
|
|
|
|
|
|
|
osh_info "Applying proper ownerships...";
|
|
|
|
$fnret = OVH::Bastion::execute(
|
2022-06-30 21:00:29 +08:00
|
|
|
cmd => [
|
|
|
|
'chown', 'allowkeeper:allowkeeper',
|
|
|
|
"/home/allowkeeper/$account", "/home/allowkeeper/$account/allowed.ip",
|
|
|
|
"/home/allowkeeper/$account/allowed.private"
|
|
|
|
],
|
2020-10-16 00:32:37 +08:00
|
|
|
noisy_stderr => 1
|
|
|
|
);
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret->err eq 'OK'
|
|
|
|
or HEXIT('ERR_CHMOD_FAILED', msg => "Error while running chmod on allowkeeper (" . $fnret->msg . ")");
|
2020-10-16 00:32:37 +08:00
|
|
|
|
|
|
|
$fnret = OVH::Bastion::execute(cmd => ['chmod', '-R', 'o+rX', "/home/allowkeeper/$account"], noisy_stderr => 1);
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret->err eq 'OK'
|
|
|
|
or HEXIT('ERR_CHMOD_FAILED', msg => "Error while running chmod -R on allowkeeper (" . $fnret->msg . ")");
|
2020-10-16 00:32:37 +08:00
|
|
|
|
|
|
|
if (ref $config->{'accountCreateDefaultPersonalAccesses'} eq 'ARRAY' && $type eq 'normal') {
|
|
|
|
foreach my $defAccess (@{$config->{'accountCreateDefaultPersonalAccesses'}}) {
|
|
|
|
my (undef, $user, $ip, undef, $port) = $defAccess =~ m{^(([^@]+)@)?([0-9./]+)(:(\d+))?$};
|
|
|
|
next unless $ip;
|
|
|
|
my @command = qw{ sudo -n -u allowkeeper -- };
|
|
|
|
push @command, $OVH::Bastion::BASEPATH . '/bin/helper/osh-accountModifyPersonalAccess';
|
|
|
|
push @command, '--target', 'any';
|
|
|
|
push @command, '--action', 'add';
|
|
|
|
push @command, '--account', $account;
|
|
|
|
push @command, '--ip', $ip;
|
2020-11-23 05:05:45 +08:00
|
|
|
|
2020-10-16 00:32:37 +08:00
|
|
|
if ($user) {
|
|
|
|
push @command, '--user', ($user eq 'ACCOUNT' ? $account : $user);
|
|
|
|
}
|
|
|
|
$port and push @command, '--port', $port;
|
|
|
|
$fnret = OVH::Bastion::execute(cmd => \@command, noisy_stdout => 1, noisy_stderr => 1, is_helper => 1);
|
|
|
|
$fnret->err eq 'OK' or osh_warn("Couldn't add private access to account to $defAccess (" . $fnret->msg . ")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-16 00:36:13 +08:00
|
|
|
# store some metadata
|
|
|
|
my $creation_time = time();
|
|
|
|
my %metadata = (
|
|
|
|
by => $self,
|
|
|
|
bastion_version => $OVH::Bastion::VERSION,
|
|
|
|
datetime_utc => POSIX::strftime("%a %Y-%m-%d %H:%M:%S UTC", gmtime($creation_time)),
|
|
|
|
datetime_local => POSIX::strftime("%a %Y-%m-%d %H:%M:%S %Z", localtime($creation_time)),
|
|
|
|
timestamp => $creation_time,
|
|
|
|
comment => $comment,
|
|
|
|
);
|
|
|
|
$fnret = OVH::Bastion::account_config(account => $account, key => "creation_info", value => encode_json(\%metadata));
|
|
|
|
if (!$fnret) {
|
|
|
|
osh_warn("Couldn't set creation_info account metadata, continuing anyway");
|
|
|
|
warn_syslog("While creating account '$account', couldn't set creation_info metadata: " . $fnret->msg);
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
|
2021-07-16 00:36:13 +08:00
|
|
|
# also store the bare creation timestamp by itself, it's used by osh.pl for TTL accounts
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret = OVH::Bastion::account_config(account => $account, key => "creation_timestamp", value => time());
|
|
|
|
if (!$fnret) {
|
|
|
|
osh_warn("Couldn't store creation timestamp (" . $fnret->msg . "), continuing anyway");
|
2021-12-31 03:18:37 +08:00
|
|
|
warn_syslog("While creating account '$account', couldn't store creation timestamp: " . $fnret->msg);
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($ttl) {
|
|
|
|
$fnret = OVH::Bastion::duration2human(seconds => $ttl);
|
|
|
|
osh_info sprintf("Setting this account TTL (will expire in %s)", $fnret->value->{'human'});
|
|
|
|
$fnret = OVH::Bastion::account_config(account => $account, key => "account_ttl", value => $ttl);
|
|
|
|
if (!$fnret) {
|
2021-07-16 00:36:13 +08:00
|
|
|
osh_warn("Couldn't store account TTL (" . $fnret->msg . "), this account will NOT expire, continuing anyway");
|
2022-06-30 21:00:29 +08:00
|
|
|
warn_syslog("Couldn't store account TTL ("
|
|
|
|
. $fnret->msg
|
|
|
|
. ") while creating account '$account', this account will NOT expire, continuing anyway");
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($alwaysActive || $type eq 'realm') {
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::account_config(
|
|
|
|
account => $account,
|
|
|
|
key => OVH::Bastion::OPT_ACCOUNT_ALWAYS_ACTIVE,
|
|
|
|
value => "yes",
|
|
|
|
public => 1
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
if (!$fnret) {
|
|
|
|
osh_warn("Couldn't store always_active flag (" . $fnret->msg . "), continuing anyway");
|
2022-06-30 21:00:29 +08:00
|
|
|
warn_syslog("Couldn't store always_active flag ("
|
|
|
|
. $fnret->msg
|
|
|
|
. ") while creating account '$account', continuing anyway");
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::add_user_to_group(
|
|
|
|
user => "keyreader",
|
|
|
|
group => $account,
|
|
|
|
accountType => 'group',
|
|
|
|
groupType => 'regular'
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
osh_debug('user keyreader added to group');
|
|
|
|
|
|
|
|
my $finalPrefix = $realmFrom ? sprintf('from="%s"', $realmFrom) : $from;
|
|
|
|
$finalPrefix .= ' ' if $finalPrefix;
|
|
|
|
|
2020-11-26 18:40:14 +08:00
|
|
|
osh_info "Adding provided public key in authorized_keys file...";
|
|
|
|
if (open(my $fh_keys, '>>', $akfile)) {
|
2020-10-16 00:32:37 +08:00
|
|
|
foreach my $key (@vettedKeys) {
|
|
|
|
print $fh_keys $finalPrefix . $key . "\n";
|
|
|
|
}
|
|
|
|
close($fh_keys);
|
|
|
|
}
|
|
|
|
else {
|
2020-11-26 18:40:14 +08:00
|
|
|
HEXIT("ERR_CANNOT_ADD_KEY", msg => "Couldn't open $akfile when trying to add provided public key");
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
# push this flag to prevent ssh/telnet usage
|
|
|
|
if ($oshOnly) {
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret =
|
|
|
|
OVH::Bastion::account_config(account => $account, key => OVH::Bastion::OPT_ACCOUNT_OSH_ONLY, value => "yes");
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
}
|
|
|
|
|
2021-09-06 18:29:05 +08:00
|
|
|
# specific expiration policy. Note that 0 is a valid value (means "never").
|
|
|
|
if (defined $maxInactiveDays) {
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::account_config(
|
|
|
|
account => $account,
|
|
|
|
%{OVH::Bastion::OPT_ACCOUNT_MAX_INACTIVE_DAYS()}, value => $maxInactiveDays
|
|
|
|
);
|
2021-09-06 18:29:05 +08:00
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
}
|
|
|
|
|
2020-10-16 00:32:37 +08:00
|
|
|
# chown to root so user can no longer touch it
|
|
|
|
if ($immutableKey) {
|
2020-11-26 18:40:14 +08:00
|
|
|
chown 0, -1, $akfile;
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
osh_info "Generating account personal bastion key...";
|
|
|
|
$fnret = OVH::Bastion::generate_ssh_key(
|
|
|
|
folder => "$homedir/.ssh",
|
|
|
|
prefix => 'private',
|
|
|
|
name => $account,
|
|
|
|
gid => $uid,
|
|
|
|
uid => $uid,
|
|
|
|
algo => OVH::Bastion::config('defaultAccountEgressKeyAlgorithm')->value,
|
|
|
|
size => OVH::Bastion::config('defaultAccountEgressKeySize')->value,
|
|
|
|
);
|
|
|
|
$fnret or HEXIT($fnret);
|
|
|
|
|
|
|
|
osh_info "Account successfully created!";
|
|
|
|
if ($realmFrom) {
|
|
|
|
osh_info "Realm will be able to connect from the following IPs: $realmFrom";
|
|
|
|
}
|
|
|
|
elsif (scalar(@$ipList) > 0) {
|
|
|
|
osh_info "Account will be able to connect from the following IPs: " . join(', ', @$ipList);
|
|
|
|
}
|
|
|
|
|
|
|
|
# allowed to sudo for the account
|
|
|
|
osh_info("Configuring sudoers for this account");
|
2022-06-30 21:00:29 +08:00
|
|
|
$fnret = OVH::Bastion::execute(
|
|
|
|
cmd => [$OVH::Bastion::BASEPATH . '/bin/sudogen/generate-sudoers.sh', 'create', 'account', $account],
|
|
|
|
must_succeed => 1,
|
|
|
|
noisy_stdout => 1
|
|
|
|
);
|
2020-10-16 00:32:37 +08:00
|
|
|
$fnret or HEXIT('ERR_CANNOT_CREATE_SUDOERS', msg => "An error occurred while creating sudoers for this account");
|
|
|
|
|
|
|
|
my $bastionName = $config->{'bastionName'};
|
|
|
|
my $bastionCommand = $config->{'bastionCommand'};
|
|
|
|
$bastionCommand =~ s/USER|ACCOUNT/$account/g;
|
|
|
|
$bastionCommand =~ s/CACHENAME|BASTIONNAME/$bastionName/g;
|
|
|
|
my $hostname = Sys::Hostname::hostname();
|
|
|
|
$bastionCommand =~ s/HOSTNAME/$hostname/g;
|
|
|
|
|
|
|
|
if ($type eq 'realm') {
|
|
|
|
osh_info "Realm has been created.";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
osh_info "==> alias $bastionName='$bastionCommand'";
|
2022-06-30 21:00:29 +08:00
|
|
|
osh_info("To test his access, ask this user to set the above alias in their .bash_aliases, ",
|
|
|
|
"then run `$bastionName --osh info'");
|
2020-10-16 00:32:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
OVH::Bastion::syslogFormatted(
|
|
|
|
severity => 'info',
|
|
|
|
type => 'account',
|
|
|
|
fields => [
|
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
|
|
|
['action', 'create'],
|
|
|
|
['account', $account],
|
|
|
|
['account_uid', $uid],
|
|
|
|
['public_key', @vettedKeys ? $vettedKeys[0] : undef],
|
2020-10-16 00:32:37 +08:00
|
|
|
['always_active', ($alwaysActive ? 'true' : 'false')],
|
|
|
|
['uid_auto', ($uidAuto ? 'true' : 'false')],
|
|
|
|
['osh_only', ($oshOnly ? 'true' : 'false')],
|
|
|
|
['immutable_key', ($immutableKey ? 'true' : 'false')],
|
|
|
|
['comment', $comment],
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
HEXIT('OK');
|