07
2022
10
09:40:55

CentOS监控ssh免密登录

CentOS监控ssh免密登录


ssh免密登录在带来方便的同时也带来一些问题,那就是不知道什么时间什么人利用ssh免密通道登录服务器了,为此我们需要在sshd的配置文件里设置好详细日志,以便日后回溯。

CentOS里,sshd的日志文件是保存在/var/log/secure里的,如果不进行特殊设置的话,它只记录一些最简单的信息,比如什么时间哪个账号被人用免密登录的方式登录了,如果这个账号的authorized_keys里有很多key的话,这样的log没有办法告诉你客户到底是用哪个key登录的。

所以,我们需要在sshd的配置文件/etc/ssh/sshd_config文件里找到以下项LogLevel并把它改成LogLevel VERBOSE,这一项的缺省值是INFO,不够详细,所以我们需要把它改成啰嗦模式,这样可以记录更多信息。

改成VERBOSE并重启sshd(service sshd restart)后,我们会在日志文件里看到类似于这样的记录:

Apr  1 10:37:06 hostname sshd[5903]: Found matching RSA key: 83:67:b5:c7:bb:17:4d:06:ca:dc:8b:ca:85:cc:0c:b1

但这样的信息明显不同于我们在authorized_keys里存放的信息,那该怎么办呢?实际上,在sshd的日志文件里存储的只是我们authorized_keys的指纹信息fingerprint,不是真正的key,必须从authorized_keys反算出fingerprint来,才能做对比:

ssh-keygen -E md5 -lf /home/someuser/.ssh/authorized_keys

但是这样依然很麻烦,有没有办法直接告诉我日志里到底是谁登录的呢?为此我们还需要引入一个用Perl写的小程序。(原作者写的略有问题,在新版的CentOS里必须要求附加md5参数,为此我做了一些小的修改):

#!/usr/bin/perl -wuse strict;use diagnostics;use File::Temp;# Matches Fingerprints from sshd logs (sshd on loglevel VERBOSE) against# authorized_keys for the respective user.die "Please specify input file!\n" unless ($ARGV[0]);my $fingerprints;my $nav = File::Navigate->new($ARGV[0]);# Store publickey login eventsmy @lines = @{$nav->find(qr/sshd\[\d+\]: Accepted publickey for .+ from .+ port \d+/)};# Process all publickey login eventsforeach(@lines){
    $nav->cursor($_);    my $line = $nav->get();
    $line =~ /^(.{15}).+sshd\[(\d+)\]: Accepted publickey for (.+) from (.+) port (\d+)/;    my $date = $1;    my $pid  = $2;    my $user = $3;    my $ip   = $4;    my $port = $5;    my $fp   = "unknown"; # (yet)
    # Seek backwards to find matching fingerprint line
    my $sought = 0;    while ((my $seekline = $nav->getprev()) and ($sought++ < 1000)){        if ($seekline =~ /sshd\[$pid\]: Found matching .+ key: (.+)/){
            $fp = $1;            last;
        }elsif($line =~ /sshd\[$pid\]: Connection from $ip port $port/){            last;
        }
    }    my $key = get_key($fp, $user);    print "\"$date\";\"$user\";\"$fp\";\"$key\"\n";
}sub get_key{    my $fp   = shift;
    $fp = "MD5:" . $fp;    my $user = shift;    # See if FP is cached
    if ($fingerprints->{$user}){        if ($fingerprints->{$user}->{$fp}){            return $fingerprints->{$user}->{$fp};
        }else{            return "No matching key found.";
        }
    }    # Else, generate fingerprints from users authorized_keys
    print STDERR "------> Reading keys for user $user\n";    my $home = (getpwnam($user))[7];    open my $fh_in, "<$home/.ssh/authorized_keys" or warn "No such file: $home/.ssh/authorized_keys\n";    while (<$fh_in>){        chomp;        next unless (/^ssh-/);        my $out_fh = File::Temp->new();        print $out_fh "$_\n";        close $out_fh;        my $fp_raw = `ssh-keygen -E md5 -lf $out_fh`;        # Second field of output has the fingerpring
        my $fp = (split /\s+/, $fp_raw)[1];
        $fingerprints->{$user}->{$fp} = $_;
    }    if ($fingerprints->{$user}->{$fp}){        return $fingerprints->{$user}->{$fp};
    }else{        return "No matching key found.";
    }
}package File::Navigate;use strict;use warnings;=head1 NAME

File::Navigate - Navigate freely inside a text file

=head1 DESCRIPTION

The module is a glorified wrapper for tell() and seek().

It aims to simplify the creation of logfile analysis tools by
providing a facility to jump around freely inside the contents
of large files without creating the need to slurp excessive
amounts of data.

=head1 SYNOPSIS

  use File::Navigate;
  my $nav = File::Navigate->new('/var/log/messages');

  # Read what's below the "cursor":
  my $first = $nav->get;

  # Advance the cursor before reading:
  my $second = $nav->getnext;
  my $third  = $nav->getnext;

  # Advance the cursor by hand:
  $nav->next;
  my $fourth = $nav->get;

  # Position the cursor onto an arbitrary line:
  $nav->cursor(10);
  my $tenth  = $nav->get;

  # Reverse the cursor one line backward:
  $nav->prev;
  my $ninth  = $nav->get;

  # Reverse the cursor before reading:
  my $eigth  = $nav->getprev;

  # Read an arbitrary line:
  my $sixth  = $nav->get(6);

=cutour @ISA       = qw(Exporter);our @EXPORT_OK = qw();our $VERSION   = '1.0';=head1 CLASS METHODS

=head2 I<new()>

Open the file and create an index of the lines inside of it.

  my $mapper = File::Navigate->new($filename);

=cutsub new($){    my $class = shift;    my $file;    unless ($file = shift){        die "No file specified\n";
    }    unless (-e $file){        die "File not found: $file\n";
    }    unless (-r $file){        die "File not readable: $file\n";
    }    my $self = {};
       $self->{'cursor'}         = 1;
       $self->{'lineindex'}      = {};
       $self->{'lineindex'}->{1} = 0;    open my $fh, "$file"
        or die "Can't open $file: $!\n";    while (<$fh>){        my $thisline = $.;        my $nextline = $thisline + 1;
        $self->{'lineindex'}->{$nextline} = tell $fh;
    }
    $self->{'length'} = scalar(keys %{$self->{'lineindex'}}) - 1 ;
    $self->{'fh'} = $fh;    bless $self;
}=head1 OBJECT METHODS

=head2 I<count()>

Returns the number of lines in the file ("wc -l")

  my $lines = $nav->count;

=cutsub length(){    my $self = shift;    return $self->{'length'};
}=head2 I<cursor()>

Returns the current cursor position and/or sets the cursor.

  my $cursor = $nav->cursor();   # Query cursor position.
  my $cursor = $nav->cursor(10); # Set cursor to line 10

=cutsub cursor($){    my $self = shift;    if (my $goto = shift){
        $self->{'cursor'} = $goto;
    }    return $self->{'cursor'};
}=head2 I<get()>

Gets the line at the cursor position or at the given position.

  my $line = $nav->get();   # Get line at cursor
  my $line = $nav->get(10); # Get line 10

=cutsub get($){    my $self = shift;    my $fh   = $self->{'fh'};    my $getline;
    $getline = $self->{'cursor'} unless ($getline = shift);    if ($getline < 1){        warn "WARNING: Seek before first line.";        return undef;
    }elsif($getline > $self->{'length'}){        warn "WARNING: Seek beyond last line.";        return undef;
    }    seek ($fh, $self->{'lineindex'}->{$getline}, 0);    my $gotline = <$fh>;    chomp $gotline;    return $gotline;
}=head2 I<next()>

Advance the cursor position by one line. Returns the new cursor position.
Returns I<undef> if the cursor is already on the last line.

  my $newcursor = $nav->next();

=cutsub next(){    my $self = shift;    if ($self->{'cursor'} == $self->{'length'}){        return undef;
    }
    $self->{'cursor'}++;    return $self->{'cursor'};
}=head2 I<prev()>

Reverse the cursor position by one line. Returns the new cursor position.
Returns I<undef> if the cursor is already on line 1.

  my $newcursor = $nav->prev();

=cutsub prev(){    my $self = shift;    if ($self->{'cursor'} == 1){        return undef;
    }
    $self->{'cursor'}--;    return $self->{'cursor'};
}=head2 I<getnext()>

Advance to the next line and return it.
Returns I<undef> if the cursor is already on the last line.

  my $newcursor = $nav->getnext();

=cutsub getnext(){    my $self = shift;
    $self->next or return undef;    return $self->get;
}=head2 I<getprev()>

Reverse to the previous line and return it:
Returns I<undef> if the cursor is already on line 1.

  my $newcursor = $nav->getprev();

=cutsub getprev(){    my $self = shift;
    $self->prev or return undef;    return $self->get;
}=head2 I<find()>

Find lines containing given regex. Returns array with line numbers.

  my @lines = @{$nav->find(qr/foo/)};

=cutsub find($){    my $self = shift;    my $regex = shift;    my @results;    for (my $lineno = 1; $lineno <= $self->{'length'}; $lineno++){        my $line = $self->get($lineno);            if ($line =~ $regex){            push @results, $lineno;
        }
    }    return \@results;
}sub DESTROY(){    my $self = shift;    close $self->{'fh'};
}=head1 EXAMPLE

I<tac>, the opposite of I<cat>, in Perl using File::Navigate:

  #!/usr/bin/perl -w
  use strict;
  use File::Navigate;

  foreach my $file (reverse(@ARGV)){
          my $nav = File::Navigate->new($file);
          # Force cursor beyond last line
          $nav->cursor($nav->length()+1);
          print $nav->get()."\n" while $nav->prev();
  }

=head1 BUGS

Seems to lack proper error handling.

=head1 LIMITATIONS

Works only on plain text files. Sockets, STDIO etc. are not supported.

=head1 PREREQUISITES

Tested on Perl 5.6.1.

=head1 STATUS

Mostly harmless.

=head1 AUTHOR

Martin Schmitt <mas at scsy dot de>

=cut1;

为此我们给它起名叫match-ssh-keys,赋予它可执行权限(chmod +x match-ssh-keys),然后把它搬到/usr/local/bin里(mv match-ssh-keys /usr/local/bin/),这样我们以后再想查谁通过sshd免密登录过服务器就方便了,我们只需要执行:

match-ssh-keys /var/log/secure

就可以了。




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/2771.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: