fuck scammers, abusers and bad actors!
Some checks failed
Ansible Lint Check / check-ansible (push) Failing after 42s
Nix Format Check / check-format (push) Failing after 37s
Python Lint Check / check-python (push) Has been cancelled

This commit is contained in:
2025-06-15 01:33:04 +00:00
parent 3774ea6233
commit 247aa2d733
14 changed files with 275 additions and 852 deletions

View File

@@ -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 }}
}

View 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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -7,4 +7,7 @@
loop: "{{ services }}"
when: item.enabled|bool
loop_control:
label: "{{ item.name }}"
label: "{{ item.name }}"
tags:
- "{{ item.name }}"
- services