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白名单配置文件生成器 *

* 白名单模式:如果表中有启用的IP记录,则仅允许这些IP访问;表为空则放行所有IP。 *

* 输出文件: /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 配置 *

     * # 有白名单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;
     * }
     * 
* 有IP时 default 0(默认拒绝,仅白名单放行); * 无IP时 default 1(全部放行)。 *

* 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 ipList = ipWhiteMapper.selectList( new LambdaQueryWrapper() .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"; } }