Django middleware for IP and hostname-based access control
Project description
Django IP Access Control Middleware
A Django middleware for IP and hostname-based access control with support for:
- IP addresses and CIDR ranges from database
- Hostname matching from environment variables
- Automatic same-network detection for Kubernetes
- Route-based access control with regex, exact, startswith, endswith patterns
Features
- Database-driven IP control: Store granted IP addresses and CIDR ranges in the database
- Environment-based hostnames: Configure allowed hostnames via environment variables
- Kubernetes support: Automatic same-network detection for pods in the same cluster
- Flexible route matching: Support for regex, exact match, startswith, and endswith patterns
- Priority-based access control:
- Same network detection (highest priority - allows immediately)
- Hostname matching (from environment variables)
- IP checking (from database)
Installation
pip install django-ip-access-middleware
Or install from source:
pip install -e .
Quick Start
1. Add to INSTALLED_APPS
Add django_ip_access to your INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
# ... other apps
'django_ip_access',
]
2. Add Middleware
Add the middleware to your MIDDLEWARE list in settings.py:
MIDDLEWARE = [
# ... other middleware
'django_ip_access.middleware.IPAccessMiddleware',
# ... other middleware
]
3. Run Migrations
Create and run migrations for the database model:
python manage.py makemigrations django_ip_access
python manage.py migrate django_ip_access
4. Configure Routes
Configure which routes should be protected in settings.py:
IP_ACCESS_MIDDLEWARE_CONFIG = {
"DENY_MESSAGE": "Access denied",
"DENY_STATUS_CODE": 403,
# optional override
# "DENY_RESPONSE_HANDLER": "myapp.security.custom_deny_handler",
'routes': [
{
'pattern': r'^/admin/.*', # regex pattern
'type': 'regex',
},
{
'pattern': '/api/', # starts with
'type': 'startswith',
},
{
'pattern': '.json', # ends with
'type': 'endswith',
},
{
'pattern': '/api/secure/', # exact match
'type': 'exact',
},
],
# Optional: Kubernetes network configuration
'kubernetes_network_range': os.getenv('KUBERNETES_NETWORK_RANGE', ''), # e.g., '10.244.0.0/16'
'pod_ip': os.getenv('POD_IP', ''), # Kubernetes pod IP
}
# Environment variable for allowed hostnames (comma-separated)
ALLOWED_HOSTNAMES_ENV = os.getenv('ALLOWED_HOSTNAMES', '')
# Example: ALLOWED_HOSTNAMES="*.example.com,api.example.com,*.subdomain.com"
5. Add Granted IPs
Use Django admin or create GrantedIP objects to allow IP addresses:
from django_ip_access.models import GrantedIP
# Add a single IP
GrantedIP.objects.create(
ip_address='192.168.1.100',
description='Development server',
is_active=True
)
# Add an IP range (CIDR)
GrantedIP.objects.create(
ip_address='10.0.0.0/24',
description='Internal network',
is_active=True
)
Configuration
Route Types
- regex: Match using regular expressions
- exact: Exact path match
- startswith: Match if path starts with pattern
- endswith: Match if path ends with pattern
Environment Variables
ALLOWED_HOSTNAMES: Comma-separated list of allowed hostnames (supports wildcards like*.example.com)POD_IP: Kubernetes pod IP (optional, for explicit network detection)KUBERNETES_NETWORK_RANGE: Kubernetes network range (optional, e.g.,10.244.0.0/16)
Same Network Detection
The middleware automatically detects if the client IP is on the same network as the server:
- Checks if both IPs are private IPs on the same subnet
- Works automatically without configuration
- Highest priority - if same network is detected, access is allowed immediately
Usage Examples
Protect Admin Routes
IP_ACCESS_MIDDLEWARE_CONFIG = {
'routes': [
{
'pattern': r'^/admin/.*',
'type': 'regex',
},
],
}
Protect API Routes
IP_ACCESS_MIDDLEWARE_CONFIG = {
'routes': [
{
'pattern': '/api/',
'type': 'startswith',
},
],
}
Allow Hostnames from Environment
Set environment variable:
export ALLOWED_HOSTNAMES="*.example.com,api.example.com"
Opt-in usage with mixins and decorators
Sometimes you don’t want to install the middleware globally, but instead protect only specific views. This package provides a mixin and a decorator that reuse the exact same access-control logic as the middleware.
Django class-based views
from django.views.generic import TemplateView
from django_ip_access.mixins import IPAccessMixin
class SecureDashboardView(IPAccessMixin, TemplateView):
template_name = "secure_dashboard.html"
# Optional: customise how the route is matched
# ip_access_route_config = {
# "pattern": "/dashboard/",
# "type": "startswith",
# }
Django function-based views
from django_ip_access.decorators import ip_access_required
@ip_access_required() # default: protect this exact path only
def secure_view(request):
...
@ip_access_required(route_config={"pattern": "/api/", "type": "startswith"})
def secure_api_view(request):
...
Django REST framework: generic views & APIView
IPAccessMixin works out of the box with DRF generic views and APIView,
because they are class-based views with a dispatch method.
from rest_framework import generics
from django_ip_access import IPAccessMixin
from .serializers import ItemSerializer
from .models import Item
class SecureItemListView(IPAccessMixin, generics.ListAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
# Optional: customise matching
# ip_access_route_config = {
# "pattern": "/api/items/",
# "type": "exact",
# }
You can also use the decorator on DRF APIView methods (or plain DRF FBVs):
from rest_framework.views import APIView
from rest_framework.response import Response
from django_ip_access.decorators import ip_access_required
class SecureStatusView(APIView):
@ip_access_required()
def get(self, request, *args, **kwargs):
return Response({"status": "ok"})
Django REST framework: viewsets
For DRF viewsets, you can mix in IPAccessMixin as well, since viewsets
inherit from APIView:
from rest_framework import viewsets
from django_ip_access import IPAccessMixin
from .serializers import ItemSerializer
from .models import Item
class SecureItemViewSet(IPAccessMixin, viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
The deny response automatically adapts:
- For DRF
Requestobjects, it returns a DRFResponsewith a JSON payload. - For regular Django requests, it returns JSON (for XHR/JSON clients) or an HTML response for browsers.
Django Admin
The middleware includes a Django admin interface for managing granted IPs at /admin/:
- View all granted IPs
- Add/edit/delete IP addresses and ranges
- Enable/disable IP entries
- Filter and search
Models
GrantedIP
ip_address: IP address or CIDR range (e.g.,192.168.1.1or192.168.1.0/24)description: Optional descriptionis_active: Enable/disable the IP entrycreated_at: Creation timestampupdated_at: Last update timestamp
Requirements
- Python 3.8+
- Django 3.2+
Optional Dependencies
netifaces: For better network interface detection (install withpip install django-ip-access-middleware[dev])
License
MIT License
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
For issues and questions, please open an issue on GitHub.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_ip_access_middleware-1.1.2.tar.gz.
File metadata
- Download URL: django_ip_access_middleware-1.1.2.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a66604efa6f630b34797985f92d3054d1c4b30513cf7350c35e76d4ebba25c9
|
|
| MD5 |
22a17e29c24e93669f448d7ce5e1c931
|
|
| BLAKE2b-256 |
c602ca39a3a3434d11a12dcf55f03e13627c474b55494785d928ada8d22bd385
|
File details
Details for the file django_ip_access_middleware-1.1.2-py3-none-any.whl.
File metadata
- Download URL: django_ip_access_middleware-1.1.2-py3-none-any.whl
- Upload date:
- Size: 18.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
154ee5708c2f761d269508965f486c66596b464b09696aa24a4ee02a45d41a3b
|
|
| MD5 |
b85a648fdcbfdbe7a4c6b10f4fe4e85a
|
|
| BLAKE2b-256 |
71e5d2c9f25469976c91143fd0b1707a51e2c97943dbfb344fb67a7b82347acc
|