Ssh clients blocken

Aus

Login Fehler im ssh feststellen und Clients blocken

Voraussetzungen

  • ssh muss unter xinetd laufen
  • läuft nur für ssh
  • im xinetd conf file für ssh MUSS eine Zeile no_access = enthalten sein (kann auch leer sein hinter dem =)
  • muss als root laufen
  • kann erst reagieren wenn es aufgerufen wurde (wget oder php Shell-Interpreter)
  • das Logfile sollte keine zu alten Einträge enthalten. Ich verwende dazu syslog-ng und logrotate (beide via ipkg). mit dem aktuellsten Code kann man festlegen wie alt die zu beachtenden Einträge maximal sein dürfen

TODO

  • Erweiterung auf beliebige Protokolle
  • Überwachung aller Services unter xinetd
  • Gesperrte IPs nach bestimmter Zeit wieder löschen

WICHTIG

Den Code habe ich mehrfach bei mir zu Hause getestet (DS408). Es wird aber an Systemfiles rumgeschrieben. Schlimmstenfalls startet der xinetd nicht mehr. Ich konnte in allen Test keine Fehler feststellen, trotzdem geht der Einsatz komplett auf die eigene Kappe

<?php
//Array of Protocols to search in the log files
$protocolsToWatch = array('sshd');
//Path of config directory for xinetd services
$xinetdServiceConf = '/opt/etc/xinetd.d';
//Array of filenames for xinetd services config files
$xinetdConfFiles = array('ssh');
//Path of log file
$logPath = '/opt/var/log/auth.log';
//Maximum allowed failed logins tries
$maxLogonTries = 2;
//Minimum time between last and second last failed login
$minTimeBetweenLogins = 10;
//Login Failures older than this will be ignored
$maxAgeLastLogin = 600;
/**
 * Initialo function to read full content of syslog file
 *
 * @param string path Path of the log file
 * @return array Content of Log File
 */
function getRawLog($path){
  return file($path);
}
/**
 * Function to filter the raw data from syslog for failed logins with defined protocols
 *
 * @param array $content Array of syslog lines
 * @return array A array of filtered log lines
 */
function filterRecords($content){
  $cont = array();
  foreach($content as $wert){
    if(stripos($wert,'failed') !== false){
      preg_match('/.*'.$protocolsToWatch[0].'.*/',$wert,$temp);
    }else{
      continue;
    }
    //$content[] = $temp[0];
    if(count($temp) > 0) $cont[] = $temp[0];
  }
  return $cont;
}
/**
 * Function to get IP adresses and access times from filtered log lines
 *
 * @param array $content Array of filtered log lines
 * @return array Array of IPs and Access Times => array[IP_ADDR] = array(TIME_OF_TRY,TIME_OF_TRY...)
 */
function processRecords($content){
  $cont = array();
  //var_dump($content);
  foreach($content as $wert){
    preg_match('/^([a-zA-z]*)\s(\d{1,2})\s(\d{1,2}:\d{1,2}:\d{1,2}).*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/',$wert,$temp);
    $cont[$temp[4]][] = strtotime($temp[1].' '.$temp[2].' '.$temp[3]);
  }
  return $cont;
}
/**
 * Function for check the log records (return of proccessRecords()) for violations of Login Rules
 *
 * This functions checks the log records and adds ip-addresss to be blocked if:
 * * more than $maxLogonTries failed attempts to login are detected AND
 * * the time difference between the last failed login and the second last is less than $minTimeBetweenLogins seconds
 *
 * @param array $content Array of ips and access times from syslog array[IP_ADDR] = array(TIME_OF_TRY,TIME_OF_TRY...)
 * @return array Array of ips to be blocked
 */
function determineViolations($content){
  $cont = array();
  //var_dump($content);
  //exit;
  foreach($content as $key=>$wert){
    if(count($wert) >= $GLOBALS['maxLogonTries']){
      sort($wert);
      //var_dump($wert);
      //exit;
      if(($wert[count($wert)-1]-$wert[count($wert)-2] < $GLOBALS['minTimeBetweenLogins']) && (time() - $wert[count($wert)-1] <= $GLOBALS['maxAgeLastLogin'])){
        $cont[] = $key;
      }else{
        continue;
      }
    }
  }
  //var_dump($cont);
  //exit;
  return $cont;
}
/**
 * Function to block a certain IP Adress in xinet Service Conf
 *
 * This function expects a array with IP addresses to block
 * @param array $ip
 * @return boolean true for block action and false for no block action
 */
function blockIP($ip){
  //var_dump($ip);
  //exit;
  $block = false;
  $str = '';

  $content = file($GLOBALS['xinetdServiceConf'].'/'.$GLOBALS['xinetdConfFiles'][0]);
  $t = array();
  $schl = null;
  foreach($content as $key=>$wert){
    if(strpos($wert,'no_access') !== false){
      preg_match_all('/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/',$wert,$t);
      //var_dump($t);
      //exit;
      break;
    }
  }
  //var_dump($t);
  //exit;
  foreach($ip as $ipp){
    if(in_array($ipp,$t[0]) === false){
      $block = true;
      $str .= $ipp.' ';
    }
  }
  $content[$key] = 'no_access = '.implode(' ',$t[0]).' '.$str."\n";
  if($block === true){
    $t = implode('',$content);
    $fp = fopen($GLOBALS['xinetdServiceConf'].'/'.$GLOBALS['xinetdConfFiles'][0],'w');
    fwrite($fp,$t);
    fclose($fp);
    exec('kill -HUP `pidof xinetd`');
    return true;
  }else{
    return false;
  }
}
var_dump(blockIP((determineViolations((processRecords(filterRecords(getRawLog($logPath))))))));
?>

Quellcode

Leider lässt sich der PHP Code hier im Wiki nicht als Attachment anfügen. Der Quellcode als Textdatei liegt im Forum