访问控制 #

概述 #

访问控制是保护网站安全的重要手段,Caddy 提供了多种访问控制机制,包括 IP 限制、Basic 认证、请求限制等。

text
┌─────────────────────────────────────────────────────────────┐
│                    访问控制层次                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│    1. 网络层 - IP 地址限制                                   │
│         ↓                                                   │
│    2. 传输层 - TLS 客户端证书                                │
│         ↓                                                   │
│    3. 应用层 - HTTP 认证                                     │
│         ↓                                                   │
│    4. 资源层 - 路径权限控制                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

IP 地址限制 #

允许特定 IP #

caddyfile
example.com {
    @allowed {
        remote_ip 192.168.1.0/24 10.0.0.0/8
    }
    
    handle @allowed {
        respond "Access granted"
    }
    
    respond "Access denied" 403
}

禁止特定 IP #

caddyfile
example.com {
    @blocked {
        remote_ip 192.168.100.0/24
    }
    
    respond @blocked "Access denied" 403
    
    respond "Welcome"
}

多 IP 规则 #

caddyfile
example.com {
    # 管理区域 - 只允许内网
    @admin {
        path /admin/*
        remote_ip 192.168.1.0/24 10.0.0.0/8
    }
    
    handle @admin {
        reverse_proxy localhost:8080
    }
    
    # 公共区域 - 允许所有
    handle {
        reverse_proxy localhost:3000
    }
}

IP 黑名单 #

caddyfile
example.com {
    # 定义黑名单
    @blacklist {
        remote_ip 1.2.3.4 5.6.7.8 9.10.11.0/24
    }
    
    # 拒绝黑名单 IP
    respond @blacklist "Forbidden" 403
    
    # 正常处理
    reverse_proxy localhost:3000
}

使用 CIDR 表示法 #

caddyfile
example.com {
    @internal {
        remote_ip 10.0.0.0/8      # 私有网络 A 类
                    172.16.0.0/12  # 私有网络 B 类
                    192.168.0.0/16 # 私有网络 C 类
                    127.0.0.1/32   # 本地回环
    }
    
    handle @internal {
        respond "Internal access"
    }
    
    respond "External access"
}

Basic 认证 #

基本配置 #

caddyfile
example.com {
    basicauth {
        admin $2a$14$Zkx...
    }
    
    respond "Protected content"
}

生成密码哈希 #

bash
# 使用 caddy 命令生成
caddy hash-password --plaintext 'your-password'

# 输出
$2a$14$Zkx...

# 或使用 htpasswd
htpasswd -nbB admin your-password

多用户认证 #

caddyfile
example.com {
    basicauth {
        admin $2a$14$Zkx...
        user1 $2a$14$Abc...
        user2 $2a$14$Def...
    }
    
    respond "Protected content"
}

路径级认证 #

caddyfile
example.com {
    root * /var/www/html
    file_server
    
    # 管理路径需要认证
    handle /admin/* {
        basicauth {
            admin $2a$14$Zkx...
        }
        file_server
    }
    
    # 公共路径不需要认证
    handle {
        file_server
    }
}

不同路径不同用户 #

caddyfile
example.com {
    # 用户区域
    handle /user/* {
        basicauth {
            user1 $2a$14$hash1...
            user2 $2a$14$hash2...
        }
        reverse_proxy localhost:3000
    }
    
    # 管理区域
    handle /admin/* {
        basicauth {
            admin $2a$14$adminhash...
        }
        reverse_proxy localhost:8080
    }
    
    # 公共区域
    handle {
        respond "Public content"
    }
}

JWT 认证 #

基本配置(需要插件) #

caddyfile
example.com {
    # 使用 JWT 插件
    jwt {
        primary rsa /etc/keys/public.pem
    }
    
    respond "Protected by JWT"
}

JWT 验证配置 #

caddyfile
api.example.com {
    jwt {
        # 公钥验证
        primary rsa /etc/keys/public.pem
        
        # 或使用密钥
        # primary hs256 your-secret-key
        
        # 验证声明
        issuer my-app
        audience api
    }
    
    reverse_proxy localhost:3000
}

路径级 JWT #

caddyfile
api.example.com {
    # 公共 API
    handle /public/* {
        reverse_proxy localhost:3000
    }
    
    # 私有 API
    handle /private/* {
        jwt {
            primary rsa /etc/keys/public.pem
        }
        reverse_proxy localhost:3000
    }
}

请求限制 #

请求频率限制 #

caddyfile
example.com {
    # 限制请求频率
    rate_limit {
        zone dynamic {
            key {remote_host}
            events 100
            window 1m
        }
    }
    
    reverse_proxy localhost:3000
}

连接限制 #

caddyfile
example.com {
    # 限制并发连接
    # 需要配置服务器选项
    servers {
        max_header_size 16KB
    }
    
    reverse_proxy localhost:3000
}

请求体大小限制 #

caddyfile
example.com {
    # 限制请求体大小
    request_body {
        max_size 10MB
    }
    
    reverse_proxy localhost:3000
}

地理位置限制 #

基于国家限制(需要插件) #

caddyfile
example.com {
    # 使用 GeoIP 插件
    geoip {
        allow_countries US CA GB
        deny_countries CN RU
    }
    
    respond "Geo-restricted content"
}

User-Agent 限制 #

禁止特定 User-Agent #

caddyfile
example.com {
    # 禁止爬虫
    @badbots {
        header User-Agent *bot* *crawler* *spider* *scraper*
    }
    respond @badbots "Forbidden" 403
    
    respond "Welcome"
}

只允许特定 User-Agent #

caddyfile
api.example.com {
    @allowed {
        header User-Agent *MyApp/*
    }
    
    handle @allowed {
        reverse_proxy localhost:3000
    }
    
    respond "Invalid client" 403
}
## Referer 限制

### 防盗链配置

```caddyfile
example.com {
    @images path *.jpg *.png *.gif
    
    # 允许的 Referer
    @allowedReferer {
        header Referer *example.com* *trusted-site.com*
    }
    
    # 图片请求检查 Referer
    handle @images {
        respond @allowedReferer {
            root * /var/www/html
            file_server
        }
        respond "Hotlinking not allowed" 403
    }
}

组合访问控制 #

多重验证 #

caddyfile
admin.example.com {
    # 1. IP 限制
    @allowed_ip remote_ip 192.168.1.0/24 10.0.0.0/8
    respond @allowed_ip "IP not allowed" 403
    
    # 2. Basic 认证
    basicauth {
        admin $2a$14$Zkx...
    }
    
    # 3. 后端服务
    reverse_proxy localhost:8080
}

分层访问控制 #

caddyfile
example.com {
    # 公共区域
    handle /public/* {
        respond "Public content"
    }
    
    # 用户区域 - 需要登录
    handle /user/* {
        basicauth {
            user $2a$14$hash...
        }
        reverse_proxy localhost:3000
    }
    
    # 管理区域 - IP + 认证
    handle /admin/* {
        @allowed remote_ip 192.168.1.0/24
        respond @allowed "IP not allowed" 403
        
        basicauth {
            admin $2a$14$adminhash...
        }
        reverse_proxy localhost:8080
    }
    
    # API 区域 - JWT
    handle /api/* {
        jwt {
            primary rsa /etc/keys/public.pem
        }
        reverse_proxy localhost:3001
    }
}

安全头部 #

基本安全头部 #

caddyfile
example.com {
    header {
        # 防止点击劫持
        X-Frame-Options "SAMEORIGIN"
        
        # 防止 MIME 类型嗅探
        X-Content-Type-Options "nosniff"
        
        # XSS 保护
        X-XSS-Protection "1; mode=block"
        
        # 引用策略
        Referrer-Policy "strict-origin-when-cross-origin"
        
        # 内容安全策略
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
        
        # 权限策略
        Permissions-Policy "geolocation=(), microphone=(), camera=()"
        
        # 移除服务器标识
        -Server
    }
    
    respond "Secure content"
}

HSTS 配置 #

caddyfile
example.com {
    header {
        # HTTP Strict Transport Security
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    }
    
    respond "HSTS enabled"
}

CORS 配置 #

caddyfile
api.example.com {
    @cors {
        method OPTIONS
        header Origin *
    }
    
    # CORS 预检
    handle @cors {
        header {
            Access-Control-Allow-Origin "{http.request.header.Origin}"
            Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
            Access-Control-Allow-Headers "Content-Type, Authorization"
            Access-Control-Max-Age "3600"
        }
        respond "" 204
    }
    
    # CORS 响应头
    header {
        Access-Control-Allow-Origin "*"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
        Access-Control-Allow-Headers "Content-Type, Authorization"
    }
    
    reverse_proxy localhost:3000
}

完整示例 #

企业内网应用 #

caddyfile
internal.example.com {
    # IP 白名单
    @allowed remote_ip 10.0.0.0/8 192.168.0.0/16
    respond @allowed "Access denied" 403
    
    # Basic 认证
    basicauth {
        admin $2a$14$Zkx...
    }
    
    # 安全头部
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        -Server
    }
    
    reverse_proxy localhost:8080
}

API 网关 #

caddyfile
api.example.com {
    # 公共 API
    handle /public/* {
        # 速率限制
        rate_limit {
            zone public {
                key {remote_host}
                events 100
                window 1m
            }
        }
        
        reverse_proxy localhost:3000
    }
    
    # 私有 API
    handle /private/* {
        # JWT 验证
        jwt {
            primary rsa /etc/keys/public.pem
        }
        
        # 速率限制
        rate_limit {
            zone private {
                key {remote_host}
                events 1000
                window 1m
            }
        }
        
        reverse_proxy localhost:3000
    }
    
    # 管理 API
    handle /admin/* {
        # IP 限制
        @allowed remote_ip 10.0.0.0/8
        respond @allowed "Access denied" 403
        
        # Basic 认证
        basicauth {
            admin $2a$14$adminhash...
        }
        
        reverse_proxy localhost:8080
    }
    
    # CORS
    header {
        Access-Control-Allow-Origin "*"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
        Access-Control-Allow-Headers "Content-Type, Authorization"
    }
}

多租户 SaaS #

caddyfile
app.example.com {
    # JWT 验证
    jwt {
        primary rsa /etc/keys/public.pem
    }
    
    # 添加租户信息到请求头
    reverse_proxy localhost:3000 {
        header_up X-Tenant-ID {jwt.tenant_id}
        header_up X-User-ID {jwt.sub}
    }
}

# 自定义域名
*.app.example.com {
    jwt {
        primary rsa /etc/keys/public.pem
    }
    
    reverse_proxy localhost:3000 {
        header_up X-Tenant-Domain {host}
    }
}

访问控制最佳实践 #

1. 最小权限原则 #

caddyfile
# 只开放必要的访问
handle /admin/* {
    @allowed remote_ip 192.168.1.100/32
    respond @allowed "Access denied" 403
    # ...
}

2. 多层防护 #

caddyfile
# IP + 认证 + 安全头部
@allowed remote_ip 10.0.0.0/8
respond @allowed "Access denied" 403

basicauth {
    admin $2a$14$hash...
}

header {
    X-Frame-Options "SAMEORIGIN"
    X-Content-Type-Options "nosniff"
}

3. 日志记录 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log
        format json
    }
    
    # 访问控制...
}

4. 定期更新密码 #

bash
# 定期生成新密码哈希
caddy hash-password --plaintext 'new-password'

下一步 #

现在你已经掌握了访问控制配置,接下来学习 日志管理 了解如何记录和分析访问日志!

最后更新:2026-03-28