Nama : Satria Winekas Herlambang
NIM : 201012410046
Dokumentasi ini merupakan tugas mata kuliah Advanced Network Security. Model machine learning ini mendeteksi serangan DDoS pada dataset CICIDS 2017
<aside> ⛓️ Link Github : https://github.com/ChernobylWaste/P2PDocker Link Dataset : https://drive.google.com/file/d/1yIByIKTYc1iCXxm9Naxeh2PaBrZH3CaS/view?usp=sharing
</aside>
File yang diperlukan untuk membuat jaringan P2P menggunakan docker yaitu :
Berikut penjelasan detail terkait codingan pada file konfigurasi.
# Menentukan versi Docker Compose yang digunakan
version: '3'
# Mendefinisikan layanan-layanan yang akan dijalankan
services:
# Inisialisasi service pertama dengan nama node1
node1:
# Menggunakan Dockerfile di direktori saat ini untuk membangun image
build: .
# Perintah yang akan dijalankan saat container node1 dimulai
command: python ./main.py node1
# Port yang akan diekspos oleh container
expose:
- 65434
# Menghubungkan direktori lokal dengan direktori files di dalam container
volumes:
- ./node1:/files
# Jaringan p2p_network
networks:
- p2p_network
# Inisialisasi service kedua dengan nama node2
node2:
build: .
command: python ./main.py node2
expose:
- 65434
volumes:
- ./node2:/files
networks:
- p2p_network
# Inisialisasi service ketiga dengan nama node3
node3:
build: .
command: python ./main.py node3
expose:
- 65434
volumes:
- ./node3:/files
networks:
- p2p_network
# Inisialisasi service keempat dengan nama node4
node4:
build: .
command: python ./main.py node4
expose:
- 65434
volumes:
- ./node4:/files
networks:
- p2p_network
# Inisialisasi jaringan yang digunakan oleh services di atas
networks:
# Jaringan dengan nama p2p_network
p2p_network:
# Konfigurasi IP Address Management (IPAM) untuk jaringan di container
ipam:
config:
# Subnet yang digunakan oleh jaringan
- subnet: 192.168.0.0/24
# Gateway yang digunakan oleh jaringan
gateway: 192.168.0.1
# Impor modul zmq untuk komunikasi jaringan menggunakan ZeroMQ
import zmq
# Impor modul os untuk berinteraksi dengan sistem operasi
import os
# Impor modul sys untuk mengakses argumen baris perintah
import sys
# Impor modul time untuk menangani operasi waktu
import time
# Impor modul hashlib untuk menghitung hash file
import hashlib
# Impor modul Thread dari threading untuk menjalankan beberapa proses secara bersamaan
from threading import Thread
# Konfigurasi node dan port
NODES = ["node1", "node2", "node3", "node4"] # Daftar node yang terlibat dalam jaringan
PORT = 65434 # Port yang digunakan untuk komunikasi antar node
# Direktori untuk menyimpan file
FILE_DIR = "/files" # Direktori tempat file-file akan disimpan
# Node saat ini
me = str(sys.argv[1]) # Nama node saat ini yang diberikan sebagai argumen baris perintah
# Fungsi untuk menghitung hash file
def calculate_file_hash(filepath):
# Buat objek hasher MD5
hasher = hashlib.md5()
# Buka file dalam mode baca biner
with open(filepath, "rb") as f:
buf = f.read() # Baca isi file
hasher.update(buf) # Update hasher dengan isi file
return hasher.hexdigest() # Kembalikan hash dalam bentuk heksadesimal
# Fungsi untuk mengirim file
def send_file(filename, data, socket):
# Kirim nama file dan data file sebagai multipart message
socket.send_multipart([filename.encode(), data])
# Fungsi untuk menyimpan file
def save_file(filename, data):
# Tentukan path lengkap file
filepath = os.path.join(FILE_DIR, filename)
# Buat direktori jika belum ada
if not os.path.exists(FILE_DIR):
os.makedirs(FILE_DIR)
# Periksa apakah file sudah ada
if os.path.exists(filepath):
# Jika file dengan nama sama ada, periksa hash
existing_hash = calculate_file_hash(filepath)
new_hash = hashlib.md5(data).hexdigest()
if existing_hash == new_hash:
print(f"File {filename} sudah ada, tidak di-overwrite.")
return
# Simpan file baru
with open(filepath, "wb") as f:
f.write(data)
print(f"File {filename} disimpan.")
# Fungsi server untuk mengirim file
def server():
# Buat konteks ZeroMQ
context = zmq.Context()
# Buat socket PUSH untuk mengirim pesan
server_socket = context.socket(zmq.PUSH)
# Bind socket ke port yang ditentukan
server_socket.bind(f"tcp://*:{PORT}")
print(f"Server di {me} berjalan...")
known_files = {} # Dictionary untuk melacak file yang sudah dikirim
while True:
# Daftar file dalam direktori
files_in_dir = os.listdir(FILE_DIR)
for filename in files_in_dir:
# Abaikan file sementara seperti .swp atau file tersembunyi
if filename.endswith('.swp') or filename.startswith('.'):
print(f"Skipping temporary file: {filename}")
continue
# Tentukan path lengkap file
filepath = os.path.join(FILE_DIR, filename)
# Hitung hash file
file_hash = calculate_file_hash(filepath)
# Jika file baru atau file berubah
if filename not in known_files or known_files[filename] != file_hash:
# Buka file dalam mode baca biner
with open(filepath, "rb") as f:
data = f.read() # Baca isi file
# Kirim file ke setiap node lainnya
for node in NODES:
if node != me:
send_file(filename, data, server_socket)
# Update dictionary dengan hash file terbaru
known_files[filename] = file_hash
print(f"File {filename} dikirim ke node lainnya.")
# Tunggu 5 detik sebelum memeriksa kembali
time.sleep(5)
# Fungsi klien untuk menerima file
def client():
# Buat konteks ZeroMQ
context = zmq.Context()
# Buat socket PULL untuk menerima pesan
client_socket = context.socket(zmq.PULL)
# Hubungkan ke setiap node lainnya
for node in NODES:
if node != me:
client_socket.connect(f"tcp://{node}:{PORT}")
print(f"Klien di {me} berjalan dan menunggu file...")
while True:
# Terima pesan multipart (nama file dan data file)
message = client_socket.recv_multipart()
filename, data = message
# Simpan file yang diterima
save_file(filename.decode(), data)
# Jalankan server dan klien secara paralel
if __name__ == "__main__":
# Buat thread untuk server
Thread(target=server).start()
# Buat thread untuk klien
Thread(target=client).start()
# Menggunakan image Python terbaru sebagai basis image
FROM python:latest
# Install-an Opsional
# Memperbarui daftar paket dan menginstal nano
RUN apt update && apt install -y nano
# Memperbarui daftar paket dan menginstal net-tools
RUN apt update && apt install -y net-tools
# Memperbarui daftar paket dan menginstal iputils-ping
RUN apt update && apt install -y iputils-ping
# Menyalin file utama (main.py) ke dalam image Docker
ADD main.py .
# Mendefinisikan volume untuk menyimpan file yang akan disinkronisasi
VOLUME ["/files"]
Pastikan sudah menginstall docker di komputer. Berikut cara menginstall docker desktop di komputer windows.
Setelah docker desktop terinstall dan semua file codingan telah di buat, berikut cara menjalankan codingan tersebut.
docker-compose up --build


docker container ls

docker exec -it <container-name> bash
contoh ingin masuk ke bash node 1:
docker exec -it p2pdocker-node1-1 bash

/filescd files
nanonano node1.txt

Berikut contoh isi file node1.txt
Hallo, ini file dari node 1
ada dua cara melihat file yang sudah dibuat sudah tersinkron atau belum.
Cara Pertama:

Folder tersebut merupakan folder /files dari tiap node.

Gambar tersebut menunjukkan bahwasannya file telah tersinkron di tiap node nya.
Cara kedua:
docker exec -it p2pdocker-node2-1 bash
/filescd files
ls

cat untuk membaca file txtcat node1.txt
Jika P2P sudah berhasil dilakukan, langkah selanjutnya bisa melakukan federated learning menggunakan model yang akan dipakai. Pada dokumentasi ini, model machine learning yang dipakai yaitu XGBoost. Untuk agregasi modelnya menggunakan FedAVG.
File machine learning yang akan digunakan yaitu :
Berikut codingan dan penjelasannya :
# Memanggil library yang akan digunakan.
import warnings
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import xgboost as xgb
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
# Mengabaikan peringatan FutureWarning dan UserWarning untuk membersihkan output
warnings.filterwarnings('ignore', category=FutureWarning)
warnings.filterwarnings('ignore', category=UserWarning)
data_path = './Friday-WorkingHours-Afternoon-DDos.pcap_ISCX.csv'
df = pd.read_csv(data_path)
# Menghapus spasi di awal kalimat
df.columns = df.columns.str.lstrip()
# Menampilkan informasi dataset, nama kolom, dan distribusi label
print("Jumlah Label dalam Dataset:")
print(df['Label'].value_counts())
# Memisahkan fitur (X) dan label (y) dari dataset
X = df.drop('Label', axis=1)
y = df['Label']
# Mengganti nilai tak terhingga dengan NaN dan mengisi NaN dengan median fitur
X.replace([np.inf, -np.inf], np.nan, inplace=True)
X.fillna(X.median(), inplace=True)
# Membuat objek LabelEncoder yang akan digunakan untuk melakukan encoding pada label kategorikal.
label_encoder = LabelEncoder()
# Mengidentifikasi kategori unik dalam label y dan menetapkan nilai numerik untuk setiap kategori.
y_encoded = label_encoder.fit_transform(y)
# Menormalisasi fitur menggunakan StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Membuat objek XGBClassifier dengan beberapa parameter yang ditentukan.
# Parameter use_label_encoder=False untuk menentukan apakah XGBClassifier akan menggunakan LabelEncoder internal untuk mengubah label kategorikal menjadi nilai numerik.
# Parameter eval_metric='logloss' untuk menentukan metrik evaluasi yang digunakan selama pelatihan model.
# Parameter random_state=42 untuk menentukan seed untuk generator random,
model = xgb.XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42)
# Training model XGBoost
try:
model.fit(X_scaled, y_encoded)
# Jika training ada error, maka akan mengoutput kannya. Digunakannya exception karena ada bug dari XGBoost dengan skilearn nya.
except Exception as e:
print(f"Error pada saat memuat model fitting: {e}")
# Menghitung dan menampilkan Importance Score pada fitur teratas
feature_importances = model.feature_importances_
feature_scores = pd.DataFrame({'Fitur': df.columns[:-1], 'Importance': feature_importances})
feature_scores = feature_scores.sort_values(by='Importance', ascending=False)
print("\\nFeature Importance Score:")
print(feature_scores.head(20))
# Menyimpan 20 fitur dengan importance score teratas sebagai rekomendasi fitur
recommended_features = feature_scores['Fitur'].head(20).values
print("\\nRekomendasi Fitur untuk Deteksi/Klasifikasi Serangan DDoS:")
print(recommended_features)
# Memilih fitur teratas
X_selected = df[recommended_features]
# Mengganti nilai tak terhingga dengan NaN
X_selected.replace([np.inf, -np.inf], np.nan, inplace=True)
# Mengisi NaN dengan median
X_selected.fillna(X_selected.median(), inplace=True)
# Menormalisasi fitur
X_selected_scaled = scaler.fit_transform(X_selected)
# Memisahkan data menjadi set pelatihan 80% dan pengujian 20%
X_train, X_test, y_train, y_test = train_test_split(X_selected_scaled, y_encoded, test_size=0.2, random_state=42)
# Mendefinisikan parameter untuk model XGBoost
params = {
# Menentukan tujuan dari model, yaitu klasifikasi multi-kelas.
# 'multi:softprob' menghasilkan probabilitas untuk setiap kelas.
'objective': 'multi:softprob',
# Menentukan jumlah kelas dalam klasifikasi.
# Ini diambil dari jumlah kelas yang dihasilkan oleh label encoder.
'num_class': len(label_encoder.classes_),
# Metode evaluasi untuk mengukur performa model.
# 'mlogloss' (multiclass logarithmic loss) digunakan untuk klasifikasi multi-kelas.
'eval_metric': 'mlogloss',
# Nilai ini mengontrol seberapa cepat model akan beradaptasi dengan data.
'learning_rate': 0.1,
# Kedalaman maksimum untuk setiap tree dalam model. Parameter ini membantu mengontrol overfitting.
'max_depth': 6,
# Proporsi fitur yang akan dipilih secara acak untuk digunakan dalam setiap pohon. 0.8 berarti 80% fitur dipilih.
'colsample_bytree': 0.8,
# Proporsi sampel yang digunakan untuk membuat setiap pohon. 0.8 berarti 80% data digunakan.
'subsample': 0.8,
# Seed atau nilai acak untuk menjaga konsistensi hasil model.
'random_state': 42
}
# Membuat DMatrix untuk XGBoost dan menyiapkan watchlist untuk evaluasi
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
watchlist = [(dtrain, 'train'), (dtest, 'eval')]
evals_result = {}
# Melatih model XGBoost dengan parameter dan watchlist
model_final = xgb.train(
params,
dtrain,
num_boost_round=35, # Mengatur untuk seberapa banyak data ingin di training
evals=watchlist,
evals_result=evals_result,
verbose_eval=1
)
# Menyimpan model yang dilatih ke file JSON
model_file_path = "model.json"
model_final.save_model(model_file_path)
print(f"Model saved to {model_file_path}")
# Melakukan prediksi
y_pred_prob = model_final.predict(dtest)
y_pred_encoded = np.argmax(y_pred_prob, axis=1)
y_pred = label_encoder.inverse_transform(y_pred_encoded)
# Menghitung akurasi model
accuracy = accuracy_score(y_test, y_pred_encoded)
print(f"Test Accuracy: {accuracy * 100:.2f}%")
print("Classification Report:")
print(classification_report(y_test, y_pred_encoded, target_names=label_encoder.classes_, digits=4))
# Membuat untuk evaluasi model
conf_matrix = confusion_matrix(y_test, y_pred_encoded)
reordered_indices = [list(label_encoder.classes_).index(label) for label in ['DDoS', 'BENIGN']]
conf_matrix = conf_matrix[reordered_indices, :][:, reordered_indices]
plt.figure(figsize=(10, 7))
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues',
xticklabels=['DDoS', 'BENIGN'],
yticklabels=['DDoS', 'BENIGN'])
plt.title('Confusion Matrix')
plt.xlabel('Predicted')
plt.ylabel('True')
# Menyimpan confusion matrix menjadi png
conf_matrix_file_path = "files/Confusion_matrix.png"
plt.savefig(conf_matrix_file_path)
print(f"Reordered confusion matrix saved to {conf_matrix_file_path}")
plt.show()
import warnings
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import xgboost as xgb
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
# Mengabaikan peringatan FutureWarning dan UserWarning untuk membersihkan output
warnings.filterwarnings('ignore', category=FutureWarning)
warnings.filterwarnings('ignore', category=UserWarning)
data_path = './Friday-WorkingHours-Afternoon-DDos.pcap_ISCX.csv'
df = pd.read_csv(data_path)
# Menghapus spasi di awal kalimat
df.columns = df.columns.str.lstrip()
# Menampilkan informasi dataset, nama kolom, dan distribusi label
print("Jumlah Label dalam Dataset:")
df['Label'].value_counts()
# Menampilkan informasi dataset, nama kolom, dan distribusi label
print("Jumlah Label dalam Dataset:")
print(df['Label'].value_counts())
# Memisahkan fitur (X) dan label (y) dari dataset
X = df.drop('Label', axis=1)
y = df['Label']
# Mengganti nilai tak terhingga dengan NaN dan mengisi NaN dengan median fitur
X.replace([np.inf, -np.inf], np.nan, inplace=True)
X.fillna(X.median(), inplace=True)
# Membuat objek LabelEncoder yang akan digunakan untuk melakukan encoding pada label kategorikal.
label_encoder = LabelEncoder()
# Mengidentifikasi kategori unik dalam label y dan menetapkan nilai numerik untuk setiap kategori.
y_encoded = label_encoder.fit_transform(y)
# Menormalisasi fitur menggunakan StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Membuat objek XGBClassifier dengan beberapa parameter yang ditentukan.
# Parameter use_label_encoder=False untuk menentukan apakah XGBClassifier akan menggunakan LabelEncoder internal untuk mengubah label kategorikal menjadi nilai numerik.
# Parameter eval_metric='logloss' untuk menentukan metrik evaluasi yang digunakan selama pelatihan model.
# Parameter random_state=42 untuk menentukan seed untuk generator random,
model = xgb.XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42)
# Training model XGBoost
try:
model.fit(X_scaled, y_encoded)
# Jika training ada error, maka akan mengoutput kannya. Digunakannya exception karena ada bug dari XGBoost dengan skilearn nya.
except Exception as e:
print(f"Error pada saat memuat model fitting: {e}")
# Menghitung dan menampilkan Importance Score pada fitur teratas
feature_importances = model.feature_importances_
feature_scores = pd.DataFrame({'Fitur': df.columns[:-1], 'Importance': feature_importances})
feature_scores = feature_scores.sort_values(by='Importance', ascending=False)
print("\\nFeature Importance Score:")
print(feature_scores.head(20))
# Menyimpan 20 fitur dengan importance score teratas sebagai rekomendasi fitur
recommended_features = feature_scores['Fitur'].head(20).values
print("\\nRekomendasi Fitur untuk Deteksi/Klasifikasi Serangan DDoS:")
print(recommended_features)
# Memilih fitur teratas
X_selected = df[recommended_features]
# Mengganti nilai tak terhingga dengan NaN
X_selected.replace([np.inf, -np.inf], np.nan, inplace=True)
# Mengisi NaN dengan median
X_selected.fillna(X_selected.median(), inplace=True)
# Menormalisasi fitur
X_selected_scaled = scaler.fit_transform(X_selected)
# Memisahkan data menjadi set pelatihan 80% dan pengujian 20%
X_train, X_test, y_train, y_test = train_test_split(X_selected_scaled, y_encoded, test_size=0.2, random_state=42)
# Memuat model dari masing-masing node
print("\\nSedang memuat model 1, 2, dan 3")
model1 = xgb.Booster()
model1.load_model('model1.json')
model2 = xgb.Booster()
model2.load_model('model2.json')
model3 = xgb.Booster()
model3.load_model('model3.json')
print("Model berhasil dimuat!\\n")
# Membuat DMatrix untuk prediksi
dtest = xgb.DMatrix(X_test, label=y_test)
# Prediksi dengan setiap model
y_pred1 = model1.predict(dtest)
y_pred2 = model2.predict(dtest)
y_pred3 = model3.predict(dtest)
# Agregasi prediksi menggunakan rata-rata (FedAvg)
y_pred_avg = (y_pred1 + y_pred2 + y_pred3) / 3
# Mengonversi prediksi rata-rata ke kelas
y_pred_class = np.argmax(y_pred_avg, axis=1)
# Evaluasi model teragregasi
accuracy = accuracy_score(y_test, y_pred_class)
print(f"Test Accuracy (FedAvg Aggregation): {accuracy * 100:.2f}%")
# Evaluasi dengan classification report
class_report = classification_report(y_test, y_pred_class, target_names=label_encoder.classes_, digits=4)
print("Classification Report:")
print(class_report)
# Confusion matrix
conf_matrix = confusion_matrix(y_test, y_pred_class)
# Ubah urutan label untuk memindahkan DDoS ke baris dan kolom pertama
reordered_indices = [list(label_encoder.classes_).index('DDoS'), list(label_encoder.classes_).index('BENIGN')]
reordered_conf_matrix = conf_matrix[reordered_indices, :][:, reordered_indices]
# Visualisasi confusion matrix yang telah diatur ulang
plt.figure(figsize=(10, 7))
sns.heatmap(reordered_conf_matrix, annot=True, fmt='d', cmap='Blues',
xticklabels=['DDoS', 'BENIGN'],
yticklabels=['DDoS', 'BENIGN'])
plt.title('Confusion Matrix Untuk Aggregated Model')
plt.xlabel('Predicted')
plt.ylabel('True')
# Simpan confusion matrix ke file
reordered_conf_matrix_file = "Confusion Matrix.png"
plt.savefig(reordered_conf_matrix_file, dpi=300, bbox_inches='tight')
print(f"File Gambar Confusion Matrix Tersimpan dengan Nama: {reordered_conf_matrix_file}")
plt.show()
pandas
numpy
scikit-learn
xgboost
matplotlib
seaborn
pyzmq
Untuk memasukkan semua file Python ke container, cara nya menambahkan command di dockerfile nya sebagai berikut :
# Menggunakan image Python terbaru sebagai basis image
FROM python:latest
# Install-an Opsional
# Memperbarui daftar paket dan menginstal nano
RUN apt update && apt install -y nano
# Memperbarui daftar paket dan menginstal net-tools
RUN apt update && apt install -y net-tools
# Memperbarui daftar paket dan menginstal iputils-ping
RUN apt update && apt install -y iputils-ping
# Menyalin file utama (main.py) ke dalam image Docker
ADD main.py .
# Codingan untuk Federated learning
# Menyalin file requirements.txt ke dalam image Docker
COPY requirements.txt .
# Menginstal dependensi yang terdaftar di requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Menyalin file fedavg.py ke dalam image Docker
COPY fedavg.py .
# Menyalin file tes.py ke dalam image Docker
COPY lokal.py .
# Menyalin file Friday-WorkingHours-Afternoon-DDos.pcap_ISCX.csv ke dalam image Docker
COPY Friday-WorkingHours-Afternoon-DDos.pcap_ISCX.csv .
# Mendefinisikan volume untuk menyimpan file yang akan disinkronisasi
VOLUME ["/files"]
Setelah Dockerfile di update, bisa di deploy dockernya dengan cara yang serupa seperti mendeploy arsitektur jaringan P2P.
Sebelum menjalankan ML di lokal node, harus diperhatikan apakah file ML nya sudah tersedia di tiap node atau tidak. Dengan cara sebagai berikut :
docker exec -it <container-name> bash
lokalmy.py untuk menyimpan nama file yang uniknano lokal.py
# Menyimpan model yang dilatih ke file JSON
model_file_path = "<namamodel>.json"
model_final.save_model(model_file_path)
print(f"Model saved to {model_file_path}")
python lokalml.py
ls
/filesmv <namafile>.json files