# WAF Health monitor每日報表
主要是為了解決日常查詢log習慣,因此開發一個定時寄信報表排程。
每日寄件的報表程式佈建於ubuntu server 上的一個自動化排程perl script。
* VirtualHost: 140.113.220.167
* 相關模組:
```
Time::Local; # system time
DateTime::Format::ISO8601; # define timestamp
MIME::Lite; # sending mail...
```
* Scripts 路徑:
`/home/netsys/WAF_daily_report/waf_daily_report_ver-20201130.pl`
* 排程:
可查詢 `crontab –l` 指令,目前是設定在每日上午 7:00執行一次。
```
netsys@waflog [~] $ crontab -l
# For more information see the manual pages of crontab(5) and cron(8)
# m h dom mon dow command
00 07 * * * /usr/bin/perl /home/netsys/WAF_daily_report/waf_daily_report_ver-20201130.pl >> /home/netsys/WAF_daily_report/waf_praseing_20201130.log
```
#### 程式碼分解:
* ##### 日期擷取 : 擷取系統今天與昨天時間(範本)
1. 今天
```
sub spGetYesterdaysDate1 {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my $yesterday_midday=timelocal(0,0,12,$mday,$mon,$year) - 24*60*60;
($sec, $min, $hour, $mday, $mon, $year) = localtime($yesterday_midday);
my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $YesterdaysDate = sprintf "%02s %2d ", $abbr[$mon], $mday;
return $YesterdaysDate;
}
```
2. 昨天
```
sub spGetYesterdaysDate2 {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my $yesterday_midday=timelocal(0,0,12,$mday,$mon,$year) - 24*60*60;
($sec, $min, $hour, $mday, $mon, $year) = localtime($yesterday_midday);
my $YesterdaysDate2;
my $YesterdaysDate2 = sprintf "%4d%02d%02s", $year+1900, $mon+1, $mday;
return $YesterdaysDate2;
}
```
* #### perl 執行shell指令執行
把log 從設備挖到 server這邊,處理完之後刪掉,以免日久累積大量檔案
```
my $pwd=qx(pwd);
my $scp_cmd="指令用字串呈現";
system($scp_cmd);
```
* ##### 資料過濾
用 `grep` 一步一步過濾,撈出想要的log,主要根據log 字串特徵。
觀察log結構之後,主要特徵包含 `EVENT DEVICE`、`0-PPE-0`、`140.113`.
[另法]在shell搭配 `awk` 、`sed `也可。
```
my $cmd="grep '". $key_word."' ". $pwd."/ns.log.".$date_yesterday ."| grep 'Device \"server' - | grep 'EVENT DEVICE' - | grep '0-PPE-0' - | grep '140.113.' - ";
print "\$cmd=$cmd\n";
my $raw = qx($cmd);
my @RAW=split/\n/,$raw;
print " total are : ".@RAW."\n";
if(@RAW != 0){}else{ die };
```
* ### 資料格式排列
這一部分主要包含用array儲存重新排列拼貼的資料、時間計算、用hash去存資料。
1. log結構其實有些資訊室也規律性,且希望擷取出來重新排列的。用 `substr() `或` split()` 去取出想要的片段,再把他照順去排好用 `Array`存放。
2. 使用 `DateTime::Format::ISO8601`模組,因為時間的進位制不是10進位。沒辦法直接做計算,因此需要把時間轉換成一個值,方便後面去做相減計算。後面呈現需要再把他轉乘時間格式。
4. 為了同時保有資料與要被計算的值之間的連結,用 `%hash` 去以 `$key => $value` 存時間和轉換成的數值
```
my %relat; ### for event down
my @matrix; ## for parse array
my @order; ### key order
foreach $_(@RAW){
s/"//;
#[ event time & date capture]
my $time=substr($_,7,8);
my $state=(split'-',$_)[-1];
my $year=substr($date_yesterday,0,4);
my $month=substr($date_yesterday,-4,2);
my $date=substr($date_yesterday,-2,2);
# [ip & port capture]
my $network=(split'\(',$_)[0];
my $string2=(split'_',$network)[-1];
my ($ip, $port)=(split/:/,$string2);
# [make time format for caulate time value]
my $key=$state ."\t". $ip ."\t". $port ."\t". $year . $month . $date ."\t". $time;
my ($hr, $min, $sec) = (split/:/,$time)[0,1,2];
my $dt=$year."-".$month."-".$date."T".$hr.":".$min.":".$sec."Z";
my $dtt = DateTime::Format::ISO8601->parse_datetime( $dt );
my $value = $dtt->epoch();
$relat{$key}=$value;
push (@matrix,$value."\t".$key);
push(@order,$key);
print "$value\t$key\n";
}
```
* ### 過濾出來的 log 進行 duration 計算
抓down 和 up的時間點,去計算與時間格式轉換和分類。
foreach()內第一層&第二層if()去算事件down和up時間,第三層if()去判對分類server IP
```
foreach my $timstamp_key(@matrix){
if ($timstamp_key =~ /State DOWN/)
{
my($timstamp, $status_a, $ip_a, $port_a, $date_a, $timing_a)=(split'\t',$timstamp_key)[0,1,2,3,4,5];
for (my $i=0; $i<=$#order;$i++)
{
my $timstamp_compare=$order[$i];
my $up_timestamp=$relat{$timstamp_compare};
#print " !$timstamp_compare\n";
my($status_b, $ip_b, $port_b, $date_b, $timing_b)=(split'\t',$timstamp_compare)[0,1,2,3,4];
if($ip_b =~ m/$ip_a/ )
{
# if status is up and ip:port same to compare_key
if( $port_b =~ m/$port_a/ )
{
if($status_b =~ m/State UP/)
{
my $duration_sec = $up_timestamp - $timstamp_key;
my $duration_format = time_compute($duration_sec);
my $domain=$ip_domain{$ip_a};
my $html="";
if ($ip_b =~ m/140.113.41./)
{
if($ip_b =~ m/140.113.41.241/){
print "技發組(TD)\t"."$ip_a:$port_a\t$domain\t$date_a $timing_a\t$date_b $timing_b\t$duration_format\n";
$html="<tr>\n<th>技發組(TD)</th>\n<th>".$ip_a.":".$port_a."</th>\n<th>".$domain."</th>\n<th>"."$date_a $timing_a"."</th>\n<th>"."$date_b $timing_b"."</th>\n<th>".$duration_format."</th>\n</tr>";
push @all_group,$html;
}else{
print "校資組(MIS)\t"."$ip_a:$port_a\t$domain\t$date_a $timing_a\t$date_b $timing_b\t$duration_format\n";
$html="<tr>\n<th>校資組(MIS)</th>\n<th>".$ip_a.":".$port_a."</th>\n<th>".$domain."</th>\n<th>"."$date_a $timing_a"."</th>\n<th>"."$date_b $timing_b"."</th>\n<th>".$duration_format."</th>\n</tr>";
push @all_group,$html;
}
}
elsif($ip_b =~ m/140.113.40./)
{
print "校資組(MIS)\t"."$ip_a:$port_a\t$domain\t$date_a $timing_a\t$date_b $timing_b\t$duration_format\n";
$html="<tr>\n<th>校資組(MIS)</th>\n<th>".$ip_a.":".$port_a."</th>\n<th>".$domain."</th>\n<th>"."$date_a $timing_a"."</th>\n<th>"."$date_b $timing_b"."</th>\n<th>".$duration_format."</th>\n</tr>";
push @all_group,$html;
}else{
if($ip_b =~ m/140.113.9.153/){
print "校資組(MIS)\t"."$ip_a:$port_a\t$domain\t$date_a $timing_a\t$date_b $timing_b\t$duration_format\n";
$html="<tr>\n<th>校資組(MIS)</th>\n<th>".$ip_a.":".$port_a."</th>\n<th>".$domain."</th>\n<th>"."$date_a $timing_a"."</th>\n<th>"."$date_b $timing_b"."</th>\n<th>".$duration_format."</th>\n</tr>";
push @all_group,$html;
}else{
print "技發組(TD)\t"."$ip_a:$port_a\t$domain\t$date_a $timing_a\t$date_b $timing_b\t$duration_format\n";
$html="<tr>\n<th>技發組(TD)</th>\n<th>".$ip_a.":".$port_a."</th>\n<th>".$domain."</th>\n<th>"."$date_a $timing_a"."</th>\n<th>"."$date_b $timing_b"."</th>\n<th>".$duration_format."</th>\n</tr>";
push @all_group,$html;
}
}
delete $relat{$timstamp_compare};
$order[$i]="";
$num = scalar keys %relat;
last;
}else{ }
}
}
}
}else{ # do nothing }
}
```
* #### 時間轉換
時間的近位制。把計算出來的時間長度坐進為換算用。
```
sub time_compute{
my $time = shift;
my $day=int($time/60/60/24);
my $hr1=int($time/60/60);
my $hr=int(($time/60/60)-($day*24));
my $min=int(($time-60*60*$hr1)/60);
my $sec=int($time-60*60*24*$day-60*60*$hr-60*$min);
if ($time >= 24*60*60) { sprintf "$day day $hr hr $min min $sec sec";}
elsif ($time >= 60*60) { sprintf "$hr hr $min min $sec sec";}
elsif ($time >= 60) { sprintf "$min min $sec sec";}
else { sprintf "$sec sec";}
}
```
* ### 寄信模組 MIME::Lite
perl 其實有很多寄信模組可以使用。選擇 MIME::Lite,他是把信的內容用html在呈現,比較可以做一點設計。使用 MIME::Lite 套件來設計信的結構分成2塊,寄件資訊和內容本體。
1. 寄件資訊 : `$msg = MIME::Lite->new();`
```
my $msg = MIME::Lite->new(
From => 'netsys@nctu.edu.tw', #寄件
To => 'closeguan@nctu.edu.tw', #收件
Cc => 's6437094@gmail.com', #副本
Subject => '[WAF daily report] :all group WAF report', #標題
Type => 'multipart/related
);
```
2.信件內容:
裡面要寫的內容都在 `$msg->attach();` ,可以用 `html` 的語法去調整呈現。
style 屬性來指定該 HTML 元素的樣式
```
$msg->attach(
Type => 'text/html',
Data => qq{
<h4>Dear, all,</h4>
<p>
</p>
<table style="width:80%" border="1" >
<caption><font size="5" face="verdana">[$date_yesterday all_group_WAF_report]</font></caption>
<tr bgcolor=#b0e4f2>
$header
</tr>
$all_group_html #把排列好的資料直接貼上
</table>
<p>-------------</p>
<p>Please feel free to bring up any suggestion or problem you may have.</p>
<p>Sincerely,</p>
<p>
Division of NetSys<br><br>
Contact person : Yu-Chen Huang(黃俞禎)<br>
Contact number : 03-5712121 # 52861
</p>
},
);
$msg->send();
$msg->print(\*STDOUT);
```
# report 範本

# 用R繪製周報統計圖表

### 1. 累積次數
上圖呈現近雙週waf health monitor 監控次數統計呈現,以縱軸呈現日期,橫軸呈偵測到的現事件次數,並以顏色區分主機。圖中waf health monitor 監控預設參數,是以每5秒進行一次syn-ack 詢問,兩次未於2秒內未收到 ack 回應,則會等待30秒後才接續syn-ack 詢問。
```
#1 import library
library(ggplot2)
library(ggpubr)
# import data as data frame
df <- read.table("2020_0316-0329_00.txt", header = T)
dd <- c("03/16(Mon.)", "03/17(Tue.)", "03/18(Wen.)", "03/19(Thr.)", "05/20(Fri.)", "03/21(Sat.)", "03/22(Sun.)","03/23(Mon.)", "03/24(Tue.)","03/25(Wen.)", "03/26(Thr.)", "03/27(Fri.)", "03/28(Sat.)", "03/29(Sun.)")
# polt display
ggplot(df, aes(x=Date, fill=domain)) +
geom_bar(width = 0.5, position = "stack") +
geom_text(stat="count",
aes(label=..count..),
position = position_stack(vjust = 0.5),
color=I("black"),
size=3) +
labs(title = "WAF daily report",
subtitle = "2020/3/16 ~ 2020/3/29",
x = "Date", y = "Domain Frequency") +
scale_x_discrete(labels = dd) +
coord_flip() +
theme(legend.position = "bottom")
```
### 2. 發生時間分布點
WAF HTTP/HTTPS 服務偵測圖,根據一週個時間點鎖呈現服務中段時間分佈,僅呈現偵測到中斷時間大於30秒之資訊,圖示描述如下:
* 以[顏色]區分不同[服務(service ip) ]、以圖形[大小]表示[down的時間]。
* 以[形狀]表示服務隸屬[組別],三角形▲表示[校務資訊組]、圓形●表示[技術發展組]。

```
#1) import library
library(ggplot2)
library(chron)
library(ggrepel)
#2) load data
raw <- read.table("2020_0316-0329_00.txt", header = T)
#3) select duration > 30以上的資料
v1 <- raw$Date[raw$durations>30]
v2 <- raw$Division[raw$durations>30]
v3 <- raw$ip[raw$durations>30]
v4 <- raw$port[raw$durations>30]
v5 <- raw$domain[raw$durations>30]
v6 <- raw$down_time[raw$durations>30]
v7 <- raw$up_time[raw$durations>30]
v8 <- raw$durations[raw$durations>30]
all <- data.frame(v1, v2, v3, v4, v5, v6, v7, v8)
names(all) <- names(raw)
#4) x & y 軸上的刻度
dd <- c("03/16(Mon.)", "03/17(Tue.)", "03/18(Wen.)", "03/19(Thr.)", "05/20(Fri.)", "03/21(Sat.)", "03/22(Sun.)","03/23(Mon.)", "03/24(Tue.)","03/25(Wen.)", "03/26(Thr.)", "03/27(Fri.)", "03/28(Sat.)", "03/29(Sun.)")
tt <- c(
"00:00","01:00","02:00","03:00","04:00","05:00","06:00","07:00","08:00","09:00","10:00","11:00","12:00","13:00","14:00","15:00","16:00","17:00","18:00","19:00","20:00","21:00","22:00","23:00","24:00"
)
#5) 採用灰色background
theme_set(theme_gray())
#6) plot
#6.1 basic plot
g <- ggplot(all, aes(x=times(all$down_time), y=all$Date)) +
geom_point(aes(color=as.factor(all$domain), shape = all$Division, size = all$durations)) +
facet_wrap( ~ all$port, nrow=2)
#6.2 座標範圍
# 時間資料與數值資料的轉換: 01:00:00 = 1/24
# 記得修該時間軸範圍時也要修改軸上的標籤tt
g1 <- g +
coord_cartesian(xlim=c(0/24,24/24)) +
scale_x_continuous(breaks=seq(0/24,24/24,1/24), labels = tt) +
scale_y_discrete(labels = dd)
#6.3 標題與座標軸文字
# 記得修改subtitle的日期
g2 <- g1 +
labs(title = "WAF daily report",
subtitle = "2020 3/16~3/29",
y = "Date \n",
x = "Down Time") +
theme(axis.text.x = element_text(size = 12,
angle = 45,
vjust = 1.2,
hjust = 1.2),
axis.title.x = element_text(size = 14),
axis.text.y = element_text(size = 14),
axis.title.y = element_text(size = 18),
plot.title=element_text(size=10,
face="bold",
family="American Typewriter",
color="blue",
hjust=0.5,
lineheight=1.2),
plot.subtitle=element_text(size=15,
family="American Typewriter",
face="bold",
hjust=0.5)
)
#6.4 圖例
g3 <- g2 +
labs(color="domain\n", size="duration (sec)\n", shape="divisions\n") +
theme(legend.position="bottom",
legend.box = "horizontal",
legend.background = element_rect(fill="gray",
size=0.5, linetype="solid",colour ="black")) +
guides(shape = guide_legend(order = 1, ncol=1),
size = guide_legend(order = 2, ncol=1),
color = guide_legend(order=3, ncol=3))
#6.5 將down time為5:00之後的點標上domain name
v5[times(all$down_time) < times("05:00:00")] <- NA
#v5[times(all$down_time) < times("05:00:00")] <- NA
g4 <- g3 +
geom_label_repel(aes(label = v5),
box.padding = 0.35,
point.padding = 0.5,
segment.color = 'grey50')
#7) save plot
g4
ggsave(file="0plot.pdf",device="pdf" ,width=35.7 , heigh=23 , unit="cm")
```
---
# WAF 區架構圖
目前cloud區與WAF區的網路架構。

# NetScaaler crontab
```
root@ns# crontab -l
0 1 * * * sh /flash/nsconfig/script/nslogren.sh
#0 */1 * * * /usr/sbin/ntpdate -s 118.163.81.61
```
# NetScaaler script view
確認 `/flash/nsconfig/script/nslogren.sh`
```
#!/bin/sh
checkdate=`date -v -1d +%Y%m%d`
checkmonth=`date -v -1d +%Y%m`
checkcurrentmonth=`date +%Y%m`
mv /var/log/ns.log.0.gz /var/log/ns.log.$checkdate.gz
find /var/nslog/ -iregex "/var/nslog/newnslog.[0-9]\{1,3\}.*.gz" -exec ls -l -D '%Y%m%d-%H%M' {} \; | nawk '{print "mv "$7" /var/nslog/newnslog."$6".tar.gz" }' | /usr/bin/bash
find /var/nslog/*.gz -ctime +180 -exec rm {} \;
echo 0 > /var/nslog/nslog.nextfile
if [ ! -d "/var/log/$checkmonth" ] && [ $checkmonth != $checkcurrentmonth ] ; then
mkdir /var/log/$checkmonth
mv /var/log/ns.log.$checkmonth* /var/log/$checkmonth
cp /var/log/$checkmonth/ns.log.$checkdate.gz /var/log
fi
crontab -l > /flash/nsconfig/cron.txt
```
新增一條,讓換月時候不會再 /var/log 抓不到東西。
cp /var/log/$checkmonth/ns.log.$checkdate.gz /var/log