package cc.mrbird.febs.firewall.nginx;
|
|
import cc.mrbird.febs.firewall.entity.FirewallIpWhite;
|
import cc.mrbird.febs.firewall.entity.FirewallSite;
|
import cc.mrbird.febs.firewall.mapper.FirewallIpWhiteMapper;
|
import cc.mrbird.febs.firewall.mapper.FirewallSiteMapper;
|
import cn.hutool.core.io.FileUtil;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Component;
|
|
import java.io.File;
|
import java.nio.charset.StandardCharsets;
|
import java.util.Date;
|
import java.util.List;
|
|
/**
|
* Nginx IP白名单配置文件生成器
|
* <p>
|
* 白名单模式:如果表中有启用的IP记录,则仅允许这些IP访问;表为空则放行所有IP。
|
* <p>
|
* 输出文件: /etc/nginx/conf.d/firewall_ipwhite_{siteId}.conf
|
*
|
* @author auto-generated
|
* @date 2026-07-31
|
*/
|
@Slf4j
|
@Component
|
@RequiredArgsConstructor
|
public class IpWhitelistNginxConfigGenerator {
|
|
private final FirewallIpWhiteMapper ipWhiteMapper;
|
private final FirewallSiteMapper siteMapper;
|
|
/** Nginx 配置目录 */
|
private static final String NGINX_CONF_DIR = "/etc/nginx/conf.d/";
|
/** 配置文件前缀 */
|
private static final String CONF_PREFIX = "firewall_ipwhite_";
|
|
/**
|
* 生成 IP 白名单 map 配置
|
* <pre>
|
* # 有白名单IP时:
|
* map $remote_addr $allow_ip_1 {
|
* default 0;
|
* 1.2.3.4 1;
|
* 5.6.7.8 1;
|
* }
|
*
|
* # 无白名单IP时:
|
* map $remote_addr $allow_ip_1 {
|
* default 1;
|
* }
|
* </pre>
|
* 有IP时 default 0(默认拒绝,仅白名单放行);
|
* 无IP时 default 1(全部放行)。
|
* <p>
|
* Nginx 中判断: if ($allow_ip_1 = 0) { return 403; }
|
*
|
* @param siteId 站点ID
|
*/
|
public void generate(Long siteId) {
|
FirewallSite site = siteMapper.selectById(siteId);
|
if (site == null) {
|
log.warn("防火墙站点不存在: siteId={}", siteId);
|
return;
|
}
|
|
// 查询站点下所有启用的白名单IP(未过期或永久有效)
|
List<FirewallIpWhite> ipList = ipWhiteMapper.selectList(
|
new LambdaQueryWrapper<FirewallIpWhite>()
|
.eq(FirewallIpWhite::getSiteId, siteId)
|
.eq(FirewallIpWhite::getEnabled, 1)
|
.and(w -> w.isNull(FirewallIpWhite::getExpireTime)
|
.or().gt(FirewallIpWhite::getExpireTime, new Date()))
|
);
|
|
boolean hasIps = !ipList.isEmpty();
|
|
StringBuilder sb = new StringBuilder();
|
sb.append("# Firewall IP Whitelist Map\n");
|
sb.append("# Site: ").append(site.getSiteName()).append(" (").append(site.getDomain()).append(")\n");
|
sb.append("# Generated at: ").append(new Date()).append("\n");
|
sb.append("# Total IPs: ").append(ipList.size()).append("\n");
|
sb.append("# Whitelist active: ").append(hasIps).append("\n\n");
|
|
// 标记变量:白名单是否生效(有IP记录=1,无记录=0)
|
// 用 map 模拟,使变量在 http 级别可用
|
sb.append("# 是否启用白名单模式: ").append(hasIps ? "1=是" : "0=否").append("\n");
|
sb.append("map $host $ipwh_active_").append(siteId).append(" {\n");
|
sb.append(" default ").append(hasIps ? "1" : "0").append(";\n");
|
sb.append("}\n\n");
|
|
sb.append("map $remote_addr $allow_ip_").append(siteId).append(" {\n");
|
if (!hasIps) {
|
// 无白名单 → 全部放行
|
sb.append(" default 1;\n");
|
} else {
|
// 有白名单 → 默认拒绝,仅白名单IP放行
|
sb.append(" default 0;\n");
|
for (FirewallIpWhite ip : ipList) {
|
sb.append(" ").append(ip.getIp()).append(" 1;\n");
|
}
|
}
|
sb.append("}\n");
|
|
// 确定配置文件路径
|
String confFileName = NGINX_CONF_DIR + CONF_PREFIX + siteId + ".conf";
|
File confFile = new File(confFileName);
|
try {
|
FileUtil.writeString(sb.toString(), confFile, StandardCharsets.UTF_8);
|
log.info("Nginx IP白名单配置已生成: {}, 白名单IP数: {}", confFile.getAbsolutePath(), ipList.size());
|
} catch (Exception e) {
|
log.error("Nginx IP白名单配置文件写入失败: path={}", confFile.getAbsolutePath(), e);
|
throw new RuntimeException("Nginx IP白名单配置文件写入失败: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 获取 IP 白名单配置文件路径
|
*/
|
public static String getConfPath(Long siteId) {
|
return NGINX_CONF_DIR + CONF_PREFIX + siteId + ".conf";
|
}
|
}
|