Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/perl
# awit-ssh - SSH initiator which searches LDAP for host details
# Copyright (c) 2016, AllWorldIT
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
use warnings;
use Getopt::Long;
# Check IO::Socket::INET6
if (!eval {require IO::Socket::INET6; 1;}) {
print STDERR "You're missing IO::Socket::INET6, try 'apt-get install libio-socket-inet6-perl'\n";
exit 1;
}
# Check Net::LDAP
if (!eval {require Net::LDAP; 1;}) {
print STDERR "You're missing Net::LDAP, try 'apt-get install libnet-ldap-perl'\n";
exit 1;
}
# Check Term::ReadKey
if (!eval {require Term::ReadKey; 1;}) {
print STDERR "You're missing Term::ReadKey, try 'apt-get install libterm-readkey-perl'\n";
exit 1;
}
my $VERSION = "0.0.1";
print(STDERR "AWIT-SSH-Client v$VERSION - Copyright (c) 2016, AllWorldIT\n\n");
# Grab options
my %optctl = ();
GetOptions(\%optctl,
"help|?",
"version",
"debug",
"knock=s",
) or exit 1;
# Check for help
if (defined($optctl{'help'})) {
displayHelp();
exit 0;
}
# Check for version
if (defined($optctl{'version'})) {
displayVersion();
exit 0;
}
# Pull in hostname
my $hostSpec = shift(@ARGV) // "";
my ($host,$port) = split(':',$hostSpec);
if (!defined($host)) {
logger('ERROR',"No hostname provided\n");
displayHelp();
exit 1;
}
# Check if we should be doing port knocking
if (defined(my $knock = $optctl{'knock'})) {
# If so, split off the host and the port
my ($host,$port) = split(':',$knock);
if (!defined($port)) {
logger('ERROR',"Port knock specifications should be in the format of HOST:PORT");
exit 1;
}
print STDERR "Port knocking '$host' on port '$port'...";
# Do the port knock
my $sock = IO::Socket::INET6->new(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
Timeout => 3
);
# We should get a failure of "Connection refused", if not ERR
if (defined($sock) || $! ne "Connection refused") {
print STDERR "FAILED\n";
exit 1;
}
print STDERR "success\n";
}
print STDERR "Your LDAP CN : ";
my $username = <STDIN>;
print STDERR "Your LDAP Password: ";
# Don't echo password
Term::ReadKey::ReadMode('noecho');
chomp(my $password = <STDIN>);
# Turn echo back on
Term::ReadKey::ReadMode(0);
print STDERR "\n";
my $ldap = Net::LDAP->new('ldaps://XXX',
# 'debug' => 15
);
if (!defined($ldap)) {
logger('ERROR',"Failed to setup LDAP object '%s'",$@);
exit 2;
}
# Bind
my $mesg = $ldap->bind("cn=$username,ou=Users,YYY",password => $password);
# Search
$mesg = $ldap->search(
base => "ou=Servers,ZZZ",
filter => "(|(cn=$host)(awitLoginHost=$host)(awitLoginHostAlias=$host))",
);
# Check for error
if ($mesg->code()) {
logger('ERROR',"LDAP returned '%s'",$mesg->error());
exit 2;
}

Nigel Kukard
committed
# Some flags we may need
my $needDSS;
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# If no matches
my @ldapResults = $mesg->entries();
my $ldapNumResults = @ldapResults;
my $ldapEntry;
if ($ldapNumResults < 1) {
logger('NOTICE',"No LDAP results, using defaults");
} elsif ($ldapNumResults == 1) {
$ldapEntry = $ldapResults[0];
} elsif ($ldapNumResults > 1) {
logger('WARNING',"Found multiple entries!");
while (my ($cn,$item) = %{$mesg->as_struct()}) {
logger('WARNING'," %s",$item->{'cn'});
}
exit 3;
}
print STDERR "\n";
# Check if we got a result and modify our connection details
if ($ldapEntry) {
my $ldapEntryName = $ldapEntry->get_value('cn');
logger('INFO',"Found server entry '$ldapEntryName'");
# Check if we need to set the host
if (my $ldapLoginHost = $ldapEntry->get_value('awitLoginHost')) {
logger('INFO'," - Host %s (awitLoginHost)",$ldapLoginHost);
$host = $ldapLoginHost;
}
# Check if we need to set the port
if (my $ldapLoginPort = $ldapEntry->get_value('awitLoginPort')) {
logger('INFO'," - Port %s",$ldapLoginPort);
$port = $ldapLoginPort;
}
# Check if we have a description
if (my $ldapDescription = $ldapEntry->get_value('description')) {
logger('INFO',"Description");
foreach my $line (split(/\n/,$ldapDescription)) {
logger('INFO'," %s",$line);
}

Nigel Kukard
committed
# Hack'ish ... look if the description mentions dss is required...
if ($ldapDescription =~ /needs ssh-dss/i) {
$needDSS = 1;
}
}
# Check if we have a wiki page
if (my $ldapLoginWikiPage = $ldapEntry->get_value('awitLoginWikiPage')) {
logger('INFO',"Wiki Page '%s'",$ldapLoginWikiPage);
}
print STDERR "\n";
}
# TODO for forawarding
my @sshArgs = ();
# Check if we have a port defined, if so specify it
if (defined($port)) {
push(@sshArgs,'-p',$port);
}

Nigel Kukard
committed
# If the server is ancient, we need to enable DSS
if (defined($needDSS)) {
push(@sshArgs,'-o','PubkeyAcceptedKeyTypes=+ssh-dss');
push(@sshArgs,'-o','HostbasedKeyTypes=+ssh-dss');
push(@sshArgs,'-o','HostKeyAlgorithms=+ssh-dss');
}
logger('NOTICE',"Connecting to host '$host'" . (defined($port) ? " on port '$port'" : "") . "...\n\n\n");
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
exec('/usr/bin/ssh',
'-F',$ENV{"HOME"}.'/.ssh/config',
'-o','PreferredAuthentications=publickey,keyboard-interactive',
'-o','StrictHostKeyChecking=yes',
'-o','ServerAliveInterval=5',
'-o','ConnectTimeout=3',
'-o','Compression=yes',
'-o','CompressionLevel=1',
'-o','ExitOnForwardFailure=yes',
'-o','ControlMaster=ask',
'-o','ControlPath=~/.ssh/master-%C',
@sshArgs,
$host
);
# Log something
sub logger
{
my ($level,$arg1,@args) = @_;
printf(STDERR '%-7s: '.$arg1."\n",$level,@args);
}
# Display version
sub displayVersion
{
print("Version: $VERSION\n");
}
# Display usage
sub displayHelp
{
print(STDERR<<EOF);
Usage: $0 <options>
General Options:
--help What you're seeing now.
--version Display version.
--debug Enable debugging.
Port Knocking:
--knock HOST:PORT Port knock a host to get access.
EOF
}