Skip to content

RabbitMQ IP 白名单

概述

IP 白名单是一种基于源 IP 地址的访问控制机制,只允许预先定义的 IP 地址或网段访问 RabbitMQ 服务。这是保护 RabbitMQ 免受未授权访问的重要安全措施。

核心知识点

IP 白名单架构

┌─────────────────────────────────────────────────────────────┐
│                    IP 白名单访问控制                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                    ┌─────────────┐                          │
│                    │   Client    │                          │
│                    │  请求连接    │                          │
│                    └──────┬──────┘                          │
│                           │                                 │
│                           ▼                                 │
│                    ┌─────────────┐                          │
│                    │  IP 白名单   │                          │
│                    │   检查层     │                          │
│                    └──────┬──────┘                          │
│                           │                                 │
│              ┌────────────┼────────────┐                    │
│              │            │            │                    │
│              ▼            ▼            ▼                    │
│        ┌─────────┐  ┌─────────┐  ┌─────────┐              │
│        │ 白名单   │  │ 灰名单   │  │ 黑名单   │              │
│        │  允许    │  │  限制    │  │  拒绝    │              │
│        └────┬────┘  └────┬────┘  └────┬────┘              │
│             │            │            │                    │
│             ▼            ▼            ▼                    │
│        ┌─────────┐  ┌─────────┐  ┌─────────┐              │
│        │  允许    │  │  限速    │  │  拒绝    │              │
│        │  连接    │  │  连接    │  │  连接    │              │
│        └─────────┘  └─────────┘  └─────────┘              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

IP 白名单实现层次

层次实现方式优点缺点
网络层防火墙性能最高配置复杂
应用层RabbitMQ 配置配置简单性能较低
代理层Nginx/HAProxy灵活性高需要额外组件

白名单类型

┌─────────────────────────────────────────────────────────────┐
│                    IP 白名单类型                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 管理白名单                                              │
│     └── 控制管理界面访问权限                                 │
│     └── 最严格的安全要求                                    │
│                                                             │
│  2. 应用白名单                                              │
│     └── 控制应用服务器访问权限                               │
│     └── 按服务类型细分                                      │
│                                                             │
│  3. 集群白名单                                              │
│     └── 控制集群节点通信权限                                 │
│     └── 仅允许节点 IP                                       │
│                                                             │
│  4. 监控白名单                                              │
│     └── 控制监控系统集成权限                                 │
│     └── Prometheus、Grafana 等                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

配置示例

RabbitMQ 管理界面 IP 白名单

conf
# /etc/rabbitmq/rabbitmq.conf

# 管理界面 IP 白名单(通过代理或防火墙实现)
# RabbitMQ 本身不直接支持 IP 白名单,建议使用以下方式:

# 方式一:绑定到内网接口
management.tcp.ip = 10.0.0.10
management.tcp.port = 15672

# 方式二:通过 Nginx 代理实现白名单
# 见下方 Nginx 配置示例

Nginx 代理白名单

nginx
# /etc/nginx/conf.d/rabbitmq-management.conf

# 定义白名单
geo $allowed_ip {
    default 0;
    10.0.0.0/24 1;      # 管理网络
    192.168.1.100 1;    # 特定管理员 IP
    172.16.0.0/16 1;    # 内网
}

server {
    listen 15672;
    server_name rabbitmq.example.com;

    # 白名单检查
    if ($allowed_ip = 0) {
        return 403 "Access Denied";
    }

    location / {
        proxy_pass http://127.0.0.1:15672;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

HAProxy 白名单配置

# /etc/haproxy/haproxy.cfg

frontend rabbitmq_management
    bind *:15672
    
    # 白名单 ACL
    acl allowed_ips src 10.0.0.0/24 192.168.1.100 172.16.0.0/16
    
    # 拒绝非白名单 IP
    http-request deny unless allowed_ips
    
    default_backend rabbitmq_management_backend

backend rabbitmq_management_backend
    server rabbitmq1 127.0.0.1:15672 check

frontend rabbitmq_amqp
    bind *:5672
    
    # 应用服务器白名单
    acl app_ips src 10.0.1.0/24
    
    # TCP 模式白名单
    tcp-request connection reject unless app_ips
    
    default_backend rabbitmq_amqp_backend

backend rabbitmq_amqp_backend
    server rabbitmq1 127.0.0.1:5672 check

iptables IP 白名单

bash
#!/bin/bash
# IP 白名单防火墙脚本

# 创建白名单链
iptables -N WHITELIST

# 添加白名单 IP
iptables -A WHITELIST -s 10.0.0.0/24 -j ACCEPT
iptables -A WHITELIST -s 192.168.1.100 -j ACCEPT
iptables -A WHITELIST -s 172.16.0.0/16 -j ACCEPT

# 应用白名单到管理端口
iptables -A INPUT -p tcp --dport 15672 -j WHITELIST
iptables -A INPUT -p tcp --dport 15672 -j DROP

# 应用白名单到 AMQP 端口
iptables -A INPUT -p tcp --dport 5672 -s 10.0.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 5672 -j DROP

# 应用白名单到集群端口
iptables -A INPUT -p tcp --dport 25672 -s 10.0.2.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 25672 -j DROP

firewalld IP 白名单

bash
#!/bin/bash
# firewalld IP 白名单配置

# 创建富规则 - 管理界面
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port protocol="tcp" port="15672" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port protocol="tcp" port="15672" accept'

# 创建富规则 - AMQP
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" port protocol="tcp" port="5672" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" port protocol="tcp" port="5671" accept'

# 创建富规则 - 集群
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.2.0/24" port protocol="tcp" port="25672" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.2.0/24" port protocol="tcp" port="4369" accept'

# 重载配置
firewall-cmd --reload

PHP 代码示例

IP 白名单管理器

php
<?php

class IPWhitelistManager
{
    private $whitelistFile;
    private $whitelist = [];

    public function __construct(string $whitelistFile)
    {
        $this->whitelistFile = $whitelistFile;
        $this->loadWhitelist();
    }

    public function addIP(string $ip, string $description = ''): bool
    {
        if (!$this->isValidIP($ip)) {
            return false;
        }

        $this->whitelist[$ip] = [
            'ip' => $ip,
            'description' => $description,
            'added_at' => date('c')
        ];

        return $this->saveWhitelist();
    }

    public function removeIP(string $ip): bool
    {
        if (isset($this->whitelist[$ip])) {
            unset($this->whitelist[$ip]);
            return $this->saveWhitelist();
        }
        return false;
    }

    public function isAllowed(string $ip): bool
    {
        foreach ($this->whitelist as $allowed) {
            if ($this->ipInRange($ip, $allowed['ip'])) {
                return true;
            }
        }
        return false;
    }

    public function getAllowedIPs(): array
    {
        return array_values($this->whitelist);
    }

    private function ipInRange(string $ip, string $range): bool
    {
        if (strpos($range, '/') === false) {
            return $ip === $range;
        }

        list($subnet, $bits) = explode('/', $range);
        $ipLong = ip2long($ip);
        $subnetLong = ip2long($subnet);
        $mask = -1 << (32 - $bits);
        $subnetLong &= $mask;

        return ($ipLong & $mask) === $subnetLong;
    }

    private function isValidIP(string $ip): bool
    {
        if (strpos($ip, '/') !== false) {
            list($ip, $cidr) = explode('/', $ip);
            if (!filter_var($ip, FILTER_VALIDATE_IP)) {
                return false;
            }
            return is_numeric($cidr) && $cidr >= 0 && $cidr <= 32;
        }
        return filter_var($ip, FILTER_VALIDATE_IP) !== false;
    }

    private function loadWhitelist(): void
    {
        if (file_exists($this->whitelistFile)) {
            $content = file_get_contents($this->whitelistFile);
            $this->whitelist = json_decode($content, true) ?: [];
        }
    }

    private function saveWhitelist(): bool
    {
        return file_put_contents(
            $this->whitelistFile,
            json_encode($this->whitelist, JSON_PRETTY_PRINT)
        ) !== false;
    }

    public function generateFirewallRules(): string
    {
        $rules = "#!/bin/bash\n";
        $rules .= "# RabbitMQ IP 白名单规则\n";
        $rules .= "# 生成时间: " . date('Y-m-d H:i:s') . "\n\n";

        foreach ($this->whitelist as $entry) {
            $rules .= "# {$entry['description']}\n";
            $rules .= "iptables -A INPUT -s {$entry['ip']} -p tcp --dport 5672 -j ACCEPT\n";
            $rules .= "iptables -A INPUT -s {$entry['ip']} -p tcp --dport 15672 -j ACCEPT\n\n";
        }

        return $rules;
    }

    public function generateNginxConfig(): string
    {
        $config = "# RabbitMQ IP 白名单 Nginx 配置\n";
        $config .= "# 生成时间: " . date('Y-m-d H:i:s') . "\n\n";

        $config .= "geo \$allowed_ip {\n";
        $config .= "    default 0;\n";

        foreach ($this->whitelist as $entry) {
            $config .= "    {$entry['ip']} 1;  # {$entry['description']}\n";
        }

        $config .= "}\n";

        return $config;
    }
}

// 使用示例
$manager = new IPWhitelistManager('/etc/rabbitmq/whitelist.json');

// 添加 IP
$manager->addIP('10.0.0.0/24', '管理网络');
$manager->addIP('192.168.1.100', '管理员 IP');
$manager->addIP('10.0.1.0/24', '应用服务器');

// 检查 IP
var_dump($manager->isAllowed('10.0.0.50'));
var_dump($manager->isAllowed('8.8.8.8'));

// 生成防火墙规则
echo $manager->generateFirewallRules();

IP 访问控制器

php
<?php

class IPAccessController
{
    private $whitelistManager;
    private $blacklist = [];
    private $rateLimits = [];

    public function __construct(IPWhitelistManager $whitelistManager)
    {
        $this->whitelistManager = $whitelistManager;
    }

    public function checkAccess(string $ip): array
    {
        if ($this->isBlacklisted($ip)) {
            return [
                'allowed' => false,
                'reason' => 'IP 在黑名单中'
            ];
        }

        if ($this->whitelistManager->isAllowed($ip)) {
            return [
                'allowed' => true,
                'reason' => 'IP 在白名单中'
            ];
        }

        return [
            'allowed' => false,
            'reason' => 'IP 不在白名单中'
        ];
    }

    public function addToBlacklist(string $ip, string $reason = ''): void
    {
        $this->blacklist[$ip] = [
            'ip' => $ip,
            'reason' => $reason,
            'added_at' => date('c')
        ];
    }

    public function removeFromBlacklist(string $ip): void
    {
        unset($this->blacklist[$ip]);
    }

    private function isBlacklisted(string $ip): bool
    {
        return isset($this->blacklist[$ip]);
    }

    public function checkRateLimit(string $ip, int $maxRequests = 100, int $windowSeconds = 60): bool
    {
        $key = $ip;
        $now = time();

        if (!isset($this->rateLimits[$key])) {
            $this->rateLimits[$key] = [];
        }

        $this->rateLimits[$key] = array_filter(
            $this->rateLimits[$key],
            function ($timestamp) use ($now, $windowSeconds) {
                return $timestamp > ($now - $windowSeconds);
            }
        );

        if (count($this->rateLimits[$key]) >= $maxRequests) {
            return false;
        }

        $this->rateLimits[$key][] = $now;
        return true;
    }

    public function logAccess(string $ip, string $action, bool $allowed): void
    {
        $log = sprintf(
            "[%s] IP: %s, Action: %s, Allowed: %s\n",
            date('Y-m-d H:i:s'),
            $ip,
            $action,
            $allowed ? 'Yes' : 'No'
        );

        file_put_contents('/var/log/rabbitmq/access.log', $log, FILE_APPEND);
    }
}

// 使用示例
$whitelist = new IPWhitelistManager('/etc/rabbitmq/whitelist.json');
$controller = new IPAccessController($whitelist);

$clientIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';

$access = $controller->checkAccess($clientIP);
if (!$access['allowed']) {
    http_response_code(403);
    echo "Access Denied: {$access['reason']}";
    exit;
}

IP 白名单验证服务

php
<?php

class IPWhitelistValidator
{
    public function validateIP(string $ip): array
    {
        $errors = [];

        if (!$this->isValidFormat($ip)) {
            $errors[] = 'IP 格式无效';
        }

        if ($this->isPrivateIP($ip) && $this->isInProduction()) {
            $errors[] = '生产环境不应使用私有 IP 白名单';
        }

        if ($this->isTooBroad($ip)) {
            $errors[] = 'IP 范围过于宽泛,建议缩小范围';
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors
        ];
    }

    public function validateWhitelist(array $whitelist): array
    {
        $issues = [];

        foreach ($whitelist as $entry) {
            $validation = $this->validateIP($entry['ip']);
            if (!$validation['valid']) {
                $issues[] = [
                    'ip' => $entry['ip'],
                    'issues' => $validation['errors']
                ];
            }
        }

        if ($this->hasDuplicates($whitelist)) {
            $issues[] = [
                'ip' => 'multiple',
                'issues' => ['存在重复的 IP 条目']
            ];
        }

        return [
            'valid' => empty($issues),
            'issues' => $issues
        ];
    }

    private function isValidFormat(string $ip): bool
    {
        if (strpos($ip, '/') !== false) {
            list($ipAddr, $cidr) = explode('/', $ip);
            if (!filter_var($ipAddr, FILTER_VALIDATE_IP)) {
                return false;
            }
            return is_numeric($cidr) && $cidr >= 0 && $cidr <= 32;
        }
        return filter_var($ip, FILTER_VALIDATE_IP) !== false;
    }

    private function isPrivateIP(string $ip): bool
    {
        $ipAddr = strpos($ip, '/') !== false ? explode('/', $ip)[0] : $ip;
        return !filter_var(
            $ipAddr,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
        );
    }

    private function isTooBroad(string $ip): bool
    {
        if (strpos($ip, '/') !== false) {
            list(, $cidr) = explode('/', $ip);
            return (int)$cidr < 16;
        }
        return false;
    }

    private function isInProduction(): bool
    {
        return getenv('APP_ENV') === 'production';
    }

    private function hasDuplicates(array $whitelist): bool
    {
        $ips = array_column($whitelist, 'ip');
        return count($ips) !== count(array_unique($ips));
    }
}

动态 IP 白名单服务

php
<?php

class DynamicIPWhitelistService
{
    private $whitelistManager;
    private $cache;
    private $cacheTTL = 300;

    public function __construct(IPWhitelistManager $whitelistManager)
    {
        $this->whitelistManager = $whitelistManager;
    }

    public function addTemporaryIP(string $ip, int $durationSeconds, string $reason = ''): bool
    {
        $expiry = time() + $durationSeconds;

        return $this->whitelistManager->addIP($ip, "临时访问 - {$reason} (过期: " . date('Y-m-d H:i:s', $expiry) . ")");
    }

    public function cleanupExpiredIPs(): array
    {
        $removed = [];
        $whitelist = $this->whitelistManager->getAllowedIPs();
        $now = time();

        foreach ($whitelist as $entry) {
            if (preg_match('/过期: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/', $entry['description'], $matches)) {
                $expiry = strtotime($matches[1]);
                if ($expiry < $now) {
                    $this->whitelistManager->removeIP($entry['ip']);
                    $removed[] = $entry['ip'];
                }
            }
        }

        return $removed;
    }

    public function syncFromExternalSource(string $apiUrl): array
    {
        $ch = curl_init($apiUrl);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            return [
                'success' => false,
                'error' => '无法获取外部 IP 列表'
            ];
        }

        $ips = json_decode($response, true) ?: [];
        $synced = 0;

        foreach ($ips as $ipData) {
            if ($this->whitelistManager->addIP($ipData['ip'], $ipData['description'] ?? '')) {
                $synced++;
            }
        }

        return [
            'success' => true,
            'synced' => $synced
        ];
    }
}

实际应用场景

场景一:多环境 IP 白名单

php
<?php

class EnvironmentIPWhitelist
{
    private $environments = [
        'development' => [
            'management' => ['0.0.0.0/0'],
            'application' => ['0.0.0.0/0'],
            'cluster' => ['10.0.2.0/24']
        ],
        'staging' => [
            'management' => ['10.10.0.0/24', '192.168.1.0/24'],
            'application' => ['10.10.1.0/24'],
            'cluster' => ['10.10.2.0/24']
        ],
        'production' => [
            'management' => ['10.0.0.0/24'],
            'application' => ['10.0.1.0/24'],
            'cluster' => ['10.0.2.0/24']
        ]
    ];

    public function getWhitelistForEnvironment(string $env): array
    {
        return $this->environments[$env] ?? [];
    }

    public function generateConfig(string $env): string
    {
        $whitelist = $this->getWhitelistForEnvironment($env);

        $config = "# {$env} 环境 IP 白名单\n\n";

        foreach ($whitelist as $type => $ips) {
            $config .= "# {$type} 白名单\n";
            foreach ($ips as $ip) {
                $config .= "allow {$ip};\n";
            }
            $config .= "\n";
        }

        return $config;
    }
}

场景二:API 网关 IP 白名单

php
<?php

class APIGatewayIPFilter
{
    private $whitelistManager;
    private $pathRules = [];

    public function addPathRule(string $path, array $allowedIPs): void
    {
        $this->pathRules[$path] = $allowedIPs;
    }

    public function filterRequest(string $ip, string $path): bool
    {
        foreach ($this->pathRules as $rulePath => $allowedIPs) {
            if (strpos($path, $rulePath) === 0) {
                foreach ($allowedIPs as $allowedIP) {
                    if ($this->ipInRange($ip, $allowedIP)) {
                        return true;
                    }
                }
                return false;
            }
        }

        return $this->whitelistManager->isAllowed($ip);
    }

    private function ipInRange(string $ip, string $range): bool
    {
        if (strpos($range, '/') === false) {
            return $ip === $range;
        }

        list($subnet, $bits) = explode('/', $range);
        $ipLong = ip2long($ip);
        $subnetLong = ip2long($subnet);
        $mask = -1 << (32 - $bits);
        $subnetLong &= $mask;

        return ($ipLong & $mask) === $subnetLong;
    }
}

常见问题与解决方案

问题 1:白名单不生效

解决方案

bash
# 检查防火墙规则顺序
iptables -L -n --line-numbers

# 确保白名单规则在拒绝规则之前
iptables -I INPUT 1 -s 10.0.0.0/24 -p tcp --dport 15672 -j ACCEPT

# 检查 Nginx 配置
nginx -t
systemctl reload nginx

问题 2:动态 IP 无法添加

解决方案

php
<?php
// 确保 IP 格式正确
$ip = trim($ip);
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
    throw new InvalidArgumentException("无效的 IP 地址: {$ip}");
}

// 检查文件权限
if (!is_writable($whitelistFile)) {
    chmod($whitelistFile, 0644);
}

问题 3:CIDR 计算错误

解决方案

php
<?php
// 使用正确的 CIDR 计算
function ipToRange(string $cidr): array
{
    list($ip, $prefix) = explode('/', $cidr);
    $ipLong = ip2long($ip);
    $mask = -1 << (32 - (int)$prefix);
    
    return [
        'start' => long2ip($ipLong & $mask),
        'end' => long2ip($ipLong | (~$mask & 0xFFFFFFFF))
    ];
}

最佳实践建议

1. IP 白名单审计

php
<?php

class IPWhitelistAuditor
{
    public function audit(array $whitelist): array
    {
        $issues = [];

        foreach ($whitelist as $entry) {
            if ($this->isTooBroad($entry['ip'])) {
                $issues[] = [
                    'severity' => 'medium',
                    'ip' => $entry['ip'],
                    'issue' => 'IP 范围过于宽泛'
                ];
            }

            if ($this->isPublicIP($entry['ip'])) {
                $issues[] = [
                    'severity' => 'high',
                    'ip' => $entry['ip'],
                    'issue' => '包含公网 IP,请确认是否必要'
                ];
            }
        }

        return $issues;
    }

    private function isTooBroad(string $ip): bool
    {
        if (strpos($ip, '/') !== false) {
            list(, $cidr) = explode('/', $ip);
            return (int)$cidr < 16;
        }
        return false;
    }

    private function isPublicIP(string $ip): bool
    {
        $ipAddr = strpos($ip, '/') !== false ? explode('/', $ip)[0] : $ip;
        return filter_var(
            $ipAddr,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
        ) !== false;
    }
}

安全注意事项

重要警告

  1. 最小范围原则:白名单 IP 范围应尽可能小
  2. 定期审计:定期检查白名单是否需要更新
  3. 日志记录:记录所有白名单变更和访问尝试
  4. 多层防护:IP 白名单应与认证、加密配合使用
  5. 备份配置:定期备份白名单配置

相关链接