Automated country blocklist aggregation and UniFi CyberSecure Region Blocking configuration.
This toolkit provides three main commands:
- discover - Probe UniFi API to find the Region Blocking endpoint
- aggregate - Collect and normalize country blocklists from authoritative sources
- configure - Apply the aggregated blocklist to UniFi with idempotent verification
go install github.com/mattsblocklist/cmd/discover@latest
go install github.com/mattsblocklist/cmd/aggregate@latest
go install github.com/mattsblocklist/cmd/configure@latestOr build from source:
git clone https://github.com/x86txt/mattsblocklist.git
cd mattsblocklist
go build -o bin/discover ./cmd/discover
go build -o bin/aggregate ./cmd/aggregate
go build -o bin/configure ./cmd/configure# Run the aggregator to collect data from all sources
./bin/aggregate --verbose
# Output files:
# data/blocked_countries.txt - Simple list (one code per line)
# data/blocked_countries.json - Full data with provenanceTip
You only need to run this if the apply to Unifi stage below fails to find your API endpoints, we have pre-populated the most common endpoints into the code as of the date of this release.
# Set credentials via environment
export UNIFI_HOST="https://ip.of.your.unifi.controller"
export UNIFI_USERNAME="local.user.you.created"
export UNIFI_PASSWORD="password.for.local.user"
# Discover endpoints (focus on region blocking)
./bin/discover --insecure --region-only --output discovered.json# Dry run first
./bin/configure --insecure --dry-run
# Apply changes
./bin/configure --insecure --verboseAll sources are scraped and normalized to ISO 3166-1 alpha-2 country codes.
| Source | Description | URL |
|---|---|---|
| Freedom House | Freedom on the Net report - countries rated "Not Free" | https://freedomhouse.org/countries/freedom-net/scores |
| OONI | Open Observatory of Network Interference - censorship data | https://ooni.org/countries/ |
| RSF | Reporters Without Borders Press Freedom Index | https://rsf.org/en/index |
| Source | Description | URL |
|---|---|---|
| EU Sanctions | European Union restrictive measures | https://www.sanctionsmap.eu/ |
| FATF Grey List | Financial Action Task Force high-risk jurisdictions | https://www.fatf-gafi.org/en/countries/black-and-grey-lists.html |
| UK Sanctions | UK financial sanctions consolidated list | https://www.gov.uk/government/collections/financial-sanctions-regime-specific-consolidated-lists-and-releases |
| UN Sanctions | United Nations Security Council sanctions | https://www.un.org/securitycouncil/sanctions/information |
| US OFAC | US Treasury Office of Foreign Assets Control | https://home.treasury.gov/policy-issues/financial-sanctions/sanctions-programs-and-country-information |
| Source | Description | URL |
|---|---|---|
| Blockpass Reference | Consolidated sanctions reference | https://help.blockpass.org/hc/en-us/articles/11881237145241-Which-countries-should-I-block-Sanctions-list-countries |
./bin/discover [options]
Options:
-host string UniFi controller URL (or UNIFI_HOST env)
-username string UniFi username (or UNIFI_USERNAME env)
-password string UniFi password (or UNIFI_PASSWORD env)
-site string UniFi site name (default "default")
-insecure Skip TLS certificate verification
-output string Output file path (JSON format)
-verbose Enable verbose output
-workers int Number of concurrent workers (default 5)
-region-only Only test region blocking candidate endpoints./bin/aggregate [options]
Options:
-output-txt string Output text file (default "data/blocked_countries.txt")
-output-json string Output JSON file (default "data/blocked_countries.json")
-sources string Comma-separated list of sources (empty = all)
-verbose Enable verbose output
-timeout duration HTTP request timeout (default 60s)
-workers int Number of concurrent workers (default 4)./bin/configure [options]
Options:
-host string UniFi controller URL (or UNIFI_HOST env)
-username string UniFi username (or UNIFI_USERNAME env)
-password string UniFi password (or UNIFI_PASSWORD env)
-site string UniFi site name (default "default")
-insecure Skip TLS certificate verification
-input string Input file with country codes (default "data/blocked_countries.txt")
-input-url string URL to fetch country codes from (overrides -input)
-dry-run Show what would change without applying
-verbose Enable verbose output
-output string Write result to JSON file
-endpoint string Override the region blocking endpoint path
-enable Enable region blocking (default true)export UNIFI_HOST="https://ip.of.your.unifi.controller"
export UNIFI_USERNAME="local.user.you.created"
export UNIFI_PASSWORD="password.for.local.user"
export UNIFI_SITE="default"
export UNIFI_SKIP_TLS_VERIFY="true"
export GITHUB_TOKEN="ghp_..." # For GitHub integrationCopy config.yaml.example to config.yaml:
unifi:
host: "https://ip.of.your.unifi.controller"
username: "local.user.you.created"
password: "${UNIFI_PASSWORD}"
site: "default"
skip_tls_verify: true
github:
repo: "x86txt/mattsblocklist"
token: "${GITHUB_TOKEN}"You can set up automated updates using cron to periodically refresh the blocklist and apply it to your UniFi controller.
- Create a wrapper script (
update-blocklist.sh):
#!/bin/bash
# Update UniFi region blocking blocklist
# Change to the directory containing the tools
cd /path/to/mattsblocklist
# Set environment variables (or source from a file)
export UNIFI_HOST="https://ip.of.your.unifi.controller"
export UNIFI_USERNAME="local.user.you.created"
export UNIFI_PASSWORD="password.for.local.user"
# Aggregate latest blocklist
./bin/aggregate --verbose 2>&1 | tee /var/log/unifi-blocklist-update.log
# Apply to UniFi (only updates if changes detected)
./bin/configure --insecure --verbose 2>&1 | tee -a /var/log/unifi-blocklist-update.log- Make the script executable:
chmod +x update-blocklist.sh- Add to crontab:
# Edit crontab
crontab -e
# Add entry to run daily at 3 AM
0 3 * * * /path/to/mattsblocklist/update-blocklist.sh
# Or run twice daily (3 AM and 3 PM)
0 3,15 * * * /path/to/mattsblocklist/update-blocklist.sh
# Or run weekly (every Monday at 3 AM)
0 3 * * 1 /path/to/mattsblocklist/update-blocklist.shFor production use, create a more robust script:
#!/bin/bash
# update-blocklist.sh - Automated UniFi blocklist updater with error handling
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_FILE="/var/log/unifi-blocklist-update.log"
LOCK_FILE="/tmp/unifi-blocklist-update.lock"
# Prevent concurrent runs
if [ -f "$LOCK_FILE" ]; then
echo "$(date): Update already in progress, skipping..." >> "$LOG_FILE"
exit 0
fi
trap "rm -f $LOCK_FILE" EXIT
touch "$LOCK_FILE"
cd "$SCRIPT_DIR"
# Source environment variables from a secure file
if [ -f "$SCRIPT_DIR/.env" ]; then
source "$SCRIPT_DIR/.env"
fi
echo "$(date): Starting blocklist update" >> "$LOG_FILE"
# Aggregate latest blocklist
if ./bin/aggregate --verbose >> "$LOG_FILE" 2>&1; then
echo "$(date): Aggregation successful" >> "$LOG_FILE"
else
echo "$(date): ERROR: Aggregation failed" >> "$LOG_FILE"
exit 1
fi
# Apply to UniFi
if ./bin/configure --insecure --verbose >> "$LOG_FILE" 2>&1; then
echo "$(date): Configuration update successful" >> "$LOG_FILE"
else
echo "$(date): ERROR: Configuration update failed" >> "$LOG_FILE"
exit 1
fi
echo "$(date): Update completed successfully" >> "$LOG_FILE"Store credentials in a .env file (add to .gitignore):
# .env file (chmod 600)
UNIFI_HOST="https://ip.of.your.unifi.controller"
UNIFI_USERNAME="local.user.you.created"
UNIFI_PASSWORD="password.for.local.user"chmod 600 .envAdd log rotation to prevent logs from growing too large:
# /etc/logrotate.d/unifi-blocklist
/var/log/unifi-blocklist-update.log {
daily
rotate 30
compress
delaycompress
notifempty
missingok
}Test the cron job manually first:
# Test the script
./update-blocklist.sh
# Check logs
tail -f /var/log/unifi-blocklist-update.log
# Test cron syntax (runs the job in 1 minute)
# Add this to crontab temporarily:
* * * * * /path/to/mattsblocklist/update-blocklist.sh- Daily: Good balance between freshness and resource usage
- Twice daily: If you need more frequent updates
- Weekly: If updates are infrequent and you want to minimize API calls
Note: The configure command is idempotent - it only makes changes when the blocklist actually differs from what's configured in UniFi, so running it frequently is safe.
Simple text format, one ISO alpha-2 code per line:
AF
BY
CN
CU
IR
...
Full data with source provenance:
{
"timestamp": "2024-12-26T12:00:00Z",
"total_codes": 42,
"countries": [
{
"alpha2": "AF",
"name": "Afghanistan",
"sources": ["FATF Grey List", "Freedom House"],
"raw_tokens": ["Afghanistan"]
}
],
"source_stats": {
"EU Sanctions List": {
"url": "https://www.sanctionsmap.eu/api/v1/sanctions",
"fetched_at": "2024-12-26T12:00:00Z",
"parse_status": "success",
"raw_count": 19,
"matched_count": 19
}
}
}- Never commit credentials - Use environment variables or gitignored config files
- Use a dedicated local admin account - Avoids MFA issues with UI.com accounts
- TLS verification - Only use
--insecureflag with self-signed certificates you control - Rate limiting - The tools include reasonable delays to avoid overloading APIs
The Region Blocking feature in UniFi is located at: Settings → CyberSecure → Region Blocking
For reliable endpoint discovery, use browser DevTools:
- Log into UniFi Network UI
- Open DevTools → Network tab
- Navigate to Region Blocking
- Toggle settings and observe API calls
- Note the endpoint path and request format
Common endpoints to check:
v2/api/site/{site}/trafficrulesapi/s/{site}/rest/setting
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Run tests:
go test ./... - Submit a pull request
- UniFi Community Wiki for API documentation
- Freedom House, RSF, OONI for open data on internet freedom
- Various government agencies for public sanctions data