Automātiskā nodroŔināŔana Yealink T19 + dinamiskā adreŔu grāmata

Kad atnācu strādāt uz Å”o uzņēmumu, man jau bija kāda IP ierīču datubāze, vairāki serveri ar zvaigznÄ«ti un ielāps FreeBPX formātā. Turklāt analogais PBX Samsung IDCS500 strādāja paralēli un kopumā bija galvenā saziņas sistēma uzņēmumā; IP telefonija darbojās tikai pārdoÅ”anas nodaļā. Un viss bÅ«tu turpinājis gatavoties Ŕādi, bet vienā jaukā dienā tika dots rÄ«kojums par visiem pārcelÅ”anu uz IP telefoniju, tika saskaņoti termiņi, iegādāts aprÄ«kojums un sāka Ä«stenot plānu uzņēmumu pārcelt uz 21. gadsimtu.
Pirmais, kas Ŕādā situācijā sāk uztraukties, ir strauji augoÅ”ais telefona aparātu skaits, kas kaut kā jāpārvalda, otrs, kas ļoti satrauca bija tālruņu grāmata. Ja Endpoint Manager varēja mums palÄ«dzēt ar pirmo (kas, starp citu, tika izgriezta no jaunākajām FreePBX versijām), tad saistÄ«bā ar grāmatu radās daži jautājumi:

  • Pirmkārt, kā nodroÅ”ināt tā precizitāti, kad lietotāju atraÅ”anās vieta/plÅ«stamÄ«ba nepārtraukti mainās?
  • Otrkārt, kā pilnÄ«bā depersonalizēt telefonus. Un katru reizi neierakstiet kontaktpersonas vārdu?

Problēma bija interesanta, nebija ilgi jāgaida lÄ«dz risinājumam. Tagad es sniegÅ”u pilnu sarakstu, un tad mēs to izskatÄ«sim secÄ«bā.

from scapy.all import sniff
from scapy.layers.inet import IP
import mysql.connector
import ldap
import getpass
import tftpy
import requests
import os
import time
from string import replace

def conn_ldap(login):
    ad = ldap.initialize('ldap://***.local')
    ad.simple_bind_s('voip@***.local', 'password')
    basedn = 'OU=IT,DC=***,DC=LOCAL'
    basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL'
    scope = ldap.SCOPE_SUBTREE
    filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))"
    filterexp2 = "(&(ObjectClass=organizationUnit))"
    attrlist = ['cn']
    attrlist2 = ['OU']
    search = ad.search_s(basedn, scope, filterexp, attrlist)
    adname = search[0][1]['cn'][0].decode('utf-8')
    if adname == ' ':
        search = ad.search_s(basedn_user, scope, filterexp2, attrlist2)
        for i in range(1, len(search)+1):
            group = search[i][1]['ou'][0]
            basedn_user2 = 'OU='+group+','+basedn_user
            search = ad.search_s(basedn_user2, scope, filterexp, attrlist)
            adname = search[0][1]['cn'][0].decode('utf-8')
            if adname != ' ':
                return adname
        adname = search[0][1]['cn'][0].decode('utf-8')
    ad.unbind_s()
    return adname


def tftp_file_change(config,place,adname,current_account,current_account_password):

    client = tftpy.TftpClient("192.168.0.3", 69)
    client.download('template.cfg', place)
    fileread = open(place, 'r')
    line = fileread.readlines()
    fileread.close()
    line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + 'n')
    line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + 'n')
    filewrite = open(place, 'w')
    for i in line:
      filewrite.write(i)
    filewrite.close()
    print place
    print config
    client.upload(config,place)


def get_phone_inform(ipaddr):
    fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]')
    conf = fileconf.text.split('|')
    current_account = conf[2]
    return current_account


def sniff_frame():
    pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060")
    if len(pcapf) == 0:
        exit()
    frame = pcapf[0]
    macaddr = frame.src
    print macaddr[:8]
    if macaddr[:8] != '80:5e:c0':
        exit()
    ipaddr = frame[0][IP].src
    return macaddr, ipaddr


def conn_mysql(query,fquery,macaddr,qwery2):
    connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***')
    cursor = connect.cursor()
    cursor.execute(fquery)
    state = cursor.fetchall()
    state = bool(state[0][0])
    if state == True:
        cursor.execute(qwery2)
        connect.commit()
        connect.close()
    else:
        cursor.execute(query)
        connect.commit()
        connect.close()


def check_account(current_account):
    connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***')
    cursor = connect.cursor()
    qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";'
    cursor.execute(qwery)
    password = cursor.fetchall()
    if password == ' ':
        exit()
    else:
        return password


if __name__ == '__main__':
    macaddr, ipaddr = sniff_frame()
    current_account = get_phone_inform(ipaddr)
    current_account_password = check_account(current_account)
    macaddr = macaddr.replace(':', '')
    ipaddr = ipaddr.decode('utf-8')
    adname = conn_ldap(getpass.getuser())
    query = 'INSERT INTO station (mac, ip, name, number) VALUES (' + '"' + macaddr + '",' + '"' + ipaddr + '",' + '"' + adname + '",' + '"' + get_phone_inform(ipaddr) + '"' + ')'
    qwery2 = 'UPDATE station SET ip=' + '"' + ipaddr + '"' + ', name=' + '"' + adname + '"' + ', number=' + '"' + get_phone_inform(ipaddr) + '"' + ' WHERE mac=' + '"' + macaddr + '"'
    fquery = 'SELECT EXISTS(SELECT mac FROM voip.station WHERE mac=' + '"' + macaddr + '")'
    query = query.encode('utf-8')
    fquery = fquery.encode('utf-8')
    config = macaddr + '.cfg'
    place = os.path.expanduser("~") + "" + "AppDataLocal" + config
    conn_mysql(query,fquery,macaddr,qwery2)
    tftp_file_change(config,place,adname,current_account,current_account_password)
    requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP')
    requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot')

Programma darbojas lietotāja datorā un darbojas ar nosacījumu, ka dators ir savienots ar tīklu, izmantojot tālruni, jo Yealink T19 nevar darboties kā vārteja.

Pirmkārt, mums ir jāsaprot, vai tas ir saistīts? un kāds mac un ip ir mūsu telefonam.

def sniff_frame():
    pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060")
    if len(pcapf) == 0:
        exit()
    frame = pcapf[0]
    macaddr = frame.src
    print macaddr[:8]
    if macaddr[:8] != '80:5e:c0':
        exit()
    ipaddr = frame[0][IP].src
    return macaddr, ipaddr

Å eit mēs izmantojam funkciju sniff no scapy framework, ar tās palÄ«dzÄ«bu saņemam iepriekÅ” noteiktu udp paketi, nogaidām 70 sekundes un, ja neko neÄ·eram, izejam.

count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060"

Tālāk mēs pārliecināmies, ka ierÄ«ce patieŔām ir Yealink, un atgriežam nepiecieÅ”amās vērtÄ«bas (ip un mac).

Izmantojot Ä«paÅ”u pieprasÄ«jumu, noskaidrojam norēķinu kontu tālrunÄ«. Lai to izdarÄ«tu, paÅ”reizējā konfigurācija tiek lejupielādēta no tālruņa un parsēta.

def get_phone_inform(ipaddr):
    fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]')
    conf = fileconf.text.split('|')
    current_account = conf[2]
    return current_account

Uzziniet Ŕī konta paroli. Lai to izdarÄ«tu, mēs pievērÅ”amies tabulai asterisk.sip un tajā esoÅ”ajam datu laukam.

def check_account(current_account):
    connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***')
    cursor = connect.cursor()
    qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";'
    cursor.execute(qwery)
    password = cursor.fetchall()
    if password == ' ':
        exit()
    else:
        return password

Nu, pēdējā posmā mēs izveidojam savienojumu ar ldap AD un izmantojot funkciju sAMAccountName getpass.getuser() ņemiet paÅ”reizējā lietotāja cn (kurā parasti ir pilns lietotāja vārds).

def conn_ldap(login):
    ad = ldap.initialize('ldap://***.local')
    ad.simple_bind_s('voip@***.local', 'password')
    basedn = 'OU=***,DC=***,DC=LOCAL'
    basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL'
    scope = ldap.SCOPE_SUBTREE
    filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))"
    filterexp2 = "(&(ObjectClass=organizationUnit))"
    attrlist = ['cn']
    attrlist2 = ['OU']
    search = ad.search_s(basedn, scope, filterexp, attrlist)
    adname = search[0][1]['cn'][0].decode('utf-8')
    if adname == ' ':
        search = ad.search_s(basedn_user, scope, filterexp2, attrlist2)
        for i in range(1, len(search)+1):
            group = search[i][1]['ou'][0]
            basedn_user2 = 'OU='+group+','+basedn_user
            search = ad.search_s(basedn_user2, scope, filterexp, attrlist)
            adname = search[0][1]['cn'][0].decode('utf-8')
            if adname != ' ':
                return adname
        adname = search[0][1]['cn'][0].decode('utf-8')
    ad.unbind_s()
    return adname

Mēs izveidojam savienojumu ar iepriekÅ” izveidotu tabulu datu bāzē (es to izveidoju tur) un ievadām visu, ko uzzinājām, proti: ip, mac, lietotājvārds.

def conn_mysql(query,fquery,macaddr,qwery2):
    connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***')
    cursor = connect.cursor()
    cursor.execute(fquery)
    state = cursor.fetchall()
    state = bool(state[0][0])
    if state == True:
        cursor.execute(qwery2)
        connect.commit()
        connect.close()
    else:
        cursor.execute(query)
        connect.commit()
        connect.close()

Mēs varētu apstāties Å”eit, jo mēs jau esam izveidojuÅ”i dinamisku adreÅ”u grāmatu, jÅ«s varat jautāt, bet es gāju tālāk un pievienoju Å”eit ierīču automātisko nodroÅ”ināŔanu.

Lai to izdarÄ«tu, no iepriekÅ” konfigurēta tftp servera tiek lejupielādēta veidnes konfigurācija, kurā mēs veicam izmaiņas un saglabājam to kā mac.cfg. Tas nozÄ«mē, ka Yealink ir divu veidu konfigurācijas, viens ir globāls, bet otrs attiecas uz konkrētu tālruni, un tam jābÅ«t Ŕādā formātā: mac_phone.cfg.

Pēc visām izmaiņām failā un tā saglabāŔanas atpakaļ tftp serverÄ« mēs dodam komandu tālrunim nodroÅ”ināt un pārstartēt ierÄ«ci.

def tftp_file_change(config,place,adname,current_account,current_account_password):

    client = tftpy.TftpClient("192.168.0.3", 69)
    client.download('template.cfg', place)
    fileread = open(place, 'r')
    line = fileread.readlines()
    fileread.close()
    line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + 'n')
    line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + 'n')
    filewrite = open(place, 'w')
    for i in line:
      filewrite.write(i)
    filewrite.close()
    print place
    print config
    client.upload(config,place)

requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP')
requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot')

Pēc ierÄ«ces pārstartÄ“Å”anas telefona ekrānā tiek parādÄ«ts pilns vārds + vienmēr pareizi aizpildÄ«ta adreÅ”u grāmata datu bāzes veidā, tad atliek tikai pievienot XML un nedaudz PHP, lai dinamiski parādÄ«tu saturu. Tādu piemēru ir daudz, pat YEALINK paÅ”am tādi ir.

P.S. Lai nodroÅ”inātu lielāku mērogojamÄ«bu, galvenos iestatÄ«jumus (mainÄ«gos) varat ievietot atseviŔķā failā.

Avots: www.habr.com

Pievieno komentāru