Appearance
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 checkiptables 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 DROPfirewalld 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 --reloadPHP 代码示例
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;
}
}安全注意事项
重要警告
- 最小范围原则:白名单 IP 范围应尽可能小
- 定期审计:定期检查白名单是否需要更新
- 日志记录:记录所有白名单变更和访问尝试
- 多层防护:IP 白名单应与认证、加密配合使用
- 备份配置:定期备份白名单配置
