mirror of
https://github.com/ovh/the-bastion.git
synced 2024-11-10 17:26:51 +08:00
72cefa6417
Commit that introduced the performance degradation is effab4a
(fix: workaround for undocumented caching in getpw/getgr funcs)
Rewrote caching at the getpwent/getpwnam/getgrent/getgrnam level,
which restores performance pre-effab4a and even enhances it in somes cases,
for example on a 2000-accounts and 2000-groups bastion, we are:
- 11% faster on --osh help
- 35% faster on --osh selfListAccesses (reduces syscalls by 87%)
306 lines
13 KiB
Perl
Executable file
306 lines
13 KiB
Perl
Executable file
#! /usr/bin/env perl
|
|
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
|
|
use common::sense;
|
|
use Sys::Hostname ();
|
|
use Term::ANSIColor;
|
|
|
|
use File::Basename;
|
|
use lib dirname(__FILE__) . '/../../../lib/perl';
|
|
use OVH::Result;
|
|
use OVH::Bastion;
|
|
use OVH::Bastion::Plugin qw( :DEFAULT help );
|
|
|
|
# globally allow sys_getpw* and sys_getgr* cache use
|
|
$ENV{'PW_GR_CACHE'} = 1;
|
|
|
|
my ($name);
|
|
OVH::Bastion::Plugin::begin(
|
|
argv => \@ARGV,
|
|
header => "information",
|
|
options => {'name' => \$name},
|
|
helptext => <<'EOF',
|
|
Displays some information about this bastion instance
|
|
|
|
Usage: --osh SCRIPT_NAME
|
|
EOF
|
|
);
|
|
|
|
my $fnret = OVH::Bastion::load_configuration();
|
|
$fnret or osh_exit($fnret);
|
|
my $config = $fnret->value;
|
|
|
|
my %ret;
|
|
|
|
my $selfdisplay = $remoteself ? $remoteself : $self;
|
|
$ret{'account'} = $selfdisplay;
|
|
osh_info "You are " . colored($selfdisplay, 'green');
|
|
if ($realm) {
|
|
osh_info "You are a citizen of a distant realm named " . colored($realm, 'green');
|
|
$ret{'realm'} = $realm;
|
|
}
|
|
if (OVH::Bastion::is_auditor(account => $self)) {
|
|
$ret{'is_auditor'} = 1;
|
|
osh_info "You are a " . colored('bastion auditor!', 'green');
|
|
}
|
|
if (OVH::Bastion::is_super_owner(account => $self)) {
|
|
$ret{'is_superowner'} = 1;
|
|
osh_info "Look at you, you are a " . colored('bastion superowner!', 'green');
|
|
}
|
|
if (OVH::Bastion::is_admin(account => $self)) {
|
|
$ret{'is_admin'} = 1;
|
|
osh_info "Woosh, you are even a " . colored('bastion admin!', 'green');
|
|
}
|
|
|
|
if (!$realm) {
|
|
osh_info "\nYour alias to connect to this bastion is:";
|
|
my $bastionName = OVH::Bastion::config('bastionName')->value();
|
|
my $bastionCommand = OVH::Bastion::config('bastionCommand')->value();
|
|
$bastionCommand =~ s/USER|ACCOUNT/$self/g;
|
|
$bastionCommand =~ s/CACHENAME|BASTIONNAME/$bastionName/g;
|
|
my $hostname = Sys::Hostname::hostname();
|
|
$bastionCommand =~ s/HOSTNAME/$hostname/g;
|
|
osh_info colored("alias $bastionName='$bastionCommand'", "magenta");
|
|
$ret{'bastion_alias_command'} = $bastionCommand;
|
|
|
|
if (OVH::Bastion::config('moshAllowed')->value) {
|
|
osh_info "Your alias to connect to this bastion with MOSH is:";
|
|
my ($cacheDestination) = $bastionCommand =~ m{(\S+@\S+)};
|
|
$bastionCommand =~ s{\Q$cacheDestination\E}{};
|
|
$bastionCommand =~ s{\s+--\s+$}{};
|
|
$bastionCommand =~ s{ +}{ }g;
|
|
osh_info colored("alias ${bastionName}m='mosh --ssh=\"$bastionCommand\" $cacheDestination -- '", "magenta");
|
|
$ret{'bastion_alias_command_mosh'} = "mosh --ssh=\"$bastionCommand\" $cacheDestination -- ";
|
|
}
|
|
}
|
|
|
|
osh_info "\nMulti-Factor Authentication (MFA) on your account:";
|
|
$ret{'mfa_password_required'} =
|
|
OVH::Bastion::is_user_in_group(user => $self, group => OVH::Bastion::MFA_PASSWORD_REQUIRED_GROUP) ? 1 : 0;
|
|
$ret{'mfa_password_bypass'} =
|
|
OVH::Bastion::is_user_in_group(user => $self, group => OVH::Bastion::MFA_PASSWORD_BYPASS_GROUP) ? 1 : 0;
|
|
$ret{'mfa_password_configured'} =
|
|
OVH::Bastion::is_user_in_group(user => $self, group => OVH::Bastion::MFA_PASSWORD_CONFIGURED_GROUP) ? 1 : 0;
|
|
osh_info "- Additional password authentication is "
|
|
. ($ret{'mfa_password_required'} ? colored('required', 'green') : colored('not required', 'blue'));
|
|
osh_info "- Additional password authentication bypass is "
|
|
. ($ret{'mfa_password_bypass'} ? colored('enabled', 'green') : colored('disabled', 'blue'));
|
|
osh_info "- Additional password authentication is "
|
|
. ($ret{'mfa_password_configured'} ? colored('enabled and active', 'green') : colored('disabled', 'blue'));
|
|
|
|
$ret{'mfa_totp_required'} =
|
|
OVH::Bastion::is_user_in_group(user => $self, group => OVH::Bastion::MFA_TOTP_REQUIRED_GROUP) ? 1 : 0;
|
|
$ret{'mfa_totp_bypass'} =
|
|
OVH::Bastion::is_user_in_group(user => $self, group => OVH::Bastion::MFA_TOTP_BYPASS_GROUP) ? 1 : 0;
|
|
$ret{'mfa_totp_configured'} =
|
|
OVH::Bastion::is_user_in_group(user => $self, group => OVH::Bastion::MFA_TOTP_CONFIGURED_GROUP) ? 1 : 0;
|
|
osh_info "- Additional TOTP authentication is "
|
|
. ($ret{'mfa_totp_required'} ? colored('required', 'green') : colored('not required', 'blue'));
|
|
osh_info "- Additional TOTP authentication bypass is "
|
|
. ($ret{'mfa_totp_bypass'} ? colored('enabled', 'green') : colored('disabled', 'blue'));
|
|
osh_info "- Additional TOTP authentication is "
|
|
. ($ret{'mfa_totp_configured'} ? colored('enabled and active', 'green') : colored('disabled', 'blue'));
|
|
|
|
$fnret = OVH::Bastion::sys_getpasswordinfo(user => $self);
|
|
if ($fnret) {
|
|
$ret{"password_$_"} = $fnret->value->{$_} for (keys %{$fnret->value});
|
|
osh_info "\nAccount PAM UNIX password information (used for password MFA):";
|
|
osh_info sprintf("- Password is %s",
|
|
$ret{'password_password'} eq 'locked'
|
|
? colored('unused', 'blue')
|
|
: ($ret{'password_password'} eq 'set' ? colored('set', 'green') : colored($ret{'password_password'}, 'red')));
|
|
osh_info "- Password was last changed on " . colored($ret{'password_date_changed'}, 'magenta');
|
|
if ($ret{'password_max_days'} == -1) {
|
|
osh_info "- Password will never expire";
|
|
}
|
|
else {
|
|
osh_info "- Password must be changed every " . colored($ret{'password_max_days'}, 'magenta') . " days at least";
|
|
osh_info "- A warning is displayed "
|
|
. colored($ret{'password_warn_days'}, 'magenta')
|
|
. " days before expiration";
|
|
}
|
|
if ($ret{'password_min_days'} != 0) {
|
|
osh_info "- The minimum time between two password changes is " . $ret{'password_min_days'} . " days";
|
|
}
|
|
if ($ret{'password_max_days'} != -1) {
|
|
if ($ret{'password_inactive_days'} != -1) {
|
|
osh_info "- Account will be disabled "
|
|
. colored($ret{'password_inactive_days'}, 'magenta')
|
|
. " days after password expiration";
|
|
}
|
|
else {
|
|
osh_info "- Account will " . colored('not', 'magenta') . " be disabled after password expiration";
|
|
}
|
|
}
|
|
}
|
|
|
|
osh_info
|
|
sprintf("\nI am %s, aka %s", colored(Sys::Hostname::hostname(), 'green'), colored($config->{'bastionName'}, 'green'));
|
|
$ret{'hostname'} = Sys::Hostname::hostname();
|
|
$ret{'bastion_name'} = $config->{'bastionName'};
|
|
|
|
$fnret = OVH::Bastion::get_account_list();
|
|
my $nbaccounts = $fnret ? keys %{$fnret->value} : '?';
|
|
$fnret = OVH::Bastion::get_group_list();
|
|
my $nbgroups = $fnret ? keys %{$fnret->value} : '?';
|
|
osh_info "I have "
|
|
. colored($nbaccounts, 'green')
|
|
. " registered accounts and "
|
|
. colored($nbgroups, 'green')
|
|
. " groups";
|
|
$ret{'registered_accounts'} = $nbaccounts;
|
|
$ret{'registered_groups'} = $nbgroups;
|
|
|
|
if ($config->{'readOnlySlaveMode'}) {
|
|
osh_info "I am a " . colored("SLAVE", "cyan") . ", which means modifications must be made through my master";
|
|
}
|
|
else {
|
|
osh_info "I am a " . colored("MASTER", "cyan") . ", which means I accept modifications";
|
|
}
|
|
$ret{'slave_mode'} = $config->{'readOnlySlaveMode'};
|
|
|
|
if (OVH::Bastion::is_auditor(account => $self)) {
|
|
my @adminAccounts = @{$config->{'adminAccounts'}};
|
|
osh_info "My admins are: " . colored(@adminAccounts ? join(", ", @adminAccounts) : "-", "magenta");
|
|
$ret{'adminAccounts'} = \@adminAccounts;
|
|
|
|
my @superOwnerAccounts = @{$config->{'superOwnerAccounts'}};
|
|
osh_info "My super owners are: " . colored(@superOwnerAccounts ? join(", ", @superOwnerAccounts) : "-", "magenta");
|
|
$ret{'superOwnerAccounts'} = \@superOwnerAccounts;
|
|
}
|
|
|
|
my @allowedNets = @{$config->{'allowedNetworks'}};
|
|
osh_info "The networks I'm able to connect you to on the egress side are: "
|
|
. colored(@allowedNets ? join(", ", @allowedNets) : "all", "magenta");
|
|
$ret{'allowed_networks_list'} = \@allowedNets;
|
|
|
|
my @forbiddenNets = @{$config->{'forbiddenNetworks'}};
|
|
osh_info "The networks that are explicitly forbidden on the egress side are: "
|
|
. colored(@forbiddenNets ? join(", ", @forbiddenNets) : "none", "magenta");
|
|
$ret{'forbidden_networks_list'} = \@forbiddenNets;
|
|
|
|
$fnret = OVH::Bastion::get_bastion_ips();
|
|
if ($fnret) {
|
|
my @ips = grep { !/^127\./ } @{$fnret->value};
|
|
if (@ips > 1) {
|
|
osh_info "My egress connection IPs to remote servers are " . colored(join(", ", @ips), "magenta");
|
|
osh_info
|
|
"...this includes the IPs of my potential siblings, don't forget to whitelist those in your firewalls!";
|
|
}
|
|
else {
|
|
osh_info "My egress connection IP to remote servers is " . colored(join(", ", @ips), "magenta");
|
|
osh_info "...don't forget to whitelist me in your firewalls!";
|
|
}
|
|
$ret{'egress_ip_list'} = \@ips;
|
|
}
|
|
|
|
osh_info "\nThe following policy applies on this bastion:";
|
|
osh_info "- The interactive mode (-i) is "
|
|
. ($config->{'interactiveModeAllowed'} ? colored('ENABLED', 'green') : colored('DISABLED', 'red'));
|
|
$ret{'interactive_mode_allowed'} = $config->{'interactiveModeAllowed'};
|
|
osh_info "- The support of mosh is "
|
|
. ($config->{'moshAllowed'} ? colored('ENABLED', 'green') : colored('DISABLED', 'red'));
|
|
$ret{'mosh_allowed'} = $config->{'moshAllowed'};
|
|
if ($config->{'accountMaxInactiveDays'}) {
|
|
osh_info "- Account expiration is "
|
|
. colored('ENABLED', 'green')
|
|
. ", with an expiration time of "
|
|
. colored($config->{'accountMaxInactiveDays'}, 'magenta') . " days";
|
|
}
|
|
else {
|
|
osh_info "- Account expiration is " . colored('DISABLED', 'red');
|
|
}
|
|
$ret{'account_expiration_days'} = $config->{'accountMaxInactiveDays'};
|
|
|
|
if ($config->{'idleLockTimeout'}) {
|
|
osh_info "- Keyboard input idle time for session locking is "
|
|
. colored('ENABLED', 'green')
|
|
. ", kicking in after "
|
|
. colored($config->{'idleLockTimeout'}, 'magenta')
|
|
. " seconds";
|
|
}
|
|
else {
|
|
osh_info "- Keyboard input idle time for session locking is " . colored('DISABLED', 'red');
|
|
}
|
|
$ret{'idle_lock_timeout'} = $config->{'idleLockTimeout'};
|
|
|
|
if ($config->{'idleKillTimeout'}) {
|
|
osh_info "- Keyboard input idle time for session killing is "
|
|
. colored('ENABLED', 'green')
|
|
. ", kicking in after "
|
|
. colored($config->{'idleKillTimeout'}, 'magenta')
|
|
. " seconds";
|
|
}
|
|
else {
|
|
osh_info "- Keyboard input idle time for session killing is " . colored('DISABLED', 'red');
|
|
}
|
|
$ret{'idle_kill_timeout'} = $config->{'idleKillTimeout'};
|
|
|
|
$fnret = OVH::Bastion::get_from_for_user_key();
|
|
if ($fnret && $fnret->value->{'from'}) {
|
|
osh_info "- The forced \"from\" prepend on ingress keys is "
|
|
. colored('ENABLED', 'green')
|
|
. ", with the following value: "
|
|
. colored($fnret->value->{'from'}, 'magenta');
|
|
$ret{'ingress_keys_from_ip_list'} = $fnret->value->{'ipList'};
|
|
}
|
|
else {
|
|
osh_info "- The forced \"from\" prepend on ingress keys is " . colored('DISABLED', 'red');
|
|
$ret{'ingress_keys_from_ip_list'} = [];
|
|
}
|
|
|
|
foreach my $way (qw{ ingress egress }) {
|
|
$fnret = OVH::Bastion::get_supported_ssh_algorithms_list(way => $way);
|
|
if ($fnret) {
|
|
osh_info "- The following algorithms are allowed for $way SSH keys: "
|
|
. colored(join(', ', @{$fnret->value}), 'magenta');
|
|
$ret{"${way}_ssh_key_algorithms"} = $fnret->value;
|
|
}
|
|
if (grep { $_ eq 'rsa' } @{$fnret->value}) {
|
|
osh_info "- The RSA key size for $way SSH keys must be between "
|
|
. colored($config->{"minimum" . ucfirst($way) . "RsaKeySize"}, "magenta") . " and "
|
|
. colored($config->{"maximum" . ucfirst($way) . "RsaKeySize"}, "magenta") . " bits";
|
|
$ret{"${way}_rsa_min_size"} = $config->{"minimum" . ucfirst($way) . "RsaKeySize"};
|
|
$ret{"${way}_rsa_max_size"} = $config->{"maximum" . ucfirst($way) . "RsaKeySize"};
|
|
}
|
|
}
|
|
osh_info "- The Multi-Factor Authentication (MFA) policy is "
|
|
. colored(uc($config->{'accountMFAPolicy'}), $config->{'accountMFAPolicy'} eq 'disabled' ? 'red' : 'green');
|
|
|
|
osh_info "- The PIV-enforced ingress keys policy is "
|
|
. colored($config->{'ingressRequirePIV'} ? 'ENABLED' : 'DISABLED', $config->{'ingressRequirePIV'} ? 'red' : 'green');
|
|
|
|
if (OVH::Bastion::is_admin(account => $self)) {
|
|
osh_info "\nAs you are a bastion admin, more information follows:";
|
|
$fnret = OVH::Bastion::sysinfo();
|
|
if ($fnret and $fnret->value) {
|
|
osh_info "We're running under " . $fnret->value->{'system'} . " " . $fnret->value->{'release'};
|
|
$ret{'os_system'} = $fnret->value->{'system'};
|
|
$ret{'os_release'} = $fnret->value->{'release'};
|
|
}
|
|
|
|
$fnret = OVH::Bastion::execute(cmd => ['uptime']);
|
|
if ($fnret) {
|
|
$fnret->value->{'stdout'}->[0] =~ s/^\s+//;
|
|
osh_info $fnret->value->{'stdout'}->[0];
|
|
$ret{'uptime'} = $fnret->value->{'stdout'}->[0];
|
|
}
|
|
|
|
$fnret = OVH::Bastion::execute(cmd => ['free', '-h']);
|
|
if ($fnret) {
|
|
osh_info "Memory info:";
|
|
osh_info join("\n", @{$fnret->value->{'stdout'}});
|
|
}
|
|
}
|
|
|
|
if (-x "/usr/games/fortune") {
|
|
my @command = qw{ /usr/games/fortune bofh-excuses };
|
|
$fnret = OVH::Bastion::execute(cmd => \@command);
|
|
if ($fnret) {
|
|
my $fortune = join("\n", grep { $_ } @{$fnret->value->{'stdout'}});
|
|
osh_info "\nHere is your excuse for anything not working today:";
|
|
osh_info $fortune;
|
|
$ret{'fortune'} = $fortune;
|
|
}
|
|
}
|
|
|
|
osh_ok \%ret;
|