Skip to main content

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:
    1. Same network detection (highest priority - allows immediately)
    2. Hostname matching (from environment variables)
    3. 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 Request objects, it returns a DRF Response with 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.1 or 192.168.1.0/24)
  • description: Optional description
  • is_active: Enable/disable the IP entry
  • created_at: Creation timestamp
  • updated_at: Last update timestamp

Requirements

  • Python 3.8+
  • Django 3.2+

Optional Dependencies

  • netifaces: For better network interface detection (install with pip 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

django_ip_access_middleware-1.1.0.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_ip_access_middleware-1.1.0-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file django_ip_access_middleware-1.1.0.tar.gz.

File metadata

File hashes

Hashes for django_ip_access_middleware-1.1.0.tar.gz
Algorithm Hash digest
SHA256 6dfd9556094c6a2b4c9c630014fd3a5b663e4515811ee212ac269a0b33629c8b
MD5 126dacb81667f644e034d5c6591d249b
BLAKE2b-256 4607439a73612454f4570c9ddb46c038b50336f376c310a57d823dc5acdf171a

See more details on using hashes here.

File details

Details for the file django_ip_access_middleware-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_ip_access_middleware-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35b7c23713083a9e894a4459736a8712d28d5b83bfe1f5d3cc8ec2a9156acabe
MD5 8c49be0b82d0df48ea68bb26b9fed537
BLAKE2b-256 02d28b8bbf03244e6fe7bd0b051073da6627069a1e30a2de31a17f0db5ec6744

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page