File Coverage

lib/Rex/Interface/Connection/OpenSSH.pm
Criterion Covered Total %
statement 28 133 21.0
branch 0 34 0.0
condition 1 36 2.7
subroutine 10 18 55.5
pod 0 10 0.0
total 39 231 16.8


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             package Rex::Interface::Connection::OpenSSH;
6              
7 1     1   15 use v5.12.5;
  1         3  
8 1     1   7 use warnings;
  1         2  
  1         61  
9              
10             our $VERSION = '1.14.2.3'; # TRIAL VERSION
11              
12             BEGIN {
13 1     1   9 use Rex::Require;
  1         2  
  1         8  
14 1     1   30 Net::OpenSSH->require;
15             }
16              
17 1     1   11 use Rex::Interface::Connection::Base;
  1         2  
  1         12  
18 1     1   34 use Rex::Helper::IP;
  1         6  
  1         17  
19 1     1   76 use Data::Dumper;
  1         2  
  1         59  
20 1     1   7 use base qw(Rex::Interface::Connection::Base);
  1         2  
  1         1249  
21              
22             sub new {
23 1     1 0 3 my $that = shift;
24 1   33     6 my $proto = ref($that) || $that;
25 1         7 my $self = $that->SUPER::new(@_);
26              
27 1         2 bless( $self, $proto );
28              
29 1         6 return $self;
30             }
31              
32             sub connect {
33 0     0 0 0 my ( $self, %option ) = @_;
34              
35             my (
36 0         0 $user, $pass, $private_key, $public_key, $server,
37             $port, $timeout, $auth_type, $is_sudo
38             );
39              
40 0         0 $user = $option{user};
41 0         0 $pass = $option{password};
42 0         0 $server = $option{server};
43 0         0 $port = $option{port};
44 0         0 $timeout = $option{timeout};
45 0         0 $public_key = $option{public_key};
46 0         0 $private_key = $option{private_key};
47 0         0 $auth_type = $option{auth_type};
48 0         0 $is_sudo = $option{sudo};
49              
50 0         0 $self->{server} = $server;
51 0         0 $self->{is_sudo} = $is_sudo;
52 0         0 $self->{__auth_info__} = \%option;
53              
54 0         0 Rex::Logger::debug("Using Net::OpenSSH for connection");
55 0         0 Rex::Logger::debug( "Using user: " . $user );
56 0 0       0 Rex::Logger::debug( Rex::Logger::masq( "Using password: %s", $pass ) )
57             if defined $pass;
58              
59 0         0 my $proxy_command = Rex::Config->get_proxy_command( server => $server );
60              
61 0   0     0 $port ||= Rex::Config->get_port( server => $server ) || 22;
      0        
62 0   0     0 $timeout ||= Rex::Config->get_timeout( server => $server ) || 3;
      0        
63              
64 0   0     0 $server =
65             Rex::Config->get_ssh_config_hostname( server => $server ) || $server;
66              
67 0         0 ( $server, $port ) = Rex::Helper::IP::get_server_and_port( $server, $port );
68              
69 0         0 Rex::Logger::debug( "Connecting to $server:$port (" . $user . ")" );
70              
71 0         0 my %ssh_opts = Rex::Config->get_openssh_opt();
72 0         0 Rex::Logger::debug("get_openssh_opt()");
73 0         0 Rex::Logger::debug( Dumper( \%ssh_opts ) );
74              
75 0   0     0 $ssh_opts{LogLevel} ||= "QUIET";
76 0         0 $ssh_opts{ConnectTimeout} = $timeout;
77              
78             my %net_openssh_constructor_options = (
79             exists $ssh_opts{initialize_options}
80 0 0       0 ? %{ $ssh_opts{initialize_options} }
  0         0  
81             : ()
82             );
83              
84             delete $ssh_opts{initialize_options}
85 0 0       0 if ( exists $ssh_opts{initialize_options} );
86              
87 0         0 my @ssh_opts_line;
88              
89 0         0 for my $key ( keys %ssh_opts ) {
90 0         0 push @ssh_opts_line, "-o" => $key . "=" . $ssh_opts{$key};
91             }
92              
93 0         0 my @connection_props = ( "" . $server ); # stringify server object, so that a dumper don't print out passwords.
94 0         0 push @connection_props, ( user => $user, port => $port );
95              
96 0 0       0 if (@ssh_opts_line) {
97 0 0       0 if ( !$net_openssh_constructor_options{external_master} ) {
98 0         0 push @connection_props, master_opts => \@ssh_opts_line;
99             }
100              
101 0         0 push @connection_props, default_ssh_opts => \@ssh_opts_line;
102             }
103              
104 0 0       0 push @connection_props, proxy_command => $proxy_command if $proxy_command;
105              
106 0         0 my @auth_types_to_try;
107 0 0 0     0 if ( $auth_type && $auth_type eq "pass" ) {
    0 0        
108 0         0 Rex::Logger::debug(
109             Rex::Logger::masq(
110             "OpenSSH: pass_auth: $server:$port - $user - %s", $pass
111             )
112             );
113 0         0 push @auth_types_to_try, "pass";
114             }
115             elsif ( $auth_type && $auth_type eq "krb5" ) {
116 0         0 Rex::Logger::debug("OpenSSH: krb5_auth: $server:$port - $user");
117 0         0 push @auth_types_to_try, "krb5";
118              
119             # do nothing here
120             }
121             else { # for key auth, and others
122 0         0 Rex::Logger::debug(
123             "OpenSSH: key_auth or not defined: $server:$port - $user");
124 0         0 push @auth_types_to_try, "key", "pass";
125             }
126              
127 0         0 Rex::Logger::debug("OpenSSH options: ");
128 0         0 Rex::Logger::debug( Dumper( \@connection_props ) );
129 0         0 Rex::Logger::debug("OpenSSH constructor options: ");
130 0         0 Rex::Logger::debug( Dumper( \%net_openssh_constructor_options ) );
131 0         0 Rex::Logger::debug("Trying following auth types:");
132 0         0 Rex::Logger::debug( Dumper( \@auth_types_to_try ) );
133              
134 0         0 my $fail_connect = 0;
135             CONNECT_TRY:
136 0         0 while (
137             $fail_connect < Rex::Config->get_max_connect_fails( server => $server ) )
138             {
139              
140 0         0 for my $_try_auth_type (@auth_types_to_try) {
141              
142 0         0 my @_internal_con_props = @connection_props;
143              
144 0 0       0 if ( $_try_auth_type eq "pass" ) {
    0          
145 0         0 push @_internal_con_props, password => $pass;
146             }
147             elsif ( $_try_auth_type eq "key" ) {
148 0         0 push @_internal_con_props, key_path => $private_key;
149 0 0       0 if ($pass) {
150 0         0 push @_internal_con_props, passphrase => $pass;
151             }
152             }
153              
154             $self->{ssh} =
155 0         0 Net::OpenSSH->new( @_internal_con_props,
156             %net_openssh_constructor_options );
157              
158 0 0 0     0 if ( $self->{ssh} && !$self->{ssh}->error ) {
159 0         0 last CONNECT_TRY;
160             }
161             }
162              
163 0         0 $fail_connect++;
164             }
165              
166 0 0       0 if ( !$self->{ssh} ) {
167 0         0 Rex::Logger::info( "Can't connect to $server", "warn" );
168 0         0 $self->{connected} = 0;
169 0         0 return;
170             }
171              
172 0 0 0     0 if ( $self->{ssh} && $self->{ssh}->error ) {
173             Rex::Logger::info(
174 0         0 "Can't authenticate against $server (" . $self->{ssh}->error() . ")",
175             "warn" );
176 0         0 $self->{connected} = 1;
177              
178 0         0 return;
179             }
180              
181 0         0 Rex::Logger::debug( "Current Error-Code: " . $self->{ssh}->error() );
182 0         0 Rex::Logger::debug("Connected and authenticated to $server.");
183              
184 0         0 $self->{connected} = 1;
185 0         0 $self->{auth_ret} = 1;
186              
187 0         0 eval { $self->{sftp} = $self->{ssh}->sftp; };
  0         0  
188             }
189              
190             sub reconnect {
191 0     0 0 0 my ($self) = @_;
192 0         0 Rex::Logger::debug("Reconnecting SSH");
193              
194 0         0 $self->connect( %{ $self->{__auth_info__} } );
  0         0  
195             }
196              
197             sub disconnect {
198 0     0 0 0 my ($self) = @_;
199 0         0 undef $self->{ssh};
200 0         0 return 1;
201             }
202              
203             sub error {
204 0     0 0 0 my ($self) = @_;
205 0         0 return $self->get_connection_object->error;
206             }
207              
208             sub get_connection_object {
209 0     0 0 0 my ($self) = @_;
210 0         0 return $self->{ssh};
211             }
212              
213             sub get_fs_connection_object {
214 0     0 0 0 my ($self) = @_;
215 0         0 return $self->{sftp};
216             }
217              
218             sub is_connected {
219 1     1 0 2 my ($self) = @_;
220 1         8 return $self->{connected};
221             }
222              
223             sub is_authenticated {
224 0     0 0   my ($self) = @_;
225 0           return $self->{auth_ret};
226             }
227              
228             sub get_connection_type {
229 0     0 0   my ($self) = @_;
230              
231 0           my $type = "OpenSSH";
232              
233 0 0 0       if ( $self->{is_sudo} && $self->{is_sudo} == 1 ) {
234 0           return "Sudo";
235             }
236              
237 0 0 0       if ( Rex::is_ssh() && !Rex::is_sudo() ) {
    0          
238 0           $type = "OpenSSH";
239             }
240             elsif ( Rex::is_sudo() ) {
241 0           $type = "Sudo";
242             }
243              
244 0           return $type;
245             }
246              
247             1;