
Q-Feeds를 NFtables 또는 IPtables에 직접 구현하는 스크립트
tip.qfeeds.com에서 무료 API 키를 받으세요.
git clone https://github.com/Q-Feeds/NFtables-IPtables-integration-script.git cd NFtables-IPtables-integration-script chmod +x qfeeds-installer.sh qfeeds-uninstaller.sh
### 단계 3: 설치 프로그램을 root로 실행하십시오```bash
sudo ./qfeeds-installer.sh
설치 프로그램이 수행하는 작업:
이제 서버가 보호됩니다. 크론 작업은 20분마다(구성 가능) 업데이트를 확인하며, 실제 API 호출은 라이선스가 허용할 때만 발생합니다.
이 솔루션은 Q-Feeds의 최신 위협 인텔리전스 피드를 주기적으로 다운로드하여 방화벽 규칙으로 적용함으로써 다음을 가능하게 합니다:
설치 프로그램이 사용 가능한 방화벽 백엔드를 자동으로 감지합니다:
| 우선순위 | 감지 조건 | 백엔드 |
|---|---|---|
감지된 백엔드는 구성 파일에 저장됩니다. 업데이터 및 제거 스크립트는 이를 사용하여 올바른 방화벽 명령어를 실행합니다.
두 백엔드 모두 최대 성능을 위해 동일한 분할 집합 전략을 사용합니다:
nftables 백엔드:``` ┌─────────────────────────────────────────────────────────┐ │ table ip qfeeds │ │ │ │ ┌─────────────────────────┐ ┌───────────────────────┐ │ │ │ qfeeds_blacklist_v4 │ │ qfeeds_blacklist_v4 │ │ │ │ (hash set) │ │ _nets (interval set) │ │ │ │ │ │ │ │ │ │ Individual IPs │ │ CIDR ranges │ │ │ │ ~99% of entries │ │ ~1% of entries │ │ │ │ O(1) lookup & insert │ │ O(log n) lookup │ │ │ └─────────────────────────┘ └───────────────────────┘ │ │ │ │ ┌─────────────────────────┐ │ │ │ qfeeds_whitelist_v4 │ │ │ │ (interval set) │ │ │ │ Your allowed IPs/CIDRs │ │ │ └─────────────────────────┘ │ │ │ │ chain input-chain (hook input, priority 0, accept) │ │ → ip saddr @qfeeds_whitelist_v4 accept │ │ → ip saddr @qfeeds_blacklist_v4 drop │ │ → ip saddr @qfeeds_blacklist_v4_nets drop │ │ │ │ chain output-chain (if enabled) │ │ → ip daddr @qfeeds_whitelist_v4 accept │ │ → ip daddr @qfeeds_blacklist_v4 drop │ │ → ip daddr @qfeeds_blacklist_v4_nets drop │ └─────────────────────────────────────────────────────────┘
**iptables+ipset 백엔드:**```
┌──────────────────────────────────────────────────────────┐
│ ipset sets │
│ │
│ ┌─────────────────────────┐ ┌────────────────────────┐ │
│ │ qfeeds_blacklist_v4 │ │ qfeeds_blacklist_v4 │ │
│ │ (hash:ip) │ │ _nets (hash:net) │ │
│ │ maxelem 1000000 │ │ maxelem 65536 │ │
│ │ │ │ │ │
│ │ Individual IPs │ │ CIDR ranges │ │
│ └─────────────────────────┘ └────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ qfeeds_whitelist_v4 │ │
│ │ (hash:net) │ │
│ └─────────────────────────┘ │
│ │
│ iptables: INPUT/OUTPUT jump to a dedicated chain │
│ (jump rule tagged -m comment "qfeeds"): │
│ │
│ chain QFEEDS_INPUT (rebuilt each run, in order): │
│ -m set --match-set whitelist_v4 src -j ACCEPT │
│ -m set --match-set blacklist_v4 src -j DROP │
│ -m set --match-set blacklist_v4_nets src -j DROP │
│ (QFEEDS_OUTPUT mirrors this with dst, if enabled) │
└──────────────────────────────────────────────────────────┘
동일한 구조가 IPv6(ip6 qfeeds 테이블 또는 ip6tables + family inet6 ipsets)에도 존재합니다.
두 가지 세트 유형을 사용하는 이유는?
┌──────────────────────────────────────────────────────┐ │ 1. Check license schedule (licenses.php API) │ │ → Skip run if not yet time for next update │ │ 2. Determine sync mode (full or diff) │ │ 3. Fetch IPv4 feed (ipv6=0) and IPv6 feed │ │ (ipv6=only) separately │ │ 4. Separate IPs from CIDRs in awk │ │ 5. Batch-load into hash set (IPs) and net/interval │ │ set (CIDRs) │ │ 6. Update whitelist sets from config │ │ 7. Persist rules │ └──────────────────────────────────────────────────────┘
### Full Sync vs Diff Sync
| Mode | When | What it does |
|------|------|-------------|
| **Full sync** | First run, forced update, after a diff failure, when the local set has lost its baseline (empty or much smaller than expected), or when the last sync is older than `FULL_SYNC_MAX_AGE` (default 24h) | Fetches and validates each feed first, then flushes and reloads the blacklist sets. The set is only flushed once valid data is in hand, so a failed fetch never leaves you unprotected |
| **Diff sync** | Subsequent runs (`malware_ip` feed only) with a healthy local set | Fetches only additions (`+`) and removals (`-`) since last pull |
The diff sync is **per API key** — the API tracks your last successful pull and only returns changes since then. If a diff fails, the script automatically falls back to a full sync.
> **Self-healing:** Diff updates only patch the existing set. If that set is ever lost or truncated — for example a reboot where the firewall rules were not persisted, a manual flush, or a previous partial sync — the updater detects the missing baseline (live element count is 0 or far below the last recorded count) and forces a full rebuild instead of diffing onto an empty set. As an extra safety net it also forces a periodic full sync (every 24h by default, via `FULL_SYNC_MAX_AGE`).
### License-Based Scheduling
The updater checks the Q-Feeds license API (`licenses.php`) before every run. If your license's `next_update` timestamp hasn't been reached yet, the script exits early without making unnecessary API calls. The cron job runs frequently (default: every 20 minutes), but actual updates only happen when your license allows.
---
## ✅ Prerequisites
Before installing, ensure you have:
- [x] **Linux server** with **nftables** or **iptables** (Debian, Ubuntu, CentOS, Fedora, Arch, Alpine)
- [x] **Root access** — the installer and updater must run as root
- [x] **Q-Feeds API Token** — get yours free at [tip.qfeeds.com](https://tip.qfeeds.com/)
- [x] **Internet access** — the server needs to reach `api.qfeeds.com`
The installer will automatically install required dependencies:
- **nftables backend**: `nftables`, `curl`, `jq`, `util-linux`
- **iptables backend**: `iptables`, `ipset`, `curl`, `jq`, `util-linux`
---
## 📝 Detailed Installation Guide
### 1. Get Your API Token
Visit [tip.qfeeds.com](https://tip.qfeeds.com/) to obtain your free Q-Feeds API token.
### 2. Download and Run```bash
git clone https://github.com/Q-Feeds/NFtables-IPtables-integration-script.git
cd NFtables-IPtables-integration-script
chmod +x qfeeds-installer.sh qfeeds-uninstaller.sh
sudo ./qfeeds-installer.sh
설치 프로그램은 다음 질문을 합니다:
Enter your Q-Feeds API Token:
[tip.qfeeds.com](https://tip.qfeeds.com/)에서 가져온 토큰입니다. 설치 프로그램은 비어 있으면 계속 진행되지 않습니다.
#### 피드 종류```
Enter feed type [default: malware_ip]:
기본값은 malware_ip입니다. Q-Feeds에서 다른 피드 유형을 제공한 경우에만 이 값을 변경하세요.
Enter the limit of IPs to fetch (leave empty for no limit):
#### 방향 차단
제한 없음(권장)을 선택하려면 Enter를 누르세요. 피드 크기를 제한할 숫자를 입력하세요.```
Block INCOMING connections from malicious IPs? [Y/n]:
Block OUTGOING connections to malicious IPs? [y/N]:
Configure a whitelist of IPs/CIDRs that must NEVER be blocked? [y/N]: Enter IPv4 whitelist (comma-separated, e.g. 1.2.3.4,5.6.7.8): Enter IPv6 whitelist (comma-separated, e.g. 2001:db8::1):
여기에 관리 IP를 추가하여, 피드에 나타나더라도 잠기지 않도록 하십시오. 화이트리스트 규칙은 블랙리스트 규칙 **보다 먼저** 항상 확인됩니다.
#### Cron 일정```
Enter cron schedule (e.g., '*/20 * * * *') [default: */20 * * * *]:
업데이터가 새 데이터를 확인하는 빈도입니다. 기본값은 20분마다입니다. 라이선스 기반 스케줄링은 라이선스가 업데이트를 허용할 때만 API가 호출되도록 보장합니다.
사용자 정의 crontab으로 설치 프로그램 다시 실행: Q-Feeds crontab 항목이 이미 존재하면 설치 프로그램이 건드리기 전에 물어봅니다:
An existing Q-Feeds cron entry was found in the current crontab. Replace it with a fresh default entry? Choosing 'no' keeps your crontab unchanged [y/N]:기존 crontab을 유지하려면
no(기본값)를 선택하세요. 깨끗한 첫 설치에는 기존 항목이 없으므로 이 프롬프트가 표시되지 않습니다. 무인 설치의 경우QFEEDS_SKIP_CRON=1을 설정하여 프롬프트 없이 crontab을 그대로 둡니다.
모든 설정은 /etc/qfeeds/qfeeds_config.conf에 저장됩니다. 설치 프로그램을 다시 실행하지 않고 이 파일을 직접 편집할 수 있습니다. 변경 사항은 다음 cron 실행 시 적용됩니다.
nft list table ip qfeeds
nft list set ip qfeeds qfeeds_blacklist_v4 | grep -oP '\d+.\d+.\d+.\d+' | wc -l
nft list set ip qfeeds qfeeds_blacklist_v4_nets | head -20
nft list set ip6 qfeeds qfeeds_blacklist_v6 | wc -l
### iptables+ipset 백엔드```bash
# List all Q-Feeds ipsets and their sizes
ipset list -t | grep -A4 qfeeds
# Count loaded IPv4 IPs
ipset list qfeeds_blacklist_v4 | tail -n +9 | wc -l
# Show loaded CIDR ranges
ipset list qfeeds_blacklist_v4_nets | tail -n +9 | head -20
# Show the qfeeds jump rule in INPUT, then the dedicated chain's block rules
iptables -L INPUT -n --line-numbers | grep qfeeds
iptables -L QFEEDS_INPUT -n
ip6tables -L QFEEDS_INPUT -n
tail -20 /var/log/qfeeds_blocklist.log
grep -i "error" /var/log/qfeeds_blocklist.log
sudo /usr/local/bin/update_qfeeds_blocklist.sh
sudo QFEEDS_FORCE_UPDATE=1 /usr/local/bin/update_qfeeds_blocklist.sh
sudo crontab -l | grep qfeeds
---
## 🔍 문제 해결
### 일반
**"Unable to locate package" 오류로 설치 실패**
- 설치 프로그램이 사용자 배포판(Debian/Ubuntu, CentOS/RHEL, Fedora, Arch, Alpine)을 자동 감지합니다. 감지에 실패하면 수동으로 의존성을 설치하세요: `curl`, `jq`, `util-linux` (for `flock`), 그리고 `nftables` 또는 `iptables`+`ipset`.
**설치 후 세트가 비어 있음**
- 로그 확인: `tail -50 /var/log/qfeeds_blocklist.log`
- API 토큰이 올바른지 확인
- 강제 업데이트 시도: `sudo QFEEDS_FORCE_UPDATE=1 /usr/local/bin/update_qfeeds_blocklist.sh`
**"아직 시간이 아닙니다. 다음 업데이트 예정 시간: ..."**
- 업데이트 프로그램은 사용자 라이선스 일정을 따릅니다. 이 메시지는 cron이 실행되었지만 라이선스에서 아직 업데이트를 허용하지 않음을 의미합니다. 정상입니다. 다음 cron 실행 시 다시 확인합니다.
- Linux 설치 프로그램은 로컬에 캐시된 `licenses.php` 인덱스를 유지하며, 캐시된 `next_update`를 일정 게이트로 사용합니다. 성공적으로 가져온 후 다음 주기를 위해 해당 로컬 인덱스를 새로고침합니다.
**재부팅 후 규칙이 유지되지 않음**
- `netfilter-persistent`가 설치된 경우 규칙이 자동으로 저장됩니다.
- **nftables**: `netfilter-persistent`가 없으면 업데이트 프로그램이 이제 전체 규칙 집합을 `/etc/nftables.conf`에 자동으로 작성하고 `nftables` 서비스를 활성화합니다. 수동으로 저장할 수도 있습니다: `nft list ruleset > /etc/nftables.conf`
- **iptables**: 업데이트 프로그램이 `ipset save > /etc/ipset.conf` 및 `iptables-save`로 저장합니다. 수동으로 저장할 수도 있습니다.
- 지속성이 완전히 실패하더라도 업데이트 프로그램이 자가 치유합니다. 다음 실행 시 재부팅 후 비어 있는 세트를 감지하고 전체 동기화로 다시 빌드합니다.
### nftables 관련
**"Batch nft -f 실패. 명령별 실행으로 대체..."**
- 특히 netlink 버퍼 제한(`wmem_max`)이 있는 LXC 컨테이너에서 정상입니다. 명령별 대체는 올바르게 작동하며 빠릅니다(40만 개 이상의 IP에 대해 약 10초).
**구문 오류: "예상치 못한 문자열"**
- 최신 버전의 nftables를 실행 중인지 확인하세요. 스크립트는 nftables 0.9+가 필요한 `ip saddr`/`ip daddr` 구문을 사용합니다.
**"오류: 규칙을 처리할 수 없음: 메시지가 너무 깁니다"**
- 일반적으로 LXC 컨테이너의 netlink 버퍼 제한입니다. 스크립트가 자동으로 명령별 실행으로 대체합니다. 로그에서 이 메시지와 함께 성공적인 로드가 표시되면 의도된 대로 작동 중인 것입니다.
### iptables+ipset 관련
**"ipset 복원 실패"**
- `ipset`이 설치되었는지 확인: `command -v ipset`
- 로그에서 구체적인 오류 확인: `grep -i "error" /var/log/qfeeds_blocklist.log`
- ipset 모듈이 로드되었는지 확인: `lsmod | grep ip_set`
**iptables 규칙이 표시되지 않음**
- 차단 규칙은 전용 `QFEEDS_INPUT` / `QFEEDS_OUTPUT` 체인에 있습니다. `INPUT`/`OUTPUT`에는 `qfeeds` 주석이 있는 `-j QFEEDS_INPUT` 점프만 있습니다.
- 확인: `iptables -L INPUT -n | grep qfeeds` (점프) 및 `iptables -L QFEEDS_INPUT -n` (차단 규칙)
- 점프 규칙은 식별을 위해 `-m comment --comment "qfeeds"`를 사용합니다.
- `xt_set` 모듈이 로드되었는지 확인: `modprobe xt_set`
**"ipset create ... 실패"**
- 매우 오래된 커널에서는 `hash:ip` 또는 `hash:net` 유형을 사용할 수 없을 수 있습니다. 커널을 업그레이드하거나 최신 저장소에서 `ipset`을 설치하세요.
---
## 🗑️ 제거```bash
sudo ./qfeeds-uninstaller.sh
The uninstaller removes everything based on the detected backend:
nftables backend:
ip qfeeds and ip6 qfeeds tables (including all chains, rules, and sets)iptables backend:
qfeeds comment (including the jump rules)QFEEDS_INPUT / QFEEDS_OUTPUT chainsqfeeds_blacklist_v4, qfeeds_blacklist_v4_nets, qfeeds_whitelist_v4, and IPv6 equivalents)Both backends:
/etc/qfeeds/)/usr/local/bin/update_qfeeds_blocklist.sh)If the config file is missing, the uninstaller tries cleanup for both backends.
Note: The uninstaller does not remove system packages (curl, jq, ipset, etc.) that were installed as dependencies.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Use at your own risk.
Please test these scripts in your environment before deploying them in production. The author is not responsible for any issues or damages that may occur from their use.
AI assistance: Parts of this project (code, fixes, and documentation) were written with the help of AI tools and subsequently reviewed by maintainers. While we test and review changes, please review the scripts yourself before running them and report anything that looks off.
| 1순위 |
nft 명령어 발견 |
| nftables |
| 2순위 | iptables 명령어 발견 | iptables+ipset |
| — | 둘 다 없음 | 오류(종료) |
| 변수 | 설명 | 기본값 |
|---|
BACKEND | 방화벽 백엔드 (nftables 또는 iptables) | (자동 감지) |
API_TOKEN | 귀하의 Q-Feeds API 토큰 | (필수) |
FEED_TYPE | 가져올 피드 유형 | malware_ip |
LIMIT | 가져올 최대 IP 수 (비워두면 제한 없음) | (비어 있음) |
BLOCK_INCOMING | 블랙리스트 IP의 들어오는 트래픽 차단 | yes |
BLOCK_OUTGOING | 블랙리스트 IP로 나가는 트래픽 차단 | no |
WHITELIST_V4 | 쉼표로 구분된 IPv4 화이트리스트 | (비어 있음) |
WHITELIST_V6 | 쉼표로 구분된 IPv6 화이트리스트 | (비어 있음) |
LOG_FILE | 로그 파일 경로 | /var/log/qfeeds_blocklist.log |
FULL_SYNC_MAX_AGE | 전체 재동기화가 강제되기 전 최대 시간(초) (심층 방어) | 86400 (24시간) |
| 경로 | 목적 |
|---|
/etc/qfeeds/qfeeds_config.conf | 설정 파일 |
/etc/qfeeds/.last_sync | 전체/차등 동기화 추적을 위한 상태 파일 |
/etc/qfeeds/.last_count | 마지막 성공적인 요소 개수, 기준 손실 감지에 사용 |
/usr/local/bin/update_qfeeds_blocklist.sh | 업데이터 스크립트 (cron을 통해 실행) |
/var/log/qfeeds_blocklist.log | 로그 파일 |