Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/rex
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ The C<rex> script can be used to execute tasks defined in a Rexfile from the com
-O Pass additional options, like CMDB path
-s Use sudo for every command
-S Password for sudo
-D Use doas for every command
-t Number of threads to use (aka 'parallelism' param)
-v Display (R)?ex version

Expand Down
57 changes: 53 additions & 4 deletions lib/Rex.pm
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ BEGIN {
eval { Net::SSH2->require; };
}

our ( @EXPORT, @CONNECTION_STACK, $GLOBAL_SUDO, $MODULE_PATHS,
$WITH_EXIT_STATUS, @FEATURE_FLAGS );
our (
@EXPORT, @CONNECTION_STACK, $GLOBAL_SUDO, $GLOBAL_DOAS,
$MODULE_PATHS, $WITH_EXIT_STATUS, @FEATURE_FLAGS
);

$WITH_EXIT_STATUS = 1; # since 0.50 activated by default
@FEATURE_FLAGS = ();
Expand Down Expand Up @@ -241,8 +243,10 @@ sub push_connection {
}

sub pop_connection {
pop @CONNECTION_STACK;
Rex::Logger::debug( "Connections in queue: " . scalar(@CONNECTION_STACK) );
my $connection = pop @CONNECTION_STACK;

Rex::Logger::debug( 'Connections in queue: ' . scalar @CONNECTION_STACK );
return $connection;
}

sub reconnect_lost_connections {
Expand Down Expand Up @@ -385,6 +389,51 @@ sub global_sudo {
Rex::Config->set_use_cache(1);
}

=head2 is_doas

Returns 1 if the current operation is executed within doas.

=cut

sub is_doas {

if ( $CONNECTION_STACK[-1] ) {
if ( exists $CONNECTION_STACK[-1]->{server}->{auth}->{doas}
&& $CONNECTION_STACK[-1]->{server}->{auth}->{doas} == 1 )
{
return 1;
}
elsif ( exists $CONNECTION_STACK[-1]->{server}->{auth}->{doas}
&& $CONNECTION_STACK[-1]->{server}->{auth}->{doas} == 0 )
{
return 0;
}
}

if ($GLOBAL_DOAS) { return 1; }

if ( $CONNECTION_STACK[-1] ) {
return $CONNECTION_STACK[-1]->{conn}->get_current_use_doas;
}

return 0;
}

=head2 global_doas

Enable or disable doas globally.

=cut

sub global_doas {
my ($on) = @_;
$GLOBAL_DOAS = $on;

# turn cache on
Rex::Config->set_use_cache(1);
return 1;
}

=head2 get_sftp

Returns the sftp object for the current ssh connection.
Expand Down
10 changes: 10 additions & 0 deletions lib/Rex/CLI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ CHECK_OVERWRITE: {
_handle_T(%opts);

Rex::global_sudo(0);
Rex::global_doas(0);
Rex::Logger::debug("Removing lockfile") if ( !exists $opts{'F'} );
CORE::unlink("$::rexfile.lock") if ( !exists $opts{'F'} );
CORE::exit 0;
Expand All @@ -289,10 +290,16 @@ CHECK_OVERWRITE: {
if ( exists $opts{'s'} ) {
sudo("on");
}

if ( exists $opts{'S'} ) {
sudo_password( $opts{'S'} );
}

# turn doas on with cli option D is used
if ( exists $opts{'D'} ) {
doas("on");
}

if ( exists $opts{'t'} ) {
parallelism( $opts{'t'} );
}
Expand Down Expand Up @@ -439,6 +446,7 @@ sub __help__ {
printf $fmt, "-O", "Pass additional options, like CMDB path";
printf $fmt, "-s", "Use sudo for every command";
printf $fmt, "-S", "Password for sudo";
printf $fmt, "-D", 'Use doas for every command';
printf $fmt, "-t", "Number of threads to use (aka 'parallelism' param)";
printf $fmt, "-v", "Display (R)?ex version";
print "\n";
Expand Down Expand Up @@ -649,6 +657,7 @@ sub handle_lock_file {
else {
Rex::Logger::debug("Found stale lock file. Removing it.");
Rex::global_sudo(0);
Rex::global_doas(0);
CORE::unlink("$rexfile.lock");
}
}
Expand Down Expand Up @@ -813,6 +822,7 @@ sub exit_rex {
summarize($signal) if !$signal;

Rex::global_sudo(0);
Rex::global_doas(0);
Rex::Logger::debug("Removing lockfile") if !exists $opts{'F'};
unlink("$::rexfile.lock") if !exists $opts{'F'};

Expand Down
3 changes: 3 additions & 0 deletions lib/Rex/Commands.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,9 @@ sub LOCAL (&) {
my $local_connect = Rex::Interface::Connection->create("Local");

my $old_global_sudo = $Rex::GLOBAL_SUDO;
my $old_global_doas = $Rex::GLOBAL_DOAS;
$Rex::GLOBAL_SUDO = 0;
$Rex::GLOBAL_DOAS = 0;

Rex::push_connection(
{
Expand All @@ -1230,6 +1232,7 @@ sub LOCAL (&) {
Rex::pop_connection();

$Rex::GLOBAL_SUDO = $old_global_sudo;
$Rex::GLOBAL_DOAS = $old_global_doas;

return $ret;
}
Expand Down
87 changes: 82 additions & 5 deletions lib/Rex/Commands/Run.pm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ With this module you can run a command.

my $output = run 'ls -l';
sudo 'id';
doas 'id';

=head1 CONFIGURATION AND ENVIRONMENT

Expand Down Expand Up @@ -71,7 +72,7 @@ BEGIN {
use vars qw(@EXPORT);
use base qw(Rex::Exporter);

@EXPORT = qw(run can_run sudo);
@EXPORT = qw(run can_run sudo doas);

=head2 run($command [, $callback], %options)

Expand Down Expand Up @@ -206,7 +207,7 @@ sub run {
for my $_cmd ( @{$cmd} ) {
&run( $_cmd, @_ );
}
return;
return undef;
}

my ( $code, $option );
Expand Down Expand Up @@ -245,7 +246,7 @@ sub run {
}
);

return;
return undef;
}

if ( exists $option->{command} ) {
Expand Down Expand Up @@ -466,12 +467,12 @@ sub sudo {
if ( $cmd eq "on" || $cmd eq "-on" || $cmd eq "1" ) {
Rex::Logger::debug("Turning sudo globally on");
Rex::global_sudo(1);
return;
return undef;
}
elsif ( $cmd eq "0" ) {
Rex::Logger::debug("Turning sudo globally off");
Rex::global_sudo(0);
return;
return undef;
}

Rex::get_current_connection_object()->push_use_sudo(1);
Expand All @@ -493,4 +494,80 @@ sub sudo {
return $ret;
}

=head2 doas($command)

This function will execute the given command with doas.

With this function you can run a command as another user via doas.

B<Note:> doas on OpenBSD does not support password input via stdin like sudo does.
You must configure F</etc/doas.conf> appropriately for unattended execution.
A typical configuration for a user to run commands as root without a password would be:

permit nopass myuser as root

However, administrators should restrict rules appropriately for their security requirements.

You can also pass a hash reference as first argument to specify options:

doas { user => 'root', command => 'id' };

doas supports the following options:

=over 4

=item user

The user to execute the command as.

=item command

The command to execute.

=back

To use doas without a password prompt (non-interactively), Rex uses the C<-n> option.
Missing authorization will cause an immediate command failure.

=cut

sub doas {
my ($cmd) = @_;

my $options;
if ( ref $cmd eq 'HASH' ) {
$options = $cmd;
$cmd = $options->{command};
}

if ( $cmd eq 'on' || $cmd eq '-on' || $cmd eq '1' ) {
Rex::Logger::debug('Turning doas globally on');
Rex::global_doas(1);
return undef;
}
elsif ( $cmd eq '0' ) {
Rex::Logger::debug('Turning doas globally off');
Rex::global_doas(0);
return undef;
}

Rex::get_current_connection_object()->push_use_doas(1);
Rex::get_current_connection_object()->push_doas_options( %{$options} );

my $ret;

# if doas is used with a code block
if ( ref($cmd) eq 'CODE' ) {
$ret = &$cmd();
}
else {
$ret = i_run( $cmd, fail_ok => 1 );
}

Rex::get_current_connection_object()->pop_use_doas();
Rex::get_current_connection_object()->pop_doas_options();

return $ret;
}

1;
16 changes: 15 additions & 1 deletion lib/Rex/Group/Entry/Server.pm
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ sub new {
delete $self->{sudo_password};
}

if ( $self->{doas} ) {
$self->{auth}->{doas} = $self->{doas};
delete $self->{doas};
}

if ( $self->{auth_type} ) {
$self->{auth}->{auth_type} = $self->{auth_type};
delete $self->{auth_type};
Expand Down Expand Up @@ -284,12 +289,21 @@ sub get_sudo_password {
Rex::Config->get_sudo_password;
}

sub get_doas {
my ($self) = @_;
if ( exists $self->{auth}->{doas} ) {
return $self->{auth}->{doas};
}

return 0;
}

sub merge_auth {
my ( $self, $other_auth ) = @_;

my %new_auth;
my @keys =
qw/user password port private_key public_key auth_type sudo sudo_password/;
qw/user password port private_key public_key auth_type sudo sudo_password doas/;

for my $key (@keys) {
my $call = "get_$key";
Expand Down
Loading
Loading