mirror of
https://github.com/ovh/the-bastion.git
synced 2024-12-29 11:41:27 +08:00
3925e67d43
This command deletes a group, as `groupDelete` does, but works for owners so that they can delete their own group. `groupDelete` remains as a restricted command, able to delete any group. Closes #40.
206 lines
6.7 KiB
Perl
Executable file
206 lines
6.7 KiB
Perl
Executable file
#! /usr/bin/perl -T
|
|
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
|
|
# NEEDGROUP osh-groupDelete
|
|
# SUDOERS %osh-groupDelete ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-groupDelete *
|
|
# KEYSUDOERS # as an owner, we can delete our own group
|
|
# KEYSUDOERS SUPEROWNERS, %%GROUP%-owner ALL=(root) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-groupDelete --group %GROUP%
|
|
# FILEMODE 0700
|
|
# FILEOWN 0 0
|
|
|
|
#>HEADER
|
|
use common::sense;
|
|
use Getopt::Long;
|
|
use File::Copy qw(move);
|
|
|
|
use File::Basename;
|
|
use lib dirname(__FILE__) . '/../../lib/perl';
|
|
use OVH::Bastion;
|
|
local $| = 1;
|
|
|
|
#
|
|
# Globals
|
|
#
|
|
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/pkg/bin';
|
|
my ($self) = $ENV{'SUDO_USER'} =~ m{^([a-zA-Z0-9._-]+)$};
|
|
if (not defined $self) {
|
|
if ($< == 0) {
|
|
$self = 'root';
|
|
}
|
|
else {
|
|
HEXIT('ERR_SUDO_NEEDED', msg => 'This command must be run under sudo');
|
|
}
|
|
}
|
|
|
|
# Fetch command options
|
|
my $fnret;
|
|
my ($result, @optwarns);
|
|
my ($group);
|
|
eval {
|
|
local $SIG{__WARN__} = sub { push @optwarns, shift };
|
|
$result = GetOptions("group=s" => sub { $group //= $_[1] },);
|
|
};
|
|
if ($@) { die $@ }
|
|
|
|
if (!$result) {
|
|
local $" = ", ";
|
|
HEXIT('ERR_BAD_OPTIONS', msg => "Error parsing options: @optwarns");
|
|
}
|
|
|
|
if (!$group) {
|
|
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'group'");
|
|
}
|
|
|
|
#<HEADER
|
|
|
|
#>PARAMS:GROUP
|
|
# test if start by key, append if necessary
|
|
osh_debug("Checking group");
|
|
$fnret = OVH::Bastion::is_valid_group_and_existing(group => $group, groupType => "key");
|
|
$fnret or HEXIT($fnret);
|
|
|
|
# get returned untainted value
|
|
$group = $fnret->value->{'group'};
|
|
my $shortGroup = $fnret->value->{'shortGroup'};
|
|
|
|
#<PARAMS:GROUP
|
|
|
|
#>RIGHTSCHECK
|
|
if ($self eq 'root') {
|
|
osh_debug "Real root, skipping checks of permissions";
|
|
}
|
|
else {
|
|
# either we can delete any group
|
|
$fnret = OVH::Bastion::is_user_in_group(user => $self, group => "osh-groupDelete");
|
|
if (!$fnret) {
|
|
|
|
# or we can delete our own group as the owner of said group
|
|
$fnret = OVH::Bastion::is_group_owner(account => $self, group => $shortGroup, sudo => 1, superowner => 1);
|
|
if (!$fnret) {
|
|
HEXIT('ERR_SECURITY_VIOLATION', msg => "You're not allowed to run this, dear $self");
|
|
}
|
|
}
|
|
}
|
|
|
|
#<RIGHTSCHECK
|
|
|
|
#>CODE
|
|
# last security check
|
|
if (not -e "/home/$group/allowed.ip" or not -e "/home/keykeeper/$group") {
|
|
HEXIT('ERR_INVALID_GROUP', msg => "Sorry, but $shortGroup doesn't seem to be a legit bastion group");
|
|
}
|
|
|
|
if (not -d "/home/oldkeeper") {
|
|
mkdir "/home/oldkeeper";
|
|
}
|
|
chown 0, 0, "/home/oldkeeper";
|
|
chmod 0700, "/home/oldkeeper";
|
|
|
|
if (!-d "/home/oldkeeper/groups") {
|
|
mkdir "/home/oldkeeper/groups";
|
|
}
|
|
chown 0, 0, "/home/oldkeeper/groups";
|
|
chmod 0700, "/home/oldkeeper/groups";
|
|
|
|
my $suffix = 'at-' . time() . '.by-' . $self;
|
|
|
|
my $fulldir = "/home/oldkeeper/groups/$group.$suffix";
|
|
if (-e $fulldir) {
|
|
exitError("Errr... $fulldir exists?!");
|
|
}
|
|
|
|
mkdir $fulldir;
|
|
chown 0, 0, $fulldir;
|
|
chmod 0700, $fulldir;
|
|
|
|
move("/home/$group", "$fulldir/$group-home");
|
|
move("/home/keykeeper/$group", "$fulldir/$group-keykeeper");
|
|
|
|
# now tar.gz the directory, this is important because inside we'll keep the
|
|
# old GID of the group, and we don't want GID-orphaned files on our filesystem, it's
|
|
# not a problem to have those inside a tarfile however.
|
|
my @tarcmd = qw{ tar czf };
|
|
push @tarcmd, $fulldir . '.tar.gz';
|
|
push @tarcmd, '--acls' if OVH::Bastion::has_acls();
|
|
push @tarcmd, '--one-file-system', '-p', '--remove-files', $fulldir;
|
|
$fnret = OVH::Bastion::execute(cmd => \@tarcmd, must_succeed => 1);
|
|
if (!$fnret) {
|
|
osh_warn("Couldn't tar the backup homedir of this group (" . $fnret->msg . "), proceeding anyway.");
|
|
chmod 0000, $fulldir;
|
|
}
|
|
else {
|
|
chmod 0000, $fulldir . '.tar.gz';
|
|
unlink($fulldir);
|
|
}
|
|
|
|
# remove dead symlinks in users homes
|
|
my $dh;
|
|
if (opendir($dh, "/home/allowkeeper")) {
|
|
while (my $dir = readdir($dh)) {
|
|
$dir =~ /^\./ and next;
|
|
$dir !~ /^([a-zA-Z0-9._-]+)$/ and next;
|
|
$dir = "/home/allowkeeper/$1"; # and untaint
|
|
-d $dir or next;
|
|
foreach my $file ("$dir/allowed.ip.$shortGroup", "$dir/allowed.partial.$shortGroup") {
|
|
if (-e $file || -l $file) {
|
|
osh_info "Removing $file...";
|
|
unlink($file);
|
|
}
|
|
}
|
|
}
|
|
close($dh);
|
|
}
|
|
else {
|
|
osh_warn("Couldn't open /home/allowkeeper ?!");
|
|
}
|
|
|
|
# trying to remove main and gatekeeper and owner groups
|
|
foreach my $todelete ("$group-owner", "$group-aclkeeper", "$group-gatekeeper", $group) {
|
|
$fnret = OVH::Bastion::is_group_existing(group => $todelete);
|
|
if ($fnret) {
|
|
$todelete = $fnret->value->{'group'}; # untaint
|
|
my $members = $fnret->value->{'members'} || [];
|
|
if (@$members) {
|
|
osh_info "Found " . (scalar @$members) . " members, removing them from the group";
|
|
foreach my $member (@$members) {
|
|
osh_info "... removing $member from group $todelete";
|
|
$fnret = OVH::Bastion::sys_delmemberfromgroup(user => $member, group => $todelete, noisy_stderr => 1);
|
|
$fnret->err eq 'OK'
|
|
or HEXIT('ERR_DELUSER_FAILED', msg => "Error while attempting to remove member $member from group $todelete (" . $fnret->msg . ")");
|
|
}
|
|
}
|
|
|
|
if ($todelete eq $group) {
|
|
osh_info "Deleting main user of group $todelete...", $fnret = OVH::Bastion::sys_userdel(user => $todelete, noisy_stderr => 1);
|
|
$fnret->err eq 'OK'
|
|
or HEXIT('ERR_DELUSER_FAILED', msg => "Error while attempting to delete main user of group $todelete (" . $fnret->msg . ")");
|
|
}
|
|
|
|
# some OSes delete the main group of user if it has the same name
|
|
# and nobody else is a member of it, so check it still exists before
|
|
# trying to delete it
|
|
$fnret = OVH::Bastion::is_group_existing(group => $todelete);
|
|
if ($fnret) {
|
|
osh_info "Deleting group $todelete...";
|
|
$fnret = OVH::Bastion::sys_groupdel(group => $todelete, noisy_stderr => 1);
|
|
$fnret
|
|
or HEXIT('ERR_DELGROUP_FAILED', msg => "Error while attempting to delete group $todelete (" . $fnret->msg . ")");
|
|
}
|
|
}
|
|
else {
|
|
osh_info "Group $todelete not found, ignoring...";
|
|
}
|
|
}
|
|
|
|
# remove sudoers if it's there
|
|
$fnret = OVH::Bastion::execute(cmd => [$OVH::Bastion::BASEPATH . '/bin/sudogen/generate-sudoers.sh', 'delete', 'group', $group], must_succeed => 1, noisy_stdout => 1);
|
|
if (!$fnret) {
|
|
warn_syslog("Error during group deletion of '$group', couldn't delete sudoers file: " . $fnret->msg);
|
|
}
|
|
|
|
OVH::Bastion::syslogFormatted(
|
|
severity => 'info',
|
|
type => 'group',
|
|
fields => [['action', 'delete'], ['group', $shortGroup],]
|
|
);
|
|
|
|
HEXIT('OK');
|