Fakultas Ilmu Komputer UI

Commit 3f473fc4 authored by YOGI LESMANA  SULESTIO's avatar YOGI LESMANA SULESTIO
Browse files
parents be129938 f4eefecf
Pipeline #16362 passed with stages
in 2 minutes and 2 seconds
......@@ -2,7 +2,7 @@
layout: post
title: Code Splitting
date: 2019-04-15 02:38:27 +0700
author: Fata Nugraha
author: fata
---
Setelah menyadari aplikasi frontend kami yang dibuat menggunakan React tanpa
......
---
layout: post
title: Automating Log Generation
date: 2019-05-02 04:00:00 +0700
author: fata
---
Karena proyek kami adalah pembuatan manajemen log, kami perlu memiliki suatu mekanisme untuk
men-generate log yang akan digunakan untuk proses development.
<!--more-->
Kami membuat service baru untuk melakukan hal ini. Service ini disamakan dengan environment
deployment yaitu berisi Snort (sebagai IDS) dan Barnyard2 (sebagai spooler).
Secara garis besar cara service ini bekerja adalah buat log, simpan sebagai format `pcap`, dan
jalankan `snort` untuk membaca `pcap` tersebut, lalu `barnyard2` akan mengirim log dan dump packet
yang mentrigger rule dari `snort`.
Langkah pertama, yaitu kami harus membuat packet network yang mentrigger rule dari snort. Untuk
pekerjaan ini kami menggunakan library bernama `scapy`. Salah satu packet yang di-generate menggunakan
scapy untuk mentrigger event "OS-LINUX x86 Linux mountd overflow" sebagai berikut:
```py
class Attack:
source = None
dest = None
def __init__(self, source, dest):
self.source, self.dest = source, dest
def as_packet(self):
ether = Ether(dst=self.dest.mac_address, src=self.source.mac_address)
ip = IP(src=self.source.ip_address, dst=self.dest.ip_address)
return ether / ip
class ExploitAttack(Attack):
# OS-LINUX x86 Linux mountd overflow (exploit.rules)
def as_packet(self):
return (
super().as_packet()
/ UDP(sport=42000, dport=635)
/ Raw(load=b"\xEBV^VVV1\xD2\x88V\x0B\x88V\x1E")
)
```
Langkah kedua cukup simpan log ini sebagai pcap, lalu jalankan snort untuk membaca pcap tersebut.
```py
def read_pcap(pcap):
key = os.urandom(20).hex()
log_dirname = "/log/%s/" % key
os.makedirs(log_dirname)
pcap_file_name = os.path.join(log_dirname, "log.pcap")
pcap.save(pcap_file_name)
snort = subprocess.Popen(
[
"snort",
"-de",
"-r",
pcap_file_name,
"-l",
log_dirname,
"-c",
"/etc/snort/snort.conf",
"-h",
'["172.61.0.0/16"]',
],
stdout=subprocess.PIPE,
)
snort.wait()
# ...snip...
```
Langkah ketiga, jalankan spooler `Barnyard2` untuk mengirim log.
```py
# ...snip...
barnyard2 = subprocess.Popen(
[
"barnyard2",
"-c",
"/etc/barnyard2/barnyard2.conf",
"-o",
os.path.join(log_dirname, "snort.log"),
],
stdout=subprocess.PIPE,
)
barnyard2.wait()
```
Hasil:
![frontend](/assets/images/2019-05-02-automating-log-generation/screenshot.png)
Namun, hal ini tidak cukup untuk mensimulasikan keadaan dunia nyata, dimana setiap saat
ada log yang digenerate. Maka yang kami lakukan adalah mengotomasi pembuatan log diatas
dengan membuat satu thread yang membuat log setiap detik.
Masalah pertama yang kami hadapi untuk mengotomasi pembuatan log adalah cukup lamanya
waktu yang dibutuhkan untuk menjalankan snort dan barnyard, maka untuk mengotomasi
kami tidak menggunakan snort dan barnyard, namun langsung mengirim log ke service
logstash dengan bantuan library `socket`.
```py
class Generator(threading.Thread):
# ...snip...
def generate_random_logs(self):
for i in range(random.randint(*self.event_per_round)):
src = Host(ip_generator())
dest = Host("172.20.1." + str(random.randint(1, 254)))
packet = RandomAttack(src, dest).as_packet()
now = datetime.utcnow().replace(tzinfo=timezone.utc)
alert = alert_generator()
yield BarnyardLog(
now,
alert["priority"],
alert["gid"],
alert["sid"],
1,
alert["class_type"],
"log-generator-1",
alert["msg"],
packet,
)
def send_logs(self, logs):
for log in logs:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.receiver_host, self.receiver_port))
s.send(log.to_representation() + b"\n")
s.close()
def run(self):
while not self.stopped:
logs = self.generate_random_logs()
self.send_logs(logs)
time.sleep(max(0.05, random.random() * self.max_sleep))
# ...snip...
```
Selain itu karena kami harus mengirim log dengan format barnyard, kami harus
membuat log serializer yang menyerupai format barnyard2 yang kami gunakan (format syslog).
```py
class BarnyardLog(object):
def __init__(
self, timestamp, priority, gid, sid, rev, class_type, sensor_name, msg, packet
):
self.timestamp = timestamp.astimezone(tz=timezone.utc)
self.priority = priority
self.gid = gid
self.sid = sid
self.rev = rev
self.class_type = class_type
self.msg = msg
self.packet = packet
self.sensor_name = sensor_name
def get_header(self):
timestamp = re.sub(r"\.\d*\+00:00", "Z", self.timestamp.isoformat())
return f"<68> {timestamp} {self.sensor_name}"
def get_preamble(self):
return f"[SNORTIDS[LOG]: [{self.sensor_name}] ]"
def get_snort_header(self):
timestamp = self.timestamp.isoformat().replace("T", " ")
timestamp = re.sub(r"\d{3}\+00:00", "+000", timestamp)
return (
f"{timestamp} {self.priority} [{self.gid}:{self.sid}:{self.rev}] {self.msg}"
)
def get_class_type(self):
return self.class_type
def get_packet_dump(self):
packet = self.packet.__class__(bytes(self.packet))
return dump_packet(packet)
def to_representation(self):
fields = [
self.get_preamble(),
self.get_snort_header(),
self.get_class_type(),
*self.get_packet_dump(),
]
result = " || ".join(fields)
return f"{self.get_header()} | {result} |".encode()
```
Dengan otomasi ini, selain untuk men-seed data yang cukup berkualitas, dapat kami gunakan juga
untuk melakukan stress testing pada keseluruhan sistem dengan mengontrol berapa banyak log yang dikirim
tiap detik.
---
layout: post
title: Design Patterns
date: 2019-05-02 04:00:00 +0700
author: fata
---
Pemilihan design pattern yang tepat membuat solusi dari permasalahan semakin elegan dan mudah.
Dalam post ini akan dibahas penggunaan pattern Observer dan Chain of Responsibility pada fitur
Realtime World Map dalam proyek kami.
![visualisation](/assets/images/2019-05-02-design-patterns/visualisation.png)
<!--more-->
## Observer
Pattern ini digunakan dalam _system design_ kami untuk mempublikasikan apabila ada log yang
diterima oleh sistem ke semua client yang sedang membuka fitur realtime world map.
![observer pattern](https://sourcemaking.com/files/v2/content/patterns/State1.png)
Subject dalam pattern ini adalah service `aggregator` yang bertanggungjawab untuk menerima
log dan mendistribusikan log tersebut ke Observer yaitu client `frontend` yang sedang membuka
fitur ini.
Implementasi pattern ini dalam fitur ini bisa dibayangkan seperti bagaimana aplikasi _chatting_
bekerja, namun dalam kasus ini hanya ada satu pengguna yang hanya dapat memposting ke grup yang
bernama `world-map` (yaitu si subject) dan dimana anggota grup tersebut adalah user yang membuka
fitur ini (yaitu si observer).
Dalam mengimplementasikan design pattern ini kami menggunakan libary `Django Channels`
yang menggunakan Redis sebagai backend yang memiliki fungsi PubSub (Publish Subscribe).
Setiap client akan melakukan subscribe ke channel fitur, dan saat ada log baru masuk,
akan dilakukan publish ke channel tersebut sehingga setiap client akan menerima
log tersebut.
Observer cukup menunggu adanya perubahan yang diterima oleh subject dan menampilkan perubahan
sesuai log yang dikirimkan oleh subject. Untuk menampilkan visualisasi yang berbeda beda kami
menggunakan pattern selanjutnya yaitu Chain of Responsibility
## Chain of Responsibility
Dalam fitur ini kita baru memiliki 5 jenis visualisasi. Yaitu satu peta dan 4 widget yang menampilkan
ranking dari metrik Unique Country Source, Unique IP Source, Unique IP Destination, dan Class Type.
![chain of responsibility](https://sourcemaking.com/files/v2/content/patterns/Chain_of_responsibility__.png)
Karena kita tidak bisa menggeneralisasi apa yang dilakukan untuk setiap visualisasi, kita dapat
menggunakan pattern ini untuk menyelesaikan permasalahan ini.
Pertama definisikan fungsi handler yang akan menerima jika ada perubahan dalam state (dalam konteks ini
adalah log).
```js
constructor() {
// ...snipped..
this.widgetRefs = [
this.countryWidgetRef,
this.srcIPWidgetRef,
this.dstIPWidgetRef,
this.classTypeWidgetRef,
this.mapWidgetRef
];
// ...snipped...
}
```
Kedua panggil setiap handler ketika ada perubahan di state.
```js
addToState = event => {
this.events.push(event);
this.widgetRefs.forEach(
widget => !this.unmounting && widget.current.addEvent(event, true)
);
};
removeFromState = event => {
this.widgetRefs.forEach(
widget => !this.unmounting && widget.current.removeEvent(event, false)
);
};
```
Dengan pattern ini, parent component tidak perlu mengetahui business logic dari komponen visualisasi dan
kode akan lebih elegan dan mudah di maintain (jika ada penambahan atau pengurangan widget).
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment