Vedlegg B Brukermanual
Innholdsfortegnelse 1. INTRODUKSJON 3 2. UTVIKLING 3 2.1 PLUGINBIBLIOTEKET 3 2.2 SENSOR 4 2.3 DATABASE 5 2.4 CORE 6 2.5 WEBSERVICE 6 2.6 FRONTEND 6 2.6.1 INNHENTING AV DATA FRA WEBSERVICE 6 2.6.2 GRAFER 7 3. BRUK 7 3.1 PLUGINBIBLIOTEK 7 3.1.1 LOAD_SESSION 7 3.1.2 GET_STATUSCODES 8 3.1.3 IP_SEARCH 8 3.1.4 SOURCEPORT 8 3.1.5 DESTPORT 8 3.1.6 STATUSLINE_COUNT 8 3.1.7 STATUSLINE_ALL 8 3.1.8 STATUSLINE_DATE 9 3.1.9 HOSTNAME_COUNT 9 3.1.10 USERAGENT_COUNT 9 3.2 SENSOR 9 3.3 DATABASE 10 3.4 CORE 10 3.5 WEBSERVICE 11 Filnavn: Brukerdokumentasjon.docx Side: 2 av 11
1. INTRODUKSJON Dette dokumentet er ment som en brukermanual for de som måtte ønske å ta i bruk, eller utvikle ny funksjonalitet for de ulike delene i PySniff. 2. UTVIKLING I dette kapittelet beskrives det som er nødvendig for å utvikle, og legge til rette for ny funksjonalitet i de ulike delene av PySniff. 2.1 Pluginbiblioteket Slik ser «skallet» for en tom plugin ut: import datetime, os import logging, logging.config import cherrypy from cherrypy import tools logger = logging.getlogger('dev') def get_class(): return EksempelPlugin() class EksempelPlugin(): logger.info('eksempel module loaded') Figur 1: Et skall for en tom plugin. Denne filen skal lagres som eksempel.py i mappen lib/plugins. Det første som må gjøres er å skrive en metode for tilkobling til databaselagene. En slik metode returnerer en databasetilkobling som kan brukes av alle funksjonene som skal kommunisere mot databasen. En slik funksjon kan se sånn ut: def load_session(self): ''' Creates and returnes a database session ''' try: except: metadata = Base.metadata Session = sessionmaker(bind=engine) session = Session() logger.info('db session loaded') return session logger.error('something went terribly wrong' \ 'when creating the db session.') Denne funksjonen bruker noen variabler, disse er gjengitt her, og defineres som globale, ovenfor «def_class()»: Filnavn: Brukerdokumentasjon.docx Side: 3 av 11
con = 'postgresql+psycopg2://brukernavn:passord@ip-adresse:port/database' logger = logging.getlogger('dev') engine = create_engine(con, pool_size=50, max_overflow=0) Base = declarative_base(engine) For eksempelets skyld, er det her ikke brukt konfigurasjonsfiler, men dette bør selvfølgelig brukes der det er hensiktsmessig. En funksjon i denne pluginen som skal være tilgjengelig utenfor Webservice, må ha disse to linjene foran metodedefinisjonen: @tools.json_out() @cherrypy.expose Den første linjen spesifiserer at funksjonen skal returnere gyldig JSON, den andre forteller CherryPy at denne pluginen skal være tilgjengelig utenfra. Selve funksjonen er det ingen spesielle krav til, rent bortsett fra at den skal returnere gyldig JSON. Det er likevel ønskelig at den skal følge visse krav til kommentering, samt feilhåndtering og logging. Her følger et eksempel på en slik funksjon i sin helhet. @tools.json_out() @cherrypy.expose def hostname_count(self, limit, start="0", end="0"): """Returns the most visited hosts, limited by limit, and optionally specify start and end time. Will return values for the last hour with no times specified Args: limit: how many hosts that should be returned start (optional): timestamp for start time, format YYYY-mm-ddTHH:MM:SS end (optional): timestamp for end time, format YYYY-mm-ddTHH:MM:SS Returns: the most visited hosts, descending, limited by limit """ start = self.format_start(start) end = self.format_end(end) try: 2.2 Sensor session = self.load_session() query = session.query(http.host, func.count(http.host) \ session.close() result = [{.label('count')).filter(http.date>=start, HTTP.date<=end) \.group_by(http.host).order_by(desc('count')).limit(limit) 'hostname':res.host, 'count':res.count }for res in query] return result except Exception, err: logger.error(err) return [] Sensoren er alt kompatibel til å fange opp nettverkstrafikken i transportlaget i OSI-modellen. Det som må utvikles i Sensoren er en plugin for å hente ut den trafikken du vil ha av TCP-laget. I PySniff/sensor/http_plugin.py er en plugin som henter ut HTTP-pakker fra TCP-laget. Denne pluginen Filnavn: Brukerdokumentasjon.docx Side: 4 av 11
består av en rekke regulære-utrykk og sammensettinger for å returnere en Python pakke med innehold av den trafikken du har lyst å returnere. I /PySniff/sensor/sniffer.py som kjører pluginen for å prosessere TCP-strømmen og ta ut pakker av denne må det evt legge til ny funksjonalitet for den nye pluginen. Dette kan gjøres ved å legge på en ny for løkke som for eksempel er: #This is the code to only take out HTTP packeges from the TCP stream. http=packets.filter(lambda(s): http_plugin.httprequest in s or http_plugin.httpresponse in s) for p in http.filter(lambda(s): http_plugin.httprequest in s): postgres_handler.insert_into(hostname,p.src,p.dst,p.len,p.sport,p.dport,p.method,p.host,p.useragent) Postgres_handler må også inneholde innsettings statementet til databasen. Denne kan se ut som for eksempel def insert_into(ihostname,isrc, idst, ilen, isport, idport, imethod, ihost, iuseragent): session = load_session() datef = strftime("%y-%m-%d %H:%M:%S", time.localtime()) ins1 = HTTP(hostname=ihostname, date=datef, src=isrc, dst=idst, len=ilen, sport=isport,dport=idport,method=imethod,host=ihost,useragent=iuseragent) session.add(ins1) session.commit() session.close() 2.3 Database For å utvikle databasen til å inneholde enda en plugin for eksempel HTTP må man lage et nytt script for å lage tabeller i databaselag 1 og databaselag 2. Scriptet for http ser ut som for eksempel dette: db=create_engine('postgresql+psycopg2://username:password@127.0.0.1:5432/la yer1') db.echo = False #Meta data does that everything before metada.create_all() is including in the command metadata = MetaData(db) session = create_session() #metadata.drop_tables() #This is the table of layer1 HTTP. table_http = Table('HTTP', metadata, Column('id', Integer, primary_key=true), Column('hostname', String), Column('date', DateTime), Column('src', String), Column('dst', String), Column('len', String), Column('sport', String), Column('dport', String), Column('method', String), Column('host', String), Column('useragent', String), Column('statusline', String), Column('location', String), Column('server', String), Column('load', String), ) metadata.create_all() Filnavn: Brukerdokumentasjon.docx Side: 5 av 11
2.4 Core For Core sin del er det ikke snakk om videre utvikling av dens hovedfunksjonaliteten. Core er kun ment som et lag rundt kall på aggregeringsfunksjonalitet som er å finne i pluginbiblioteket. 2.5 Webservice Webservice er utviklet på en slik måte at den automatisk laster inn plugins. Det eneste kravet for at denne skal lastes inn er at det ligger en fil i mappen lib/plugins, samt at variabelen valid_plugins inneholder pluginnavnet. valid_plugins ser slik ut: valid_plugins = ['http', 'sql'] For at Webservice skal legge til en ny plugin, for eksempel for FTP, skal en fil med navnet ftp.py, kopieres til mappen lib/plugins, og valid_plugins i webservice.py oppdateres til valid_plugins = ['http', 'sql', 'ftp'] 2.6 Frontend Frontend er utviklet på en slik måte at det er mulig å implementere ny funksjonalitet på mange måter. Django benytter såkalte apps som det er mulig å opprette nye av for å implementere ny funksjonalitet. Disse appene legges under frontend/pysniff/apps/<navn på app> og kan inneholde helt separert kode. Nye apps vil tilføye muligheten for ny adskilt funksjonalitet, og inneholder egne MVC deler, som modeller, tester og views. HTML templatene som er beskrevet i prosjektrapporten legges under frontend/pysniff/template, hvor det kan legges til flere undermapper. Disse må da legges til i konfigurasjonen pysniff.conf. Under den nye appen kan nye funskjoner legges inn i viewet. Eksempel på en slik funksjon er gjengitt under. Dette er en funksjon som henter base templaten og returnerer denne til brukeren. def hello_world(request): """ Prints base.html which prints hello world with the loading of static files """ t = get_template('base.html') c = RequestContext(request, {}) return HttpResponse(t.render(c)) For å kunne benytte denne funksjonen må det enten konfigureres en ny url konfigurasjon for appen, eller lages en regular expression som kan matches til funksjonen. I urls.py vil dette sett slik ut: from django.conf.urls import patterns, include, url from PySniff.apps.<your app name> import <your app name> urlpatterns = patterns('', # PySniff spesific url('^hello_world$', <your app name>.hello_world), Denne url konfigurasjonen ville blitt matchet hvis en bruker hadde spurt etter http://localhost/hello_world. 2.6.1 Innhenting av data fra webservice Henting av data fra webservice er en annen viktig funksjon. Dette gjøres fra viewet. For at det skal være mulig å benytte funksjonaliteten i webservice rammeverket, som gjør det mulig å koble mot webservicen, er dette nødvendig å bli importert. Filnavn: Brukerdokumentasjon.docx Side: 6 av 11
Dette gjøres ved å skrive følgende linje i viewet. from PySniff.libs.ws import ws def api_getdata(request): """ Function for getting data from webservice. This is usefull since you cant use cross-site javascript requests """ stream = ws.webservice() data = stream.api_getdata() return HttpResponse(data, mimetype='application/json') Funksjonen over instansierer webservictilkoblingen og henter data via streamen som opprettes. Funskjonen som benyttes er funksjonen som heter api_getdata(); 2.6.2 Grafer Grafer genereres ved bruk av javascript. Dette skrives i templaten, og kan gjøres ved å opprette nye templates eller ved å gjenbruke de andre som er benyttet. Nedenfor er et eksempel som oppretter og genererer grafer. Det eneste som trengs i tillegg til dette er å konfigurere datoformatet, og dataene som skal hentes. var chart; var data = []; var model = nv.models.multibarchart(); var modelname = "multibarchart"; getjson(); function setupgraph(d) { var format = d3.time.format("%y-%m-%d %H:%M"); nv.addgraph(function() { } chart = model.x(function(d) { return format.parse(d[0]) }).y(function(d) { return d[1] }).color(d3.scale.category10().range()).clipedge(false).showcontrols(true); chart.xaxis.tickformat(function(d) { return d3.time.format('%h:%m')( new Date(d)); }); if(modelname == "multibarchart"){ chart.multibar.stacked(true); } d3.select('#chart1 svg').datum(d).transition().duration(500).call(chart); nv.utils.windowresize(chart.update); return chart; }); 3. BRUK I dette kapittelet beskrives hvordan de ulike delene av PySniff kan tas i bruk. 3.1 Pluginbibliotek 3.1.1 load_session Filnavn: Brukerdokumentasjon.docx Side: 7 av 11
def load_session(self): Denne funksjonen har hverken spesifisert json_out eller cherrypy.expose, hvilket betyr at funksjonen ikke skal kunne kalles eksternt. Den skal bare være tilgjengelig for bruk innad i http.py, og dens oppgave er å opprette og returnere en database-tilkobling for bruk i funksjonen som jobber mot databasen. «self» er en referanse til et objekt, og må være med i definisjonen, men ikke når metoden kalles. Databasetilkobling 3.1.2 get_statuscodes def get_statuscodes(self): Funksjonen returnerer alle de forskjellige HTTP-feilkodene som finnes i databasen. JSON-streng med HTTP-feilkoder. 3.1.3 ip_search def ip_search(self,loc,ip): Funksjonen returnerer alle linjer i databasen som inneholder en spesifikk IP. loc: Kan være dst teller src, forteller om det er destinasjons-ip eller kilde-ip ip: Hvilken IP som skal søkes etter JSON-streng med alle feltene i databasen. 3.1.4 sourceport def sourceport(self, port): Funksjonen returnerer alle linjer i databasen med en spesifikk kildeport. port: Hvilken port det skal søkes etter JSON-streng med alle feltene i databasen. 3.1.5 destport def destport(self, port): Funksjonen returnerer alle linjer i databasen med en spesifikk destinasjonsport. port: Hvilken port det skal søkes etter JSON-streng med alle feltene i databasen. 3.1.6 statusline_count def statusline_count(self): Funksjonen returnerer hvor mange linjer det er i databasen av hver enkelt statusline JSON-streng med statusline og dets antall. 3.1.7 statusline_all def statusline_all(self, status): Funksjonen returner alle datoer/tidspunkter til en gitt statuskode Filnavn: Brukerdokumentasjon.docx Side: 8 av 11
status: Hvilken statuskode som skal søkes etter, f.eks. 200 JSON-streng med dato/tidspunkt og statusline 3.1.8 statusline_date def statusline_date(self, status, start="0", end="0", grouping="minute"): Funksjonen returnerer antall statuskoder gruppert per minutt, mellom start og end, og gruppert på grouping. Start, end og grouping kan sløyfes om ønskelig. Start settes til tidspunktet for en time siden, og end settes til nå, om disse ikke spesifiseres. status: Hvilken statuskode som skal søkes etter, f.eks. 200 start: tidspunkt den skal søke fra end: tidspunkt den skal søke til grouping: hva den skal gruppere på gyldige verdier: second, minute, hour, day, month JSON-streng med dato/tidspunkt og antall. 3.1.9 hostname_count def hostname_count(self, limit, start="0", end="0"): Funksjonen returnerer mest besøkte hosts, limitert av parameteren limit, og mellom start og end. Start og end kan sløyfes om ønskelig. Start settes til tidspunktet for en time siden, og end settes til nå, om disse ikke spesifiseres. limit: antall linjer som skal returneres start: tidspunkt den skal søke fra end: tidspunkt den skal søke til JSON-streng med hostname og antall 3.1.10 useragent_count def useragent_count(self, limit, start="0", end="0"): Funksjonen returnerer mest besøkte user_agents, limitert av parameteren limit, og mellom tidspunktene start og end. Start og end kan sløyfes om ønskelig. Start settes til tidspunktet for en time siden, og end settes til nå, om disse ikke spesifiseres. limit: antall linjer som skal returneres start: tidspunkt den skal søke fra end: tidspunkt den skal søke til JSON-streng med user-agent og antall 3.2 Sensor sudo /opt/pysniff-sensor/py_env/bin/python /opt/pysniffsensor/sensor/pysniff_sensord.py start sudo /opt/pysniff-sensor/py_env/bin/python /opt/pysniffsensor/sensor/pysniff_sensord.py stop Filnavn: Brukerdokumentasjon.docx Side: 9 av 11
sudo /opt/pysniff-sensor/py_env/bin/python /opt/pysniffsensor/sensor/pysniff_sensord.py restart Er følgende kommandoer som blir brukt til å styre sensoren. Disse må kjøre med sudo for å få tilgang til nettverkskortet på maskinen. 3.3 Database Tilganger til databasen blir gitt i følgende form i pg_hba.conf under pgdata folderen til PostgresSQL. Disse blir gitt på følgende form: Host all all ip/cidr trust For eksempel: Host all all 127.0.0.1/32 trust Her må alle nett som skal få lov å kommunisere med databasen legges til. Dette er en sikkerhetsfunksjon i PostgreSQL. Databasen blir stoppet med service funksjoner i /etc/init.d/postgresql start/stop/restart For å legge til databasebruker og passord til denne må dette gjøres i en tabell som heter template1. Kommandoene for å opprette en ny bruker med tabell er da: Sudo su postgres Psql template1 Create user sniffer with password password ; Create database layer1; Grant all privileges on database layer1 to sniffer; ^D Exit 3.4 Core Cores funksjonalitet ligger i å bruke aggregeringsfunksjonalitet i pluginbiblioteket. Hvilke funksjoner som brukes i pluginbiblioteket defineres i aggregeringsprosessen, og består av kall på funksjoner i pluginbiblioteket. def process(): cfg.logger.info("aggregate process starting...") # Kall på aggregeringsfunksjon for HTTP i pluginbibliotek http.aggregate_statuscodes() cfg.logger.info("aggregate process complete.") I tillegg til å legge til kall på aggregeringsfunksjoner er det lagt muligheter for konfigurasjon av Cores daemon og bruk av aggregeringsfunksjonene. Disse er definert i en egen konfigurasjonsfil, og på konfigurasjonsformatet INI. Hvilken konfigurasjonsfil som er i bruk er definert i en egen Python-modul for konfigurasjon i Core, config.py : config = ConfigParser.ConfigParser() config.read(os.path.join(os.path.dirname( file ), 'dev.ini')) Her vises tidspunktet for når sletting av gammel data, hva som er å betegnes som gammel data, samt tidsintervall for når aggregering skal utføres. Konfigurasjonen som følger er et utdrag av et eksempel på en konfigurasjonsfil brukt i Core. [Aggregate] cleaning_time = 03 ; 24 hour format frequency = 3600 ; seconds too_old = 24 ; hours Filnavn: Brukerdokumentasjon.docx Side: 10 av 11
Databasetilkoblingene brukt for slettingen av gammel data og annet arbeid på databasen er også å finne i denne konfigurasjonsfilen. [Database] layer1_conn = postgresql+psycogp2://user:password@host:port/layer1 layer2_conn = postgresql+psycogp2://user:password@host:port/layer2 Basis for bruk av aggregeringsfunksjonaliteten i pluginbiblioteket er Cores daemon som håndterer tidspunkter og tidsintervaller for når funksjonalitet skal utføres. Denne har også en konfigurasjonsbit, hvor pidfile med prosess ID lagres og inn- og utdata fra programmet. [Daemon] stdin_path stdout_path stderr_path pidfile_path pidfile_timeout = 5 = /dev/null = /dev/tty = /dev/tty = /var/run/pysniff/core.pid Konfigurasjonen definert her benyttes deretter i Core daemonen. Hvordan konfigurasjonen lastes inn er ikke ment å endres på. Core har også loggfunksjonalitet som er definert i en egen konfigurasjonsfil for logging. Relevant for bruk av Core er formatet på logglinjene og filstien til loggfilen. [handler_corehandler] ;.. linjer fjernet for eksempelets skyld args=('core.log', 'a') [formatter_coreformatter] format=%(asctime)s %(name)s %(levelname)s %(message)s 3.5 Webservice Bruksområdene til webservice er limitert til kall på URLer fra for eksempel Frontend. Et slikt kall vil typisk se slik ut: 127.0.0.1:8080/plugin/http/hostname_count/5/0/0 En fullstendig oversikt over funksjoner i HTTP-pluginen finnes i kapittel 3.1 i dette dokumentet. Filnavn: Brukerdokumentasjon.docx Side: 11 av 11