Упрощаем жизнь IT отделу

Ответить
Kitsum
Сообщения: 15
Зарегистрирован: 01 окт 2014, 13:03
Откуда: далеко из-за мкада

Упрощаем жизнь IT отделу

Сообщение Kitsum » 11 фев 2015, 13:15

Всем доброе время суток
Данный пост не носит никакого познавательного характера, скорее просто попытка поделиться идеей и подискутировать.

Перейдем к сути.
В корпоративной сети используется Active Directory, а на дальних рубежах крутится Apache держащий на одной из своих страничек справочник с Mail адресами внутренней почты, телефонов и прочей информации. Самое главное, что имеется третий участник на этой кухне - DNS сервер, с записями совпадающими с Mail адресами пользователей.
Легко сделать вывод, что зная почту пользователя, можно узнать и ip машины на которой он работает. Таким образом, к обычному справочнику, по мере необходимости, можно прикручивать нужный функционал для IT коллектива.

Естественно, что на чужие сервера Вашего слугу никто не пустит. Поэтому, на велосипеде из PHP + небольшой магии, была слеплена программка имеющая на борту Chrome и занимающаяся перехватом mail: ссылок.

Это позволило реализовать:
  • 1. Удаленное управление
    • Протокол VNC
      Протокол RDP
    2. Администрирование
    • Удаленное управление при помощи средства "Управление компьютером" (compmgmt.msc)
    3. Информация
    • Active Directory
      • Список установленных программ
        Операционная система
        Список запущенных программ
        Список установленных принтеров
      Проверка соединения (ping)
      Трассировка маршрута (tracert)

      Информация о машине
      Информация о пользователе в домене
    4. Сетевой доступ
    • Доступ к общим файлам и принтерам
    5. Управление электропитанием
    • Перезагрузить
      Выключить
    6. Отправить пользователю письмо (не забыли)
Понятно, что все это и еще 99% остальных возможностей, доступных любому администратору, можно выполнить ручками, но! Все они раскиданы тут и там + постоянная рутина по вбиванию одних и тех же длинных команд (в которых меняется только имя хоста) ...
И ко всему, в нашем (Ваш покорный слуга + два боевых товарища) случае ситуацию омрачает наличием 700-а душ за которые мы отвечаем, а точнее за персональную технику которой они пользуются.

Краткий пример
mailit.png
Кому интересно, при первом запуске создаётся файл config.ini в котором необходимо указать http ссылку на страницу адресной книги.

Код: Выделить всё

[chrom]
url="http://localhost/"
Выкладываю на dropbox: https://www.dropbox.com/s/a73w7oq24ppsf ... t.zip?dl=0
Весит 20mb из-за Chrome-а в комплекте.

PS: естественно это все будет работать если у Вас аналогичная ситуация с DNS и Mail. Часть функционала работает только при наличии прав администратора AD.

Kitsum
Сообщения: 15
Зарегистрирован: 01 окт 2014, 13:03
Откуда: далеко из-за мкада

Re: Упрощаем жизнь IT отделу

Сообщение Kitsum » 16 фев 2015, 14:37

Доброе время суток.

Появилась потребность видеть кто где на свичах сидит, а если быть точным, то на каком порту какой MAC/IP/Производитель железа.
Свичей много (целый зоопарк), как и агрегатов на которых они расположены. Ходить к каждому на морду не удобно, тем более свичи знают только MAC адреса.
Каждый агрекат закрыт маршрутизатором Cisco и это огромный плюс т.к кошка имеет ARP таблицу и знает кто есть кто.
Drawing3.jpg
Drawing3.jpg (19.49 КБ) 11505 просмотров
Нам остаётся только спросить у нужного свича таблицу MAC адресов, а у кошки ARP таблицу и свести все это в единую картину. Смотреть на итоговый результат захотелось через Web.

Для реализации задуманного сетевое оборудование должно поддерживать SNMP и стандарт iso, на web сервере установлен PHP и к нему модуль php5-snmp

Как это должно выглядеть (многое замазано, но в целом понятно):
sw1.png
Перейдем к реализации.
На Web сервере создаем каталог (например map, именно он фигурирует в исходниках, исправьте на свой) который должен содержать три файла:

.htaccess

Код: Выделить всё

DirectoryIndex index index.php
DirectorySlash off
Options -Indexes -MultiViews

RewriteEngine On
RewriteBase /map/

RewriteCond %{REQUEST_URI} \.(css|jpg|gif|png|zip|rar|doc|xls|js|tif|tiff|docx|xlsx|ico)$
    RewriteRule ^(.*)$ $1 [L,QSA]

RewriteCond %{ENV:NS}    !=1
RewriteCond %{IS_SUBREQ} =true
    RewriteRule (.*) $1 [L,QSA]

RewriteCond %{REQUEST_URI} ^/index$ [OR]
RewriteCond %{REQUEST_URI} ^/index[.]+(\w+)$
    RewriteRule . / [R=301,L]
    
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} (.*)/$
    RewriteRule . %1 [R=301,L,E=NS:1,QSA]
    
RewriteCond %{REQUEST_URI} ^[\w\-.]+$
RewriteCond %{REQUEST_FILENAME} (.*)\.(html|php)$
RewriteCond %1.php -s [OR]
RewriteCond %1.html -s
    RewriteRule . %1.%2 [L,QSA]
    
    RewriteRule (.*) index.php?$1 [L,QSA]
Необходимо изменить RewriteBase /map/ на Ваш путь.
Я не силен в работе с Апачем, поэтому файл писался по различным мануалам. Главная задача .htaccess - создание читаемых ссылок вида:
http://localhost/map/имя_агрегата/номер_свича
Очень много лишнего для будущих модификаций скрипта.

index.php

Код: Выделить всё

<?PHP
#error_reporting(E_ALL);
header("Content-Type: text/html; charset=windows-1251");

$_GET = array();
$param = explode('/', $_SERVER['REQUEST_URI']);
if(isset($param[2]) and $param[2] != '') $_GET['unit'] = $param[2];
if(isset($param[3]) and $param[3] != '') $_GET['switch'] = $param[3];

$url = 'http://localhost/map/';
$community = 'public';
$unit = array(
    'servernaya1' => array(
        'router' => '10.10.10.1',
        'switch' => array('192.168.1.2'),
    ),
    'mashzal15' => array(
        'router' => '10.10.10.2',
        'switch' => array('192.168.2.2', '192.168.2.3'),
    ),
);

# Список используемых OID
$sysDescr          = '.1.3.6.1.2.1.1.1';            # описание свича
$dot1dTpFdbAddress = '.1.3.6.1.2.1.17.4.3.1.1';     # список MAC адресов на свиче
$dot1dTpFdbPort    = '.1.3.6.1.2.1.17.4.3.1.2';     # список расположения MAC по портам на свиче
$atPhysAddress     = '.1.3.6.1.2.1.3.1.1.2';        # список соответствия MAC и IP взятый с маршрутизатора

echo <<<HERE
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=windows-1251">
        <title>Список свичей</title>
        <style>
body, table, tr, td {
    font-style: normal;
    font-family: verdana, arial, helvetica, sans-serif;
    font-size: 11px;
}
body {
    background-color: #FFEBD5;
}
tr, td {
    padding: 2px;
}
#head {
    font-weight: bold;
    background-color: #9CB7D1;
    margin: 2px;
    border-radius: 2px;
}
td#head {
    color: black;
}
a:link, a:visited, a:active { 
    text-decoration: underline;
    color: white;
}
a:hover { 
    text-decoration: none;
    color: white;
}
#activ {
    color: #FFEBD5;
} 
#port {
    font-weight: bold;
    background-color: #9CB7D1;
    margin: 2px;
    border-radius: 2px;
}
#num {
    color: black;
}
td#title {
    background-color: #CED7DF;
    font-weight: bold;
    border-radius: 2px;
    color: black;
}
td#host1 {
    font-family: Courier New, monospace;
    font-weight: bold;
    background-color: #F2F7FA;
    border-radius: 2px;
    color: black;
}
td#host2 {
    font-family: Courier New, monospace;
    font-weight: bold;
    background-color: #E1E7EC;
    border-radius: 2px;
    color: black;
}
        </style>
    </head>
    <body>
<table width="100%" id="head">
   <tr>
      <td width="100%">
<table width="100%" id="head">
   <tr>
      <td width="122">Список агрегатов:</td>
      <td id="head">
HERE;
if(count($unit) > 0) {
    foreach($unit as $name => $info) $u[] = '<a href="'.$url.$name.'"'.((isset($_GET['unit']) and $_GET['unit'] == $name)?' id="activ"':'').'>'.$name.'</a>';
}
echo isset($u)? implode(' ', $u) : 'Нет доступных агрегатов';
echo <<<HERE
      </td>
   </tr>
   <tr>
      <td width="122">Доступные свичи:</td>
      <td id="head">
HERE;
if(count($unit[$_GET['unit']]['switch']) > 0 and isset($unit[$_GET['unit']])) {
    foreach($unit[$_GET['unit']]['switch'] as $id => $name) {
        $s[] = '<a href="'.$url.$_GET['unit'].'/'.$id.'"'.((isset($_GET['switch']) and $_GET['switch'] == $id)?' id="activ"':'').'>'.$name.'</a>';
    }
    echo isset($s)? implode(' ', $s) : 'Нет доступных свичей';
}
else {
    echo 'Не выбран агрегат';
}
echo <<<HERE
      </td>
   </tr>
</table>
      </td>
    </tr>
</table>
<table width="100%" id="head">
   <tr>
      <td width="100%">
<table width="100%" id="head">
   <tr>
      <td id="head">
HERE;
if(isset($_GET['unit'], $_GET['switch'], $unit[$_GET['unit']]['switch'][$_GET['switch']])) {
    $session = new SNMP(SNMP::VERSION_1, $unit[$_GET['unit']]['switch'][$_GET['switch']], $community); 
    $session->valueretrieval = SNMP_VALUE_LIBRARY;
    $session->oid_output_format = SNMP_OID_OUTPUT_NUMERIC;
    $session->oid_increasing_check = false;

    foreach($session->walk($sysDescr) as $id => $val) { 
        echo (($name = str_replace('STRING: ', 'Свич ', $val)) != '""')? $name : 'Устройство не передало информацию о себе';
    }
    $session->close();
}
else echo '&nbsp;';
echo <<<HERE
      </td>
   </tr>
</table>
      </td>
   </tr>
</table>
HERE;

if(isset($_GET['unit'], $_GET['switch'], $unit[$_GET['unit']]['router'], $unit[$_GET['unit']]['switch'][$_GET['switch']])) {
    $mac = $port = $map = array(); 

    $session = new SNMP(SNMP::VERSION_1, $unit[$_GET['unit']]['router'], $community); 
    $session->valueretrieval = SNMP_VALUE_LIBRARY;
    $session->oid_output_format = SNMP_OID_OUTPUT_NUMERIC;
    $session->oid_increasing_check = false;

    $info = @$session->walk($atPhysAddress);
    if($session->getErrno() == 0 and count($info) > 0) {
        foreach($info as $id => $val) { 
            $id = str_replace($atPhysAddress.'.', '', $id); 
            $val = explode(': ', $val); 
            $val = trim($val[1]);
            # Велосипед, дабы не делать еще один запрос, а надо
            $r = explode('.', $id);
            unset($r[0], $r[1]);
            $id = implode('.', $r);
        
            $route[$val] = $id; 
        }
    }
    $session->close(); 
 
    $session = new SNMP(SNMP::VERSION_1, $unit[$_GET['unit']]['switch'][$_GET['switch']], $community);
    $session->valueretrieval = SNMP_VALUE_LIBRARY; 
    $session->oid_output_format = SNMP_OID_OUTPUT_NUMERIC;
    $session->oid_increasing_check = false;
    $info = @$session->walk($dot1dTpFdbPort);
    if($session->getErrno() == 0 and count($info) > 0) {
        foreach($info as $id => $val) { 
            $id = str_replace($dot1dTpFdbPort.'.', '', $id); 
            $val = explode(': ', $val); 
            $val = trim($val[1]);
            if($val != 0) $port[$id] = $val;
        }
    }
    $info = @$session->walk($dot1dTpFdbAddress);
    if($session->getErrno() == 0 and count($info) > 0) {
        foreach($info as $id => $val) { 
            $id = str_replace($dot1dTpFdbAddress.'.', '', $id); 
            $val = explode(': ', $val); 
            $val = trim($val[1]);
            $table[$id] = $val;
            if(isset($port[$id])) {
                $map[$port[$id]][] = array('MAC' => $val, 'IP' => (isset($route[$val])? $route[$val] : 'неизвестен'));
            }
        }
    }
    $session->close(); 
 
    if(isset($map) and count($map) > 0) {
        ksort($map);
        foreach($map as $port => $info) {
            $i = 1;
            echo '<table width="100%" id="port"><tr><td width="100%">';
            echo '<table width="100%"><tr><td colspan="3"><b id="num">Порт #'.$port.'</b></td></tr>';
            echo '<tr><td id="title" width="122">MAC</td><td id="title" width="122">IP</td><td id="title">Производитель</td></tr>';
            foreach($info as $num => $host) {
                $vendor = explode('(hex)', shell_exec("cat ./oui.txt | grep ".str_replace(' ', '-', substr($host['MAC'], 0, 8))));
                $vendor = isset($vendor[1])? trim($vendor[1]) : 'неизвестен'; 
                $css = ((($i++)%2!=0)?'1':'2');
                echo '<tr><td id="host'.$css.'" width="122">'.$host['MAC'].'</td><td id="host'.$css.'" width="122">'.$host['IP'].'</td><td id="host'.$css.'">'.$vendor.'</td></tr>'; 
            }
            echo '</table>';
            echo '</td></tr></table>';
        }
    }
    else {
        echo '<table width="100%" id="head"><tr><td width="100%"><table width="100%" id="head"><tr><td id="head">Свич не сообщил таблицу MAC адресов</td></tr></table></td></tr></table>';
    }
}
else {
    echo '<table width="100%" id="head"><tr><td width="100%"><table width="100%" id="head"><tr><td id="head">1. Выберите агрегат<br>2. Выберите интересующий Вас свич</td></tr></table></td></tr></table>';
}
echo <<<HERE
    </body>
</html>
HERE;
?>
Сам скрипт. Конечно не красив, но для начала вполне сойдет.
Что нужно исправить:
1. Думаю в описании не нуждается

Код: Выделить всё

$url = 'http://localhost/map/';
$community = 'public';
2. Карта свичей. router содержит адрес маршрутизатора с которого будем брать ARP таблицу, а switch - список свичей в подсети с которых будем брать таблицу MAC адресов. servernaya1 и mashzal15 - названия агрегатов, будут фигурировать в адресах страницы.

Код: Выделить всё

$unit = array(
    'servernaya1' => array(
        'router' => '10.10.10.1',
        'switch' => array('192.168.1.2'),
    ),
    'mashzal15' => array(
        'router' => '10.10.10.2',
        'switch' => array('192.168.2.2', '192.168.2.3'),
    ),
);
oui.txt
Его содержимое можно взять отсюда http://standards-oui.ieee.org/oui.txt
Файл необходимо для получения производителя железки в сети по MAC адресу.
Если есть желание, можно добавить запись в Cron и раз в месяц обновлять базу.

Для тех у кого проблемы с кодировкой, скидываю файл чтобы не заниматься копи-пастом.
index.zip
(2.37 КБ) 247 скачиваний
PS: в целом все работает отлично. Рекомендую ограничить доступ к web морде. Могут быть проблемы с .htaccess в последней версии Апача под FreeBSD. У меня все прекрасно работает из коробки под Linux:
Apache/2.4.6 (Ubuntu)
PHP 5.5.3-1ubuntu2.6

Ответить