feat: Add Tor exit node extraction with separate TorExitNode model (#…#728
Conversation
| @shared_task() | ||
| def get_tor_exit_nodes(): | ||
| from greedybear.cronjobs.tor_exit_nodes import TorExitNodesCron | ||
|
|
||
| TorExitNodesCron().execute() |
There was a problem hiding this comment.
I think you forgot to schedule this task in celery.py.
tests/test_tor.py
Outdated
| from greedybear.cronjobs.tor_exit_nodes import TorExitNodesCron | ||
|
|
||
|
|
||
| class TestTorRepository(unittest.TestCase): |
There was a problem hiding this comment.
Please use CustomTestCase as a base class for consistency with other tests.
tests/test_tor.py
Outdated
| self.assertFalse(created) | ||
|
|
||
|
|
||
| class TestTorExitNodesCron(unittest.TestCase): |
Thanks for the review! Made both changes: - Switched to CustomTestCase for test consistency - Added weekly Celery Beat schedule (Sundays at 4:30 AM) Addresses feedback from @regulartim
|
Hey @regulartim! Thanks for catching those! Both fixed now:
CI is all green ✅. Let me know if you spot anything else! |
regulartim
left a comment
There was a problem hiding this comment.
Looks good so far, aside from the lower-case bug. However, there are still things missing for this to work:
- a migration file
- a registration of the new model in
admin.py
|
|
||
| def _update_old_ioc(self, ip_address: str): | ||
| """Update the IP reputation of an existing IOC to mark it as a Tor exit node.""" | ||
| updated = self.ioc_repo.update_ioc_reputation(ip_address, "Tor Exit Node") |
There was a problem hiding this comment.
Here, "Tor Exit Node" has to be lower case, otherwise the filtering at the API-level won't work, right?
- Added migration file for TorExitNode model (0032) - Fixed case sensitivity issue - changed 'Tor Exit Node' to lowercase for API filtering - Registered TorExitNode in admin panel for easy management
9f58dc6 to
d8ac8f5
Compare
|
Hey! Thanks for the review - really appreciate the feedback. I've gone through and addressed all three points: Migration file - Added 0032_torexitnode.py with proper dependencies. Case sensitivity - Changed all the "Tor Exit Node" strings to lowercase "tor exit node" across the codebase (models, repository, cron job, tests, migration). This should fix the API filtering issue you mentioned. Admin registration - TorExitNode is now registered in the admin panel with search by IP address. All the checks are passing now! Let me know if anything needs tweaking or if you have other suggestions. |
Description
This PR adds automatic extraction and filtering of Tor exit node IPs to reduce false positives in threat feeds. Tor exit nodes are privacy servers for anonymous browsing, not attackers - but they show up in honeypot logs and get incorrectly flagged as malicious.
Following the same approach as IntelOwl, this fetches the official Tor exit node list from torproject.org and filters them out, just like we do with mass scanners.
What I Built
Created TorExitNode model
ip_address(unique),added,reasonAdded cron job (greedybear/cronjobs/tor_exit_nodes.py)
https://check.torproject.org/exit-addressesis_valid_ipv4utilityip_reputationfield for existing IOCsBuilt repository layer (greedybear/cronjobs/repositories/tor.py)
MassScannerRepositoryandFireHolRepositoryHooked into Celery (get_tor_exit_nodes task in tasks.py)
Wrote comprehensive tests (tests/test_tor.py)
Notes
Currently handles IPv4 addresses only (what Tor Project publishes). Uses regex-based extraction for consistency with IntelOwl.
Files Changed
New:
Modified:
greedybear/cronjobs/repositories/__init__.py- Export TorRepositoryRelated issues
Closes #547
Type of change
Checklist
develop.Ruff) gave 0 errors. If you have correctly installed pre-commit, it does these checks and adjustments on your behalf.