Elasticsearch och Kibana via C# utan Logstash
På Simovits Consulting använder vi, för arbete med dokument, en server som saknar kopplingar till andra nätverk (air gapped system). Detta är en helt självklar säkerhetsåtgärd för ett IT-säkerhetsföretag. Att ha ett galvaniskt åtskilt nätverk är dock inte den enda säkerhetsåtgärden som krävs för att ha kontroll över viktiga resurser. Eftersom Simovits Consulting utför penetrationstester och säkerhetsgranskningar kan man aldrig vara tillräckligt noggrann vad gäller hantering av dokument.
Vanligtvis så exporteras få filer ut från den åtskilda servermiljön men ifall en rapport färdigställts och behöver levereras till en kund i digitalt format så behöver filer kunna exporteras ut. Om det är känsliga rapporter så är det viktigt att de krypteras innan de exporteras ut från den fristående servern. Det finns därför behov av stark kontroll av detta förfarande. En kontrollmekanism är att logga vilka filer som exporteras ut från servern, vem som har exporterat filerna och i vilket format. Det finns ett antal produkter på marknaden för data leakage prevention och liknande fokus som kan hantera detta men vi vill själva ha en bra kontroll över vilka programvaror som installeras på servern och samtidigt undvika höga licenskostnader.
Kommersiella produkter i detta område tenderar att vara ytterst kostsamma samtidigt som de inte möter våra behov exakt. Därför har vi byggt en liten Windows-tjänst i C# som övervakar skrivningar till USB-diskar som ansluts till servern och loggar detta i en Elasticsearch-databas. Självklart kan vi inte beskriva exakt hur vårt system ser ut (i egenskap av IT-säkerhetsföretag) men det är trevligt att kunna dela med sig av erfarenheter till er som lyckats hitta vår webbsida och är intresserad av företaget eller IT-säkerhet i allmänhet.
För att lagra information om vem som har skrivit vilka filer till vilket USB-minne så använder vi en Elasticsearch-databas och Kibana för att kunna få bra visuella översikter (Dashboards). Därmed får vi kontroll över vilka filer som flyttas ut från servern, när, av vem och på vilket sätt.
Elasticsearch-databaser är fantastiskt bra på att lagra stora mängder data. Det är dock förstås väldigt enkelt att använda Elasticsearch för att lagra data oavsett mängd. En fördel med Elasticsearch, anser jag personligen, är att man kan använda Kibana som gränssnitt för att enkelt skapa dashboards samt att det är väldigt enkelt att installera och skala lösningen i efterhand.
Med Logstash får man ett enkelt verktyg för att kunna importera/samla in data till Elasticsearch. I vissa fall kan det dock ses som overkill att ha en separat logginsamlingstjänst och man kan därför vilja skicka in loggar direkt till Elasticsearch utan Logstash. I vårt system har vi valt att inte använda Logstash eftersom det enda tjänsten skulle göra är att ta emot en loggrad på ett särskilt format och skicka den vidare till Elasticsearch. Om vi bara har en loggkälla känns det inte meningsfullt att använda Logstash och därför bestämde vi oss för att låta C#-programmet skicka in sina loggar till Elasticsearch genom en enkel HTTPS-anslutning utan hjälp av Logstash. Eftersom det inte är speciellt många loggrader som skickas till Elasticsearch-databasen så behöver vi inte använda Elasticsearchs bulk-API (https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bul…). Ifall systemet behöver skalas upp kan detta enkelt läggas till.
Loggar till Elasticsearch skickas via JSON-objekt som serialiseras med hjälp av tredjeparts-klassen Newtonsoft.Json.Serialization (se http://www.newtonsoft.com/json/help/html/SerializingJSON.htm). I vårt fall finns två typer av loggar och vi har valt att låta en basklass innehålla kod för serialisering såväl som minimalistisk anslutning mot Elasticsearch. De olika typerna av loggar är klasser som ärver från basklassen. Programmet är väldigt enkelt och har ett fåtal kodrader. Nedan visas basklassen för loggposter samt exempel på en loggpost-klass som används för kommunikation med Elasticsearch-databasen (varning för utebliven felhantering).
/// Copyright Simovits Consulting 2015 /// License: no rocket science, thus use it as you wish. using System; using System.Collections.Generic; using System.Linq; using System.Text; using Newtonsoft.Json.Serialization; using System.Net; using System.IO; namespace fileaxlog { /// Used for serializing logpost to json. class LogPost { // The getter-setter may be added later for making nice code. Now, we care about lines of code more. string strLogPath2 = null; // Log path for stream protected string strType = "LogPost"; // Type for indexing static ulong id = 0; // For unique IDs in index const string STR_POST = "POST"; // HTTP-method /// In order to convert to a format that is understood by elasticsearch. class ElasticTimeConverter : Newtonsoft.Json.Converters.IsoDateTimeConverter { public ElasticTimeConverter() { base.DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal; base.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss"; } } /// Constructor ///The path where to log public LogPost(string strLogPath2 ) { this.strLogPath2 = strLogPath2; id++; } /// Serializing object to JSON /// Formatted JSON public override string ToString() { return Newtonsoft.Json.JsonConvert.SerializeObject(this, new ElasticTimeConverter()); } /// Write the log to a stream. ///The stream used for writing public void writeLog(StreamWriter swrite) { if (swrite == null) { swrite = new StreamWriter(strLogPath2, true); } swrite.WriteLine(this.ToString()); swrite.Flush(); } /// Sends logs to Elasticsearch-database. /// /// TODO: Error handling ///IP or hostname ///The index where to store the log public void sendLog(string strHost, string strIndex) { string postData = this.ToString(); string strURL = string.Format("{0}/{1}/{2}", strHost, strIndex, this.strType); WebRequest request = WebRequest.Create(strURL); request.Method = STR_POST; byte[] byteArray = Encoding.UTF8.GetBytes(postData); request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = byteArray.Length; Stream dataStream = request.GetRequestStream(); dataStream.Write(byteArray, 0, byteArray.Length); dataStream.Close(); WebResponse response = request.GetResponse(); dataStream = response.GetResponseStream(); StreamReader reader = new StreamReader(dataStream); string responseFromServer = reader.ReadToEnd(); reader.Close(); dataStream.Close(); response.Close(); } } /// Standard event viewer log post class WatchLog : LogPost { public WatchLog(string str) : base(str) { strType = "FileLog"; } public DateTime dtCreated; public string strObjectName; public string strUserName; public ulong eventID; public string strProcessName; } }
Programmet exekverar som en Windows-service och övervakar filer/mappar samt ifall USB-stickor ansluts mot någon av USB-portarna på servern. Varje gång som något händer så skickas loggposten till Elasticsearch och kan senare hämtas upp genom Kibana eller andra gränssnitt som kan hantera REST och JSON. Genom detta, mycket enkla, program med endast några fåtal timmars utveckling så har vi nu en lösning som ger stora möjligheter till att övervaka fil-flyttar på vårt galvaniskt separerade servernätverk.
Elasticsearch har, i sin minimalism, inte mycket säkerhetsfunktioner. Det finns exempelvis, som standard, ingen autentisering för HTTP-requests vilket gör att vilken användare som helst kan ta bort loggposter eller skriva nya loggposter. För att åtgärda detta krävs därför att man använder sig av en proxy eller gör en egen modul till Elasticsearch. Hur detta går till lämnar vi dock för tillfället … för att möjligen återvända till ämnet i en senare bloggpost.