fuck scammers, abusers and bad actors!
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
# Global configuration for country blocking
|
||||
{
|
||||
# Block specific countries (add ISO country codes as needed)
|
||||
# Examples: CN (China), RU (Russia), KP (North Korea), IR (Iran)
|
||||
servers {
|
||||
protocols h1 h2 h3
|
||||
}
|
||||
}
|
||||
|
||||
# Country blocking snippet - reusable across all sites
|
||||
{% if enable_country_blocking | default(false) and blocked_countries | default([]) | length > 0 %}
|
||||
# Country blocking snippet using MaxMind GeoLocation - reusable across all sites
|
||||
{% if enable_country_blocking | default(false) and allowed_countries_codes | default([]) | length > 0 %}
|
||||
(country_block) {
|
||||
@blocked_countries {
|
||||
remote_ip {{ blocked_countries | join(' ') }}
|
||||
@not_allowed_countries {
|
||||
not {
|
||||
maxmind_geolocation {
|
||||
db_path "/etc/caddy/geoip/GeoLite2-Country.mmdb"
|
||||
allow_countries {{ allowed_countries_codes | join(' ') }}
|
||||
}
|
||||
}
|
||||
}
|
||||
respond @blocked_countries "Access denied from your country" 403
|
||||
respond @not_allowed_countries "Access denied" 403
|
||||
}
|
||||
{% else %}
|
||||
(country_block) {
|
||||
@@ -116,7 +119,6 @@ ip.mvl.sh {
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
|
||||
tls {{ caddy_email }}
|
||||
}
|
||||
|
||||
|
15
config/ansible/tasks/servers/services/caddy/Dockerfile
Normal file
15
config/ansible/tasks/servers/services/caddy/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM caddy:2.9.1-builder AS builder
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/porech/caddy-maxmind-geolocation
|
||||
|
||||
FROM caddy:2.9.1-alpine
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
|
||||
# Create directory for MaxMind databases and logs
|
||||
RUN mkdir -p /etc/caddy/geoip /var/log/caddy
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
@@ -1,261 +0,0 @@
|
||||
# Caddy Country Blocking Configuration
|
||||
|
||||
This directory contains configuration for implementing country-based IP blocking in Caddy reverse proxy.
|
||||
|
||||
## Overview
|
||||
|
||||
The country blocking feature allows you to block traffic from specific countries by their IP address ranges. This is useful for:
|
||||
|
||||
- Reducing spam and malicious traffic
|
||||
- Complying with regional restrictions
|
||||
- Improving security posture
|
||||
- Reducing server load from unwanted traffic
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **IP Range Generation**: The `generate_country_blocks.py` script downloads current IP ranges for specified countries
|
||||
2. **Caddy Configuration**: The Caddyfile template includes a reusable snippet that blocks IPs from those ranges
|
||||
3. **Automatic Updates**: Ansible task generates fresh IP ranges on each deployment
|
||||
|
||||
## Configuration
|
||||
|
||||
### Enable/Disable Country Blocking
|
||||
|
||||
In `group_vars/servers.yml`:
|
||||
|
||||
```yaml
|
||||
# Enable or disable country blocking
|
||||
enable_country_blocking: true # Set to false to disable
|
||||
```
|
||||
|
||||
### Specify Countries to Block
|
||||
|
||||
Add country codes (ISO 3166-1 alpha-2) to the `blocked_countries_codes` list:
|
||||
|
||||
```yaml
|
||||
blocked_countries_codes:
|
||||
# User-specified high-risk countries
|
||||
- CN # China
|
||||
- RU # Russia
|
||||
- IN # India
|
||||
- KP # North Korea
|
||||
|
||||
# Top countries for malicious IP traffic and abuse
|
||||
- IR # Iran
|
||||
- VN # Vietnam
|
||||
- BR # Brazil
|
||||
- TR # Turkey
|
||||
- ID # Indonesia
|
||||
- TH # Thailand
|
||||
- BD # Bangladesh
|
||||
- PK # Pakistan
|
||||
- RO # Romania
|
||||
- BY # Belarus
|
||||
```
|
||||
|
||||
### Currently Blocked Countries
|
||||
|
||||
The default configuration blocks these countries based on high levels of malicious traffic:
|
||||
|
||||
| Country | Code | Reason |
|
||||
|---------|------|--------|
|
||||
| China | CN | High volume of attacks, state-sponsored threats |
|
||||
| Russia | RU | Cybercrime hub, state-sponsored threats |
|
||||
| India | IN | Large botnet presence, spam sources |
|
||||
| North Korea | KP | State-sponsored attacks |
|
||||
| Iran | IR | State-sponsored threats |
|
||||
| Vietnam | VN | High malware hosting, botnet activity |
|
||||
| Brazil | BR | Large botnet networks |
|
||||
| Turkey | TR | Hosting malicious infrastructure |
|
||||
| Indonesia | ID | Compromised hosts, botnet activity |
|
||||
| Thailand | TH | Hosting malicious services |
|
||||
| Bangladesh | BD | Compromised infrastructure |
|
||||
| Pakistan | PK | Botnet activity, compromised hosts |
|
||||
| Romania | RO | Cybercrime activity |
|
||||
| Belarus | BY | State-aligned threats |
|
||||
|
||||
### Additional Country Codes Reference
|
||||
|
||||
| Country | Code | | Country | Code |
|
||||
|---------|------|-|---------|------|
|
||||
| Syria | SY | | Myanmar | MM |
|
||||
| Afghanistan | AF | | Cuba | CU |
|
||||
| Venezuela | VE | | Ukraine | UA |
|
||||
| Philippines | PH | | Nigeria | NG |
|
||||
|
||||
## Files Structure
|
||||
|
||||
```
|
||||
caddy/
|
||||
├── Caddyfile.j2 # Main Caddy configuration with country blocking
|
||||
├── caddy.yml # Ansible task for Caddy deployment
|
||||
├── country-blocking.yml # Ansible task for country blocking setup
|
||||
├── docker-compose.yml.j2 # Docker Compose configuration
|
||||
├── generate_country_blocks.py # Script to generate IP ranges
|
||||
└── README-country-blocking.md # This documentation
|
||||
```
|
||||
|
||||
## Manual Usage
|
||||
|
||||
### Generate IP Ranges Manually
|
||||
|
||||
```bash
|
||||
# Generate ranges for specific countries
|
||||
python3 generate_country_blocks.py CN RU --format=list
|
||||
|
||||
# Output to file
|
||||
python3 generate_country_blocks.py CN RU KP --output=blocked_ranges.txt
|
||||
|
||||
# Use fallback/manual ranges if download fails
|
||||
python3 generate_country_blocks.py CN RU --fallback
|
||||
|
||||
# JSON format for inspection
|
||||
python3 generate_country_blocks.py CN RU --format=json
|
||||
```
|
||||
|
||||
### Test Country Blocking
|
||||
|
||||
After deployment, test blocking by checking access from different locations:
|
||||
|
||||
```bash
|
||||
# Test from a VPN/proxy in a blocked country
|
||||
curl -I https://your-domain.com
|
||||
|
||||
# Should return HTTP 403 with "Access denied from your country"
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
The country blocking is automatically configured when you run the Caddy Ansible task:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory.ini playbook.yml --tags caddy
|
||||
```
|
||||
|
||||
Or specifically for country blocking:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory.ini playbook.yml --tags country-blocking
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No IP Ranges Generated
|
||||
|
||||
If the script fails to download ranges:
|
||||
|
||||
1. Check internet connectivity on the Ansible control machine
|
||||
2. Verify the country codes are valid (2-letter ISO codes)
|
||||
3. Use fallback ranges: set `--fallback` flag in the script
|
||||
4. Check the script output for error messages
|
||||
|
||||
### Legitimate Traffic Blocked
|
||||
|
||||
If legitimate users are blocked:
|
||||
|
||||
1. Verify their country isn't in your blocked list
|
||||
2. Check if they're using a VPN/proxy from a blocked country
|
||||
3. Consider whitelisting specific IP ranges for known users
|
||||
4. Review your country blocking list for overly broad restrictions
|
||||
|
||||
### Service Not Starting
|
||||
|
||||
If Caddy fails to start after enabling country blocking:
|
||||
|
||||
1. Check the generated Caddyfile syntax: `caddy validate --config /path/to/Caddyfile`
|
||||
2. Verify the IP ranges are valid CIDR notation
|
||||
3. Check Docker logs: `docker compose logs caddy`
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Benefits
|
||||
|
||||
- **Reduced Attack Surface**: Blocks traffic from high-risk regions
|
||||
- **Lower Server Load**: Reduces processing of malicious requests (up to 70% reduction)
|
||||
- **Compliance**: Helps meet regional access restrictions
|
||||
- **Proactive Defense**: Stops attacks before they reach your applications
|
||||
- **Cost Savings**: Reduces bandwidth and compute costs from malicious traffic
|
||||
|
||||
### Limitations
|
||||
|
||||
- **VPN Bypass**: Users can circumvent blocking using VPNs/proxies
|
||||
- **Legitimate Users**: May block legitimate traffic from blocked countries
|
||||
- **Maintenance**: IP ranges change over time and need updates
|
||||
- **False Positives**: Geolocation isn't 100% accurate
|
||||
- **Business Impact**: May affect legitimate business relationships
|
||||
- **Overblocking**: Current configuration blocks ~40% of global IP space
|
||||
|
||||
### Risk Assessment
|
||||
|
||||
The current blocking list includes countries that generate disproportionate amounts of:
|
||||
- Botnet traffic (85% from blocked countries)
|
||||
- Brute force attacks (78% from blocked countries)
|
||||
- Malware hosting (72% from blocked countries)
|
||||
- Spam campaigns (81% from blocked countries)
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Whitelist Critical Services**: Don't block access to monitoring/health endpoints
|
||||
2. **Regular Updates**: Update IP ranges regularly (automated with Ansible)
|
||||
3. **Monitor Logs**: Watch for legitimate users being blocked
|
||||
4. **Gradual Implementation**: Current config is aggressive - monitor impact
|
||||
5. **Document Decisions**: Keep records of why specific countries are blocked
|
||||
6. **Business Review**: Ensure blocking doesn't conflict with business needs
|
||||
7. **Whitelist Partners**: Add specific IP ranges for known business partners
|
||||
8. **Regular Assessment**: Review blocked country list quarterly
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Block Message
|
||||
|
||||
Modify the response in `Caddyfile.j2`:
|
||||
|
||||
```caddy
|
||||
respond @blocked_countries "Custom block message" 403
|
||||
```
|
||||
|
||||
### Whitelist Specific IPs
|
||||
|
||||
Add to the country_block snippet:
|
||||
|
||||
```caddy
|
||||
(country_block) {
|
||||
@blocked_countries {
|
||||
remote_ip {{ blocked_countries | join(' ') }}
|
||||
not remote_ip 1.2.3.4 5.6.7.8/24 # Whitelist specific IPs
|
||||
}
|
||||
respond @blocked_countries "Access denied from your country" 403
|
||||
}
|
||||
```
|
||||
|
||||
### Log Blocked Requests
|
||||
|
||||
Add logging to track blocked requests:
|
||||
|
||||
```caddy
|
||||
(country_block) {
|
||||
@blocked_countries {
|
||||
remote_ip {{ blocked_countries | join(' ') }}
|
||||
}
|
||||
log @blocked_countries {
|
||||
output file /var/log/caddy/blocked_countries.log
|
||||
format json
|
||||
}
|
||||
respond @blocked_countries "Access denied from your country" 403
|
||||
}
|
||||
```
|
||||
|
||||
## Data Sources
|
||||
|
||||
The script uses multiple data sources for IP ranges:
|
||||
|
||||
1. **Primary**: ipinfo.io free API
|
||||
2. **Fallback**: Manually curated ranges for common countries
|
||||
3. **Alternative**: Can be extended to use other sources like MaxMind, IP2Location
|
||||
|
||||
## License and Legal
|
||||
|
||||
- Ensure compliance with local laws regarding geographic blocking
|
||||
- Some regions have regulations about access restrictions
|
||||
- Consider accessibility requirements for your services
|
||||
- Review terms of service for IP geolocation data providers
|
@@ -1,46 +1,58 @@
|
||||
---
|
||||
- name: Deploy Caddy service
|
||||
block:
|
||||
- name: Setup country blocking
|
||||
ansible.builtin.include_tasks: country-blocking.yml
|
||||
- name: Set Caddy directories
|
||||
ansible.builtin.set_fact:
|
||||
caddy_service_dir: "{{ ansible_env.HOME }}/services/caddy"
|
||||
caddy_data_dir: "/mnt/object_storage/services/caddy"
|
||||
caddy_email: "{{ lookup('community.general.onepassword', 'qwvcr4cuumhqh3mschv57xdqka', vault='j7nmhqlsjmp2r6umly5t75hzb4', field='email') }}"
|
||||
- name: Set Caddy directories
|
||||
ansible.builtin.set_fact:
|
||||
caddy_service_dir: "{{ ansible_env.HOME }}/services/caddy"
|
||||
caddy_data_dir: "/mnt/object_storage/services/caddy"
|
||||
caddy_email: "{{ lookup('community.general.onepassword', 'qwvcr4cuumhqh3mschv57xdqka', vault='j7nmhqlsjmp2r6umly5t75hzb4', field='email') }}"
|
||||
|
||||
- name: Create Caddy directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_service_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
- name: Setup country blocking
|
||||
ansible.builtin.include_tasks: country-blocking.yml
|
||||
|
||||
- name: Create Caddy network
|
||||
ansible.builtin.command: docker network create caddy_default
|
||||
register: create_caddy_network
|
||||
failed_when:
|
||||
- create_caddy_network.rc != 0
|
||||
- "'already exists' not in create_caddy_network.stderr"
|
||||
changed_when: create_caddy_network.rc == 0
|
||||
- name: Create Caddy directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_service_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Deploy Caddy docker-compose.yml
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ caddy_service_dir }}/docker-compose.yml"
|
||||
mode: "0644"
|
||||
register: caddy_compose
|
||||
- name: Copy Dockerfile for custom Caddy build
|
||||
ansible.builtin.copy:
|
||||
src: Dockerfile
|
||||
dest: "{{ caddy_service_dir }}/Dockerfile"
|
||||
mode: "0644"
|
||||
register: caddy_dockerfile
|
||||
|
||||
- name: Deploy Caddy Caddyfile
|
||||
ansible.builtin.template:
|
||||
src: Caddyfile.j2
|
||||
dest: "{{ caddy_service_dir }}/Caddyfile"
|
||||
mode: "0644"
|
||||
register: caddy_file
|
||||
- name: Create Caddy network
|
||||
ansible.builtin.command: docker network create caddy_default
|
||||
register: create_caddy_network
|
||||
failed_when:
|
||||
- create_caddy_network.rc != 0
|
||||
- "'already exists' not in create_caddy_network.stderr"
|
||||
changed_when: create_caddy_network.rc == 0
|
||||
|
||||
- name: Stop Caddy service
|
||||
ansible.builtin.command: docker compose -f "{{ caddy_service_dir }}/docker-compose.yml" down --remove-orphans
|
||||
when: caddy_compose.changed or caddy_file.changed
|
||||
- name: Deploy Caddy docker-compose.yml
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ caddy_service_dir }}/docker-compose.yml"
|
||||
mode: "0644"
|
||||
register: caddy_compose
|
||||
|
||||
- name: Start Caddy service
|
||||
ansible.builtin.command: docker compose -f "{{ caddy_service_dir }}/docker-compose.yml" up -d
|
||||
when: caddy_compose.changed or caddy_file.changed
|
||||
- name: Deploy Caddy Caddyfile
|
||||
ansible.builtin.template:
|
||||
src: Caddyfile.j2
|
||||
dest: "{{ caddy_service_dir }}/Caddyfile"
|
||||
mode: "0644"
|
||||
register: caddy_file
|
||||
|
||||
- name: Stop Caddy service
|
||||
ansible.builtin.command: docker compose -f "{{ caddy_service_dir }}/docker-compose.yml" down --remove-orphans
|
||||
when: caddy_compose.changed or caddy_file.changed
|
||||
|
||||
- name: Start Caddy service
|
||||
ansible.builtin.command: docker compose -f "{{ caddy_service_dir }}/docker-compose.yml" up -d
|
||||
when: caddy_compose.changed or caddy_file.changed
|
||||
tags:
|
||||
- caddy
|
||||
- services
|
||||
- reverse-proxy
|
||||
|
@@ -1,53 +1,50 @@
|
||||
---
|
||||
- name: Country blocking setup for Caddy
|
||||
- name: Country blocking setup for Caddy with MaxMind GeoLocation
|
||||
block:
|
||||
- name: Ensure Python requests module is installed
|
||||
ansible.builtin.apt:
|
||||
name: python3-requests
|
||||
state: present
|
||||
update_cache: yes
|
||||
when: enable_country_blocking | default(false)
|
||||
- name: Copy Dockerfile for custom Caddy build with GeoIP
|
||||
ansible.builtin.copy:
|
||||
src: Dockerfile
|
||||
dest: "{{ caddy_service_dir }}/Dockerfile"
|
||||
mode: "0644"
|
||||
when: enable_country_blocking | default(false)
|
||||
|
||||
- name: Copy country blocking script
|
||||
ansible.builtin.copy:
|
||||
src: generate_country_blocks.py
|
||||
dest: "{{ caddy_service_dir }}/generate_country_blocks.py"
|
||||
mode: "0755"
|
||||
when: enable_country_blocking | default(false)
|
||||
- name: Check if MaxMind Country database is available
|
||||
ansible.builtin.stat:
|
||||
path: "/mnt/object_storage/services/echoip/GeoLite2-Country.mmdb"
|
||||
register: maxmind_country_db
|
||||
when: enable_country_blocking | default(false)
|
||||
|
||||
- name: Generate country IP ranges
|
||||
ansible.builtin.command:
|
||||
cmd: "python3 {{ caddy_service_dir }}/generate_country_blocks.py {{ blocked_countries_codes | join(' ') }} --format=list"
|
||||
register: country_ranges_result
|
||||
when:
|
||||
- enable_country_blocking | default(false)
|
||||
- blocked_countries_codes | default([]) | length > 0
|
||||
changed_when: false
|
||||
- name: Ensure log directory exists for Caddy
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_data_dir }}/logs"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
become: true
|
||||
when: enable_country_blocking | default(false)
|
||||
|
||||
- name: Set country IP ranges fact
|
||||
ansible.builtin.set_fact:
|
||||
blocked_countries: "{{ country_ranges_result.stdout.split('\n') | select('match', '^[0-9]') | list }}"
|
||||
when:
|
||||
- enable_country_blocking | default(false)
|
||||
- country_ranges_result is defined
|
||||
- country_ranges_result.stdout is defined
|
||||
- name: Display country blocking configuration
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "✅ Country blocking enabled: {{ enable_country_blocking | default(false) }}"
|
||||
- "🛡️ Countries to allow: {{ allowed_countries_codes | default([]) | join(', ') }}"
|
||||
- "📍 Using MaxMind GeoLocation plugin"
|
||||
- "💾 Database path: /etc/caddy/geoip/GeoLite2-Country.mmdb"
|
||||
- "📊 Database available: {{ maxmind_country_db.stat.exists | default(false) }}"
|
||||
when: enable_country_blocking | default(false)
|
||||
|
||||
- name: Display blocked countries info
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Country blocking enabled: {{ enable_country_blocking | default(false) }}"
|
||||
- "Countries to block: {{ blocked_countries_codes | default([]) | join(', ') }}"
|
||||
- "IP ranges found: {{ blocked_countries | default([]) | length }}"
|
||||
when: enable_country_blocking | default(false)
|
||||
|
||||
- name: Fallback to empty list if no ranges generated
|
||||
ansible.builtin.set_fact:
|
||||
blocked_countries: []
|
||||
when:
|
||||
- enable_country_blocking | default(false)
|
||||
- blocked_countries is not defined
|
||||
- name: Warn if MaxMind database not found
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "⚠️ WARNING: MaxMind Country database not found!"
|
||||
- "Expected location: /mnt/object_storage/services/echoip/GeoLite2-Country.mmdb"
|
||||
- "Country blocking will not work until EchoIP service is deployed"
|
||||
- "Run: dotf update --ansible --tags echoip"
|
||||
when:
|
||||
- enable_country_blocking | default(false)
|
||||
- not maxmind_country_db.stat.exists | default(false)
|
||||
|
||||
tags:
|
||||
- caddy
|
||||
- security
|
||||
- country-blocking
|
||||
- caddy
|
||||
- security
|
||||
- country-blocking
|
||||
- geoip
|
||||
|
@@ -1,6 +1,8 @@
|
||||
services:
|
||||
caddy:
|
||||
image: caddy:2.9.1-alpine
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
@@ -9,6 +11,8 @@ services:
|
||||
- {{ caddy_data_dir }}/data:/data
|
||||
- {{ caddy_data_dir }}/config:/config
|
||||
- {{ caddy_service_dir }}/Caddyfile:/etc/caddy/Caddyfile
|
||||
- /mnt/object_storage/services/echoip:/etc/caddy/geoip:ro
|
||||
- {{ caddy_data_dir }}/logs:/var/log/caddy
|
||||
environment:
|
||||
- TZ=Europe/Amsterdam
|
||||
- PUID=1000
|
||||
|
@@ -7,4 +7,7 @@
|
||||
loop: "{{ services }}"
|
||||
when: item.enabled|bool
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
label: "{{ item.name }}"
|
||||
tags:
|
||||
- "{{ item.name }}"
|
||||
- services
|
||||
|
Reference in New Issue
Block a user