参考
- 依据Linux命令
- 以及sysconf下现有的iptables命令,详见hsm_sysconf_server/src/sysconf_server.cpp中的firewall规则。
接口名称
- firewall_manager
目的(现实)
- 根据网口直连获取当前eth0和eth1的各种信息
- 保证设置的正确性 以及要针对管理口和服务口设计不同的初始化操作以及继承的恢复操作。
- 并且要把防火墙规则通过数据库存储
目的(虚拟)
- 最终设计出封装性良好的接口与实现
- 要经过详细的额外设计保证其通用性
开始
第一步
- 首先需要学习以及尝试使用iptables命令控制防火墙
介绍
- iptables 的主要功能是实现对网络数据包进出设备及转发的控制。当数据包需要进入设备、从设备中流出或者由该设备转发、路由时,都可以使用 iptables 进行控制
- iptables 是集成在 Linux 内核中的包过滤防火墙系统。使用 iptables 可以添加、删除具体的过滤规则,iptables 默认维护着 4 个表和 5 个链,所有的防火墙策略规则都被分别写入这些表与链中。
- “四表”是指 iptables 的功能,默认的 iptable s规则表有 filter 表(过滤规则表)、nat 表(地址转换规则表)、mangle(修改数据标记位规则表)、raw(跟踪数据表规则表):
- filter 表:控制数据包是否允许进出及转发,可以控制的链路有 INPUT、FORWARD 和 OUTPUT。
- nat 表:控制数据包中地址转换,可以控制的链路有 PREROUTING、INPUT、OUTPUT 和 POSTROUTING。
- mangle:修改数据包中的原数据,可以控制的链路有 PREROUTING、INPUT、OUTPUT、FORWARD 和 POSTROUTING。
- raw:控制 nat 表中连接追踪机制的启用状况,可以控制的链路有 PREROUTING、OUTPUT。
- “五链”是指内核中控制网络的 NetFilter 定义的 5 个规则链。每个规则表中包含多个数据链:INPUT(入站数据过滤)、OUTPUT(出站数据过滤)、FORWARD(转发数据过滤)、PREROUTING(路由前过滤)和POSTROUTING(路由后过滤),防火墙规则需要写入到这些具体的数据链中。
2>/dev/null
- 2> / dev / null是什么意思?
iptables -c用法
- 这使管理员能够初始化规则的包和字节计数器(在INSERT,APPEND,REPLACE操作期间)
重启防火墙
- Reloading iptables
2021年4月20日
今日存在的疑惑 2012年4月21日解决
execute_command干嘛的
#include "common/logging.h"#include <cstdarg>
#include <cstdio>
#include <string>namespace hsm {
namespace sys {int execute_command(const std::string &command, std::string *output = nullptr,bool redirect_stderr = false) {const auto &cmd = redirect_stderr ? command + " 2>&1" : command;auto pipe = popen(cmd.c_str(), "r");if (!pipe) {common::log_error("Can not exec command: {}", cmd);return -1;}// consume the output{char buffer[1024] = {0};while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {if (output) {output->append(buffer);}}}return pclose(pipe);
}} // namespace sys
} // namespace hsm
- Linux C popen()函数详解
- C语言pclose()函数:关闭管道I/O
- linux shell中"2>&1"含义
#include <string>
#include <cstdio>int main(){FILE *fp;char buffer[1024] = {0};fp = popen("cat /etc/passwd","r");fgets(buffer,sizeof(buffer),fp);printf("%s",buffer);pclose(fp);
}
fmt::format是否实现 字符串替换 {}
- format函数之几种常规用法
- c++使用fmt::format格式化字符串
- c++ fmt 库安装和使用示例
- Fmt:更方便的 c++ format 库
iptables -C啥意思
- -c, --set-counters PKTS BYTES
- This enables the administrator to initialize the packet and byte counters of a rule (during INSERT, APPEND, REPLACE operations).
- -c, --set-counters PKTS BYTES 这使管理员能够初始化一个规则的数据包和字节计数器(在INSERT、APPEND、REPLACE操作期间)。
{} 和 {1} 之间的区别
- {} 将输入的参数进行替代
- {1} 将输入的第一个参数进行替换
相关宏定义的声明
SYS_CONF_OK | #define SYS_CONF_OK 0x0 |
SYS_CONF_SHELL_ERROR | #define SYS_CONF_SHELL_ERROR 0x2 |
FIREWALL_CONF_FILE_PATH | #define FIREWALL_CONF_FILE_PATH "/etc/firewall.conf" |
FIREWALL_RULES_FILE_PATH | #define FIREWALL_RULES_FILE_PATH "/etc/firewall.rules" |
FIREWALL_INIT_SCRIPT | #define FIREWALL_INIT_SCRIPT "/etc/init.d/11-firewall" |
SYS_CONF_UNKNOWN_TYPE | #define SYS_CONF_UNKNOWN_TYPE 0x4 |
std::unique_ptr<KeyValueConfStorage> firewall_config_storage; KeyValueConfStorage结构
- storage.h 继承了类 SysconfStorage
#pragma once#include "common/common.h"
#include "common/logging.h"#include <mutex>
#include <string>#include <json/json.h>namespace hsm {
namespace sys {class SysconfStorage {
public:explicit SysconfStorage(std::string persistence_file);virtual ~SysconfStorage() = default;virtual std::string serialize() = 0;bool sync(bool make_backup = true);void rollback();protected:std::string storage_file;std::string backup_file;std::mutex sync_data_mutex;
};class GlobalDataStorage : public SysconfStorage {
public:explicit GlobalDataStorage(std::string persistence_file);~GlobalDataStorage() override { sync(); }std::string serialize() override;bool update_device_status(size_t device_status, bool sync_now = true);bool register_ukey(const std::string &ukey_info, bool sync_now = true);bool unregister_ukey(const std::string &ukey_info, bool sync_now = true);const json &get(const std::string &key) const;json &get(const std::string &key);const json &get() const { return data; }private:json data;
};class KeyValueConfStorage : public SysconfStorage {
public:explicit KeyValueConfStorage(std::string persistence_file);~KeyValueConfStorage() override { sync(); }std::string serialize() override;bool set(const std::string &key, const std::string &value,bool sync_now = true);const std::string &get(const std::string &key);std::map<std::string, std::string> &get() { return data; }private:std::map<std::string, std::string> data;
};} // namespace sys
} // namespace hsm
- storage.cpp 头文件中声明函数的定义
#include "storage.h"
#include "common/filesystem.h"
#include "common/string_utils.h"#include <boost/filesystem.hpp>
#include <chrono>
#include <fstream>
#include <sstream>namespace hsm {
namespace sys {namespace fs = boost::filesystem;size_t get_time_us() {using namespace std::chrono;return duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
}SysconfStorage::SysconfStorage(std::string persistence_file): storage_file(std::move(persistence_file)),backup_file(storage_file + "-") {}bool SysconfStorage::sync(bool make_backup) {std::lock_guard<std::mutex> lock(sync_data_mutex);bool make_backup_ok = false;if (make_backup && fs::exists(storage_file)) {boost::system::error_code ec{};fs::rename(storage_file, backup_file, ec);if (ec) {common::log_error("Failed to sync, can not create backup file ({})",backup_file);return false;}make_backup_ok = true;}auto content = serialize();std::ofstream f(storage_file);f << content;if (f.fail()) {common::log_error("Failed to sync, can not write data to storage file ({})",storage_file);// revert storage fileif (make_backup_ok) {boost::system::error_code ec{};fs::rename(backup_file, storage_file, ec);if (ec) {common::log_fatal("Failed to sync, can not revert storage file ({}) from backup file ""({}), this is a unexpected fatal error",storage_file, backup_file);}}return false;}f.close();// sync filesystemsif (common::file_sync(storage_file) && common::file_sync(backup_file)) {common::log_debug("Files are synchronized");return true;}return false;
}void SysconfStorage::rollback() {boost::system::error_code ec{};fs::rename(backup_file, storage_file, ec);if (ec) {common::log_fatal("Can not revert storage file ({}) from backup file ""({}), this is a unexpected fatal error",storage_file, backup_file);}
}GlobalDataStorage::GlobalDataStorage(std::string persistence_file): SysconfStorage(std::move(persistence_file)) {{std::ifstream f(storage_file);if (f.is_open()) {std::stringstream ss;ss << f.rdbuf();data = json::parse(ss.str(), nullptr, false);}}if (!data.is_object()) {// try read backup filestd::ifstream f(backup_file);if (f.is_open()) {std::stringstream ss;ss << f.rdbuf();data = json::parse(ss.str(), nullptr, false);}if (!data.is_object()) { // initialize an empty storagedata = json();data["device_status"] = 0;data["ukey_list"] = json::object();sync();} else {sync(false);}}// simple checkif (!data.contains("device_status") || !data.contains("ukey_list")) {common::log_fatal("Invalid sysconf storage content");}
}std::string GlobalDataStorage::serialize() { return data.dump(); }bool GlobalDataStorage::update_device_status(size_t device_status,bool sync_now) {data["device_status"] = device_status;IF_LIKELY(sync_now) { return sync(); }return true;
}bool GlobalDataStorage::register_ukey(const std::string &ukey_info,bool sync_now) {json ukey_item;ukey_item["sn"] = ukey_info;ukey_item["time_us"] = get_time_us();data["ukey_list"][ukey_info] = std::move(ukey_item);IF_LIKELY(sync_now) { return sync(); }return true;
}bool GlobalDataStorage::unregister_ukey(const std::string &ukey_info,bool sync_now) {data["ukey_list"].erase(ukey_info);IF_LIKELY(sync_now) { return sync(); }return true;
}const json &GlobalDataStorage::get(const std::string &key) const {return data[key];
}json &GlobalDataStorage::get(const std::string &key) { return data[key]; }KeyValueConfStorage::KeyValueConfStorage(std::string persistence_file): SysconfStorage(std::move(persistence_file)) {{std::ifstream f(storage_file);if (f.is_open()) {std::stringstream ss;ss << f.rdbuf();data = common::parse_config(ss.str());}}if (data.empty()) {// try read backup filestd::ifstream f(backup_file);if (f.is_open()) {std::stringstream ss;ss << f.rdbuf();data = common::parse_config(ss.str());}// sync such valid backup dataif (!data.empty()) {sync(false);}}
}std::string KeyValueConfStorage::serialize() {if (data.empty()) {data["this_is_an_empty_config"] = "";}std::ostringstream ss;for (const auto &item : data) {ss << item.first << "=" << item.second << std::endl;}return ss.str();
}bool KeyValueConfStorage::set(const std::string &key, const std::string &value,bool sync_now) {data[key] = value;IF_LIKELY(sync_now) { return sync(); }return true;
}const std::string &KeyValueConfStorage::get(const std::string &key) {return data[key];
}} // namespace sys
} // namespace hsm
获取eth0和eth1的各种信息
- getifaddrs()和struct ifaddrs的使用,获取本机IP
- getifaddrs(3) — Linux manual page
- getifaddrs
- ifaddrs
- getifaddrs()--Return All Interface Addresses
任务
- 梳理逻辑,画uml图
通用模型设计(补充)
- 限制每个客户端最大并发数不超过3个(xshell终端)
- iptables –A INPUT –p tcp --dport 22 –s 192.168.1.0/24 –m connlimt –connlimit-above 2 –j DROP
- 限制速度(-m limit --limit匹配速率| --burst缓冲数量)
- iptables –A INPUT –d 192.168.1.63 –m limit --limit 5/s --burst 100–j ACCEPT(在100个包内不限速,超过一百个包限制每秒只传5个包)
- 查看当前的iptables的状态
- 1,iptables -nL #默认查看filter表的状态,如果需要查看其他表的状态加上 -t 表名
- 2,iptables -nL --line-numbers #可以列出序列号,数据的插入和删除和查看
- 3,iptables -nL --line-numbers --verbose #可以查看到包过滤的流量统计,访问次数等
- 还原配置
- iptables-restore < /etc/sysconfig/iptables
- 限制单个IP一分钟内建立的连接数
- iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 25 -j REJECT
- 拒绝所有访问
- iptables -A INPUT -j DROP #这个一般放到最后,不然会对前面的规则造成影响
- 针对端口开放(需要指明协议)
- iptables -I INPUT -p tcp --dport 22 -j ACCEPT
- 针对协议开放
- iptables -I INPUT -p imcp -j ACCEPT
- 根据时段限制访问
- iptables -A INPUT -p tcp -m time --timestart 00:00 --timestop 02:00 -j DROP #这里的时间是指UTC时间记得换算
2021年4月22日
注意事项
1,数据库存储 防火墙的规则,将先前的命令进行细分
iptables -A INPUT -p tcp -s 10.159.1.0/24 --dport 22 -j ACCEPT
- 比如上述命令,字段细分 INPUT、目标设备(eth0/eth1)、端口 、跳转行为(-j) 、ACCEPT等
2,同理 也将上述字段 作为 函数的一个输入参数
2021年4月23号
- iptables -F #清除所有制定的规则
- iptables -X#清除用户自定义的chain
- iptables -Z#将所有流量统计归0
- 但是并非执行后就万事大吉了。你仍然需要检查规则是不是真的清空了,因为有的linux发行版上这个命令不会清除NAT表中的规则,此时只能手动清除:iptables -t NAT -F
- iptables工作原理及iptables命令行使用介绍
- 只允许 mac地址为aa:bb:cc:dd:ee:ff 的机器访问本地ssh端口 iptables -A INPUT -m mac --mac-source aa:bb:cc:dd:ee:ff -p tcp --dport 22 -j ACCEPT
- C++ 获取物理Mac地址
- C语言如何获得变量的物理地址以及简单的写时拷贝测试
- linux中查看网卡mac地址
- MAC地址是什么
- 使用iptables基于MAC地址进行访控
- Linux 查看网卡的MAC地址
linux查看网卡的部分具体命令
- /sbin/ifconfig | grep ether ether是使用ifconfig查看到的网口信息,有些Linux发行版本的MAC地址字段为HWaddr,有些Linux发行版本的MAC地址字段为ether。根据实际情况选择上面命令
-
如果想只提取网卡MAC地址,可以使用下面命令(用具体的网卡名替换xxx) ifconfig xxx | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}'
- cat /sys/class/net/xxx/address查看
- dmesg | grep eth
和MAC有关的相关命令
- 1、阻止MAC地址为XX:XX:XX:XX:XX:XX主机的所有通信: iptables -A INPUT -m mac --mac-source XX:XX:XX:XX:XX:XX -j DROP
- 2、允许MAC地址为XX:XX:XX:XX:XX:XX主机访问22端口: iptables -A INPUT -p tcp --destination-port 22 -m mac --mac-source XX:XX:XX:XX:XX:XX -j ACCEPT
参考链接
- Linux iptables命令详解
- Linux防火墙netfilter的编程接口libiptc简介
- Linux下使用libiptc库编程下发规则
- linux笔记防火墙之iptables入门
- sysconf
- iptables编程文章列表
- iptables随笔
- iptables-save
- iptables (简体中文)
- iptables(8) - Linux man page iptables -c用法
- iptables常用的命令汇总
- Linux 防火墙端口开放 屏蔽,IP开放屏蔽,协议开关 详解Linux 防火墙 传输协议(如TCP、UDP、ICMP)和服务类型(如HTTP、FTP和SMTP)等
- Iptables 常用命令汇总
- iptables详解及一些常用规则 yshenhn
- Linux iptables常用命令
- iptables
- 每天学习一个命令:iptables Linux 上的防火墙
- iptables工作原理及iptables命令行使用介绍
配置参考
- Linux下iptables防火墙配置详解
- Linux防火墙配置(iptables, firewalld)
- ubuntu中防火墙iptables配置
- iptables 参数端口映射