Seed Lab 实验 Firewall

Seed Lab 实验 Firewall

Task 1 使用LKM和Netfilter分别实现一个简单的防火墙

Task 1.A: Implement a Simple Kernel Module

(1)编译内核文件Makefile

在make的时候遇到一些问题,如果直接使用SeedLab给的hello.c进行make,会出现如下报错

image-20250106132555218

这是由于内核模块的代码缺少 MODULE_LICENSE() 宏。MODULE_LICENSE() 是一个必须包含的宏,用于指示该模块的许可证类型。在大多数情况下,内核模块必须指定许可证类型,以便内核能够正确地处理和加载模块。如果没有提供许可证类型,内核会认为模块是“非自由”的,这会导致一些限制(例如,某些内核符号将无法访问)

解决方法:

在源代码中添加MODULE_LICENSE() 宏,在头文件声明后加入以下代码即可:

MODULE_LICENSE("GPL");  // 可以根据你的需要选择适当的许可证类型

然后就可以正常make了。

image-20250224161706893

(2)生成的内核模块在hello.ko中,可以进行查看删除模块

lsmod

image-20250106132917147

(3)把hello.ko插入到内核中,再开启一个窗口进行监控内核。

sudo insmod hello.ko
demsg -k -w

image-20250106133040105

(4)从内核中删除hello。

sudo rmmod hello
demsg -k -w

image-20250106133252545

Task 1.B: Implement a Simple Firewall Using Netfilter

(1) 先make

image-20250106133612119

(2) 测试网络,可以连上8.8.8.8.

dig @8.8.8.8 www.example.com

image-20250106133724126

(3) 运行实验中给定的内核程序,并插入到内核中

sudo insmod seedFilter.ko
lsmod | grep seedFilter

image-20250106134117068

dmesg -k -w

image-20250106133947331

(4) 查看防火墙是否可以使用

dig @8.8.8.8 www.example.com

image-20250106134143074

可以使用了。

(5) 增加钩子的数量和位置

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_ether.h>
#include <linux/inet.h>


static struct nf_hook_ops hook1, hook2,hook3,hook4,hook5; 


unsigned int printInfo(void *priv, struct sk_buff *skb,
                 const struct nf_hook_state *state)
{
   struct iphdr *iph;
   char *hook;
   char *protocol;

   switch (state->hook){
     case NF_INET_LOCAL_IN:     hook = "LOCAL_IN";     break; 
     case NF_INET_LOCAL_OUT:    hook = "LOCAL_OUT";    break; 
     case NF_INET_PRE_ROUTING:  hook = "PRE_ROUTING";  break; 
     case NF_INET_POST_ROUTING: hook = "POST_ROUTING"; break; 
     case NF_INET_FORWARD:      hook = "FORWARD";      break; 
     default:                   hook = "IMPOSSIBLE";   break;
   }
   printk(KERN_INFO "*** %s\n", hook); // Print out the hook info

   iph = ip_hdr(skb);
   switch (iph->protocol){
     case IPPROTO_UDP:  protocol = "UDP";   break;
     case IPPROTO_TCP:  protocol = "TCP";   break;
     case IPPROTO_ICMP: protocol = "ICMP";  break;
     default:           protocol = "OTHER"; break;

   }
   // Print out the IP addresses and protocol
   printk(KERN_INFO "    %pI4  --> %pI4 (%s)\n", 
                    &(iph->saddr), &(iph->daddr), protocol);

   return NF_ACCEPT;
}


int registerFilter(void) {
   printk(KERN_INFO "Registering filters.\n");

   hook1.hook = printInfo;
   hook1.hooknum = NF_INET_LOCAL_OUT;
   hook1.pf = PF_INET;
   hook1.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook1);

   hook2.hook = printInfo;
   hook2.hooknum = NF_INET_POST_ROUTING;
   hook2.pf = PF_INET;
   hook2.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook2);
   
   hook3.hook = printInfo;
   hook3.hooknum = NF_INET_FORWARD;
   hook3.pf = PF_INET;
   hook3.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook3);

   hook4.hook = printInfo;
   hook4.hooknum = NF_INET_LOCAL_IN;
   hook4.pf = PF_INET;
   hook4.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook4);
   
   hook5.hook = printInfo;
   hook5.hooknum = NF_INET_PRE_ROUTING;
   hook5.pf = PF_INET;
   hook5.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook5);

   return 0;
}

void removeFilter(void) {
   printk(KERN_INFO "The filters are being removed.\n");
   nf_unregister_net_hook(&init_net, &hook1);
   nf_unregister_net_hook(&init_net, &hook2);
   nf_unregister_net_hook(&init_net, &hook3);
   nf_unregister_net_hook(&init_net, &hook4);
   nf_unregister_net_hook(&init_net, &hook5);
}

module_init(registerFilter);
module_exit(removeFilter);

MODULE_LICENSE("GPL");

image-20250109005153981

(6) 禁止ping和telnet

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/inet.h>


static struct nf_hook_ops hook1, hook2; 


unsigned int preventPing(void *priv, struct sk_buff *skb,
                       const struct nf_hook_state *state)
{
   struct iphdr *iph;	//取出ip头
   struct tcphdr *tcph;	//取出tcp头
   struct icmphdr *icmph; 
   iph = ip_hdr(skb);
   tcph = tcp_hdr(skb);
   icmph = icmp_hdr(skb);
   unsigned char* saddr = (unsigned char*)&iph->saddr;	//源IP地址
   if (iph->protocol == IPPROTO_ICMP  && icmph->type==ICMP_ECHO &&
    (int)saddr[0] == 10 && saddr[1] == 0 && saddr[2] == 2 && saddr[3] == 6)
   { 
      printk(KERN_INFO "丢弃ping包\n");
      return NF_DROP;
   }
   return NF_ACCEPT;
}

unsigned int preventTelnet(void *priv, struct sk_buff *skb,
                 const struct nf_hook_state *state)
{
   struct iphdr *iph;	//取出ip头
   struct tcphdr *tcph;	//取出tcp头
   iph = ip_hdr(skb);
   tcph = tcp_hdr(skb);
   unsigned char* saddr = (unsigned char*)&iph->saddr;	//源IP地址
   if (iph->protocol == IPPROTO_TCP && tcph->dest == htons(23) && 
    (int)saddr[0] == 10 && saddr[1] == 0 && saddr[2] == 2 && saddr[3] == 6)
   { 
      printk(KERN_INFO "丢弃telnet包\n");
      return NF_DROP;
   }
   return NF_ACCEPT;
}


int registerFilter(void) {
   printk(KERN_INFO "Registering filters.\n");

   hook1.hook = preventPing;
   hook1.hooknum = NF_INET_PRE_ROUTING;
   hook1.pf = PF_INET;
   hook1.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook1);

   hook2.hook = preventTelnet;
   hook2.hooknum = NF_INET_PRE_ROUTING;
   hook2.pf = PF_INET;
   hook2.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook2);

   return 0;
}

void removeFilter(void) {
   printk(KERN_INFO "The filters are being removed.\n");
   nf_unregister_net_hook(&init_net, &hook1);
   nf_unregister_net_hook(&init_net, &hook2);
}

module_init(registerFilter);
module_exit(removeFilter);

MODULE_LICENSE("GPL");

然后

make
sudo insmod seedFilter.ko
lsmod | grep seedFilter

image-20250223224712189

ping不通,telnet不通

卸载内核模块之后可以ping和telnet

Task 2 使用Linux自带的iptables建立防火墙

Task 2.A: Protecting the Router

启动docker

image-20250223232223699

可以直接使用样例代码

iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
iptables -P OUTPUT DROP 
iptables -P INPUT DROP 

# 清除我们定义的规则
iptables -F
iptables -P FORWARD ACCEPT

允许icmp数据包的发送和接受,其他的输入输出数据包全部丢弃

进入路由器配置上述代码

iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
iptables -P OUTPUT DROP 
iptables -P INPUT DROP 

image-20250223231813726

进入hostA

ping 10.9.0.11

可以ping

telnet 10.9.0.11

不可以telnet

image-20250223231911832

Task 2.B: Protecting the Internal Network

这个任务的要求是;外网机器不能ping内网;外网机器能ping路由器;内网机器能ping外网机器;内网与外网之间的其他数据包需要被丢弃

外网的icmp请求丢弃;内网的icmp请求放行;外网的icmp回复放行;其他数据包都丢弃

iptables -A FORWARD -i eth0 -p icmp --icmp-type echo-request -j DROP
iptables -A FORWARD -i eth1 -p icmp --icmp-type echo-request -j ACCEPT
iptables -A FORWARD -i eth0 -p icmp --icmp-type echo-reply -j ACCEPT
iptables -P FORWARD DROP

# 清除我们定义的规则
iptables -F
iptables -P FORWARD ACCEPT

image-20250223232109370

进host1 内部主机

可以ping外部主机

image-20250223232254794

进hostA

外部主机ping不通内部主机,但是ping的通路由器

image-20250223233041958

image-20250223233112526

内网与外网之间的其他数据包需要被丢弃

image-20250223233154704

Task 2.C: Protecting Internal Servers

任务要求:外网只能远程登陆192.168.60.5,不能登陆其他内网机子;外网机子不能连接内网服务;内网机子可以使用其他内网机子的服务;内网机子无法连接外网服务

外网可以远程登陆192.168.60.5(第一行是远程登陆请求以及命令传输可以,第二行是192.168.60.5数据返回可以);其他数据包被丢弃

iptables -A FORWARD -i eth0 -p tcp -d 192.168.60.5 --dport 23 -j ACCEPT
iptables -A FORWARD -i eth1 -p tcp -s 192.168.60.5 --sport 23 -j ACCEPT
iptables -P FORWARD DROP

# 清除我们定义的规则
iptables -F
iptables -P FORWARD ACCEPT

image-20250223233316487

外部机子可以连接上192.168.60.5,但是连接不上其他内部机子

image-20250223233417127

image-20250223233430339

内部机子可以连接其他内部机子,但是无法连接外部机子

image-20250223233506560

image-20250223233525780

Task 3 记录链接及建立状态防火墙

Task 3.A: Experiment with the Connection Tracking

hostA上pinghost1,host1连接跟踪信息

conntrack -L

连接前没有跟踪信息,连接时有,连接后20秒后消失

image-20250223234402096

UDP:

hostA:

nc -u 192.168.60.5 9090

host1:

nc -lu 9090

两台机子上输入一些东西

在hostA上追踪

conntrack -L

image-20250223234854527

一分钟左右后消失

TCP:

HOSTA:

nc 192.168.60.5 9090

HOST1:

nc -l 9090

image-20250224001030912

TCP的追踪信息在建立起连接之后就已经存在,发送消息之后追踪信息不变,发送消息之后一段时间追踪信息依旧存在,并且状态是ESTABLISHED,于是关闭TCP连接,发现追踪信息状态转变为TIME_WAIT并且持续两到三分钟之后消失。

Task 3.B: Setting Up a Stateful Firewall

任务要求:外网只能远程登陆192.168.60.5,不能登陆其他内网机子;外网机子不能连接内网服务;内网机子可以使用其他内网机子的服务;内网机子可以连接外网服务

iptables -A FORWARD -i eth0 -p tcp -d 192.168.60.5 --dport 23 --syn  -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD -i eth1 -p tcp --syn  -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD  -p tcp  -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD  -p tcp -j DROP
iptables -P FORWARD ACCEPT

iptables -F
iptables -P FORWARD ACCEPT

和2.c除了最后一点都一样

Task 4 限制网络数据传输

样例代码

iptables -A FORWARD -s 10.9.0.5 -m limit \
--limit 10/minute --limit-burst 5 -j ACCEPT
iptables -A FORWARD -s 10.9.0.5 -j DROP

一开始较快,后面放缓

image-20250224001552553

这是因为有了第二条命令,超过限制的数据包直接丢弃了。

Task 5 负载平衡

均匀分配:

iptables -t nat -A PREROUTING -p udp --dport 8080 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination 192.168.60.5:8080
iptables -t nat -A PREROUTING -p udp --dport 8080 -m statistic --mode nth --every 2 --packet 0 -j DNAT --to-destination 192.168.60.6:8080
iptables -t nat -A PREROUTING -p udp --dport 8080 -m statistic --mode nth --every 1 --packet 0 -j DNAT --to-destination 192.168.60.7:8080

host123上监听

nc -luk 8080

hostA上nc

echo hello | nc -u 10.9.0.11 8080

image-20250224002126214

host1,2,3:

image-20250224002135348

image-20250224002151154

image-20250224002225195

还有一种按照概率分配的

iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode random --probability 0.1 \
-j DNAT --to-destination 192.168.60.5:8080

iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode random --probability 0.3 \
-j DNAT --to-destination 192.168.60.6:8080

iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode random --probability 0.6 \
-j DNAT --to-destination 192.168.60.7:8080

结果大致同上,就是按照概率分配。

posted @ 2025-02-24 16:14  hjdssj  阅读(393)  评论(0)    收藏  举报