DomainSentinel

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2025 License: Apache-2.0 Imports: 10 Imported by: 0

README

Domain_Sentinel_Logo


GitHub GitHub release (latest by date including pre-releases) GitHub go.mod Go version GitHub issues GitHub last commit (branch)

Domain Sentinel

Overview

The domainSentinel plugin is a Traefik middleware designed to centrally manage access control based on source IP addresses, organized by domain and URL path. Instead of configuring access lists individually on routers, this plugin allows you to define and enforce those rules in one central location within Traefik.

It provides fine-grained control by allowing both domain-wide and path-specific whitelisting using individual IP addresses and CIDR blocks. This is especially useful for protecting administrative interfaces, staging environments, internal APIs, or other sensitive routes, ensuring only trusted sources can reach them.

Domain Sentinel diagram

Structs and Configuration Explanation

1. Config Struct

Purpose: Holds the entire plugin configuration, mapping domain names to their respective access rules.

Fields:

  • DomainPathRules
    • Type: map[string]DomainConfig
    • Description: Maps each domain name to a DomainConfig struct, which contains access rules for that domain and its paths.

2. DomainConfig Struct

Purpose: Contains source IP allowlists for a domain and its individual path rules.

Fields:

  • SourceIPs

    • Type: []string
    • Description: List of IP addresses or CIDR blocks that are allowed to access the domain globally.
    • Example:
      sourceIPs:
        - "192.168.1.0/24"
        - "10.0.0.1"
      
  • PathRules

    • Type: []PathConfig
    • Description: A list of access rules that apply to specific URL paths within the domain. If a request path matches a rule, its corresponding IPs override the domain-wide list.
    • Example:
      pathRules:
        - path: "/admin/*"
          sourceIPs:
            - "10.0.0.0/24"
            - "203.0.113.1"
      

3. PathConfig Struct

Purpose: Defines access rules for a specific path pattern.

Fields:

  • Path

    • Type: string
    • Description: The URL path to protect. Supports exact match or wildcard prefix (/path/*).
    • Example: "/admin/*"
  • SourceIPs

    • Type: []string
    • Description: List of allowed IPs or CIDRs for this specific path. If a path rule matches, only these IPs are used to validate the request.
    • Example:
      sourceIPs:
        - "192.168.100.1"
        - "10.0.0.0/24"
      

How it Works

Middleware Flow
  1. Extracts the domain from the Host header (ignoring any port).
  2. Looks up the domain config in DomainPathRules.
    • If no config is found → the request is allowed.
  3. Checks path-specific rules:
    • If any rule’s Path matches the request URL:
      • Validates the request's source IP against the rule’s SourceIPs.
      • If the IP is allowed → the request proceeds.
      • Else → the request is blocked with 403 Forbidden.
  4. Fallback to domain-wide IP rules if no path rule matches.
    • Same logic applies using the DomainConfig.SourceIPs.

IP Matching
  • Supports both individual IPs (203.0.113.5) and CIDR blocks (192.168.0.0/24).
  • The client IP is derived from req.RemoteAddr using net.SplitHostPort.
  • All IP checks are done using Go's built-in net.ParseIP and net.ParseCIDR.

Path Matching
  • Paths can either:
    • Match exactly: /admin
    • Use a wildcard: /admin/* matches /admin/, /admin/settings, etc.
  • If multiple path rules are defined, the first matching rule wins.

Example Configuration

http:
  middlewares:
    domain-sentinel:
      plugin:
        domainSentinel:
          domainPathRules:
            "www3.example.com":
              sourceIPs:
                - "192.168.1.0/24"
                - "78.6.34.123"
                - "10.10.3.112"
                - "10.0.2.11"
            "www4.example.com":
              sourceIPs:
                - "0.0.0.0/0"
              pathRules:
                - path: "/admin/*"
                  sourceIPs:
                    - "10.10.4.0/24"
                    - "192.168.1.2"
                    - "80.187.117.232"
                - path: "/oai/*"
                  sourceIPs:
                    - "76.5.98.123"
Explanation
  • www3.example.com is protected globally, and all paths require IPs from the specified list.
  • www4.example.com allows all IPs (0.0.0.0/0) except for restricted paths:
    • /admin/* is restricted to specific internal and external IPs.
    • /oai/* only allows 76.5.98.123.

Code Highlights

ServeHTTP (Core Logic)
  • Entry point for request handling.
  • Extracts domain and path.
  • Matches path rules or falls back to domain IP list.
  • Validates IP → allows or blocks the request.

isPathAllowed
  • Checks if request path matches a rule using exact or wildcard matching.
  • Supports /path/* pattern prefixing.

isIPAllowed
  • Checks if client IP matches any allowed CIDR or exact IP.
  • Uses net.ParseIP, net.ParseCIDR and CIDR containment.
  • Avoids misconfigured CIDRs by falling back to direct IP matching if needed.

Setup instructions

Step 1: Load/import the plugin into traefik

  1. Edit your Traefik static configuration file (e.g., traefik.yml or traefik.toml), and add the plugin's Github repository:

    Example: traefik.yml:

    experimental:
      plugins:
        domainSentinel:
          moduleName: "github.com/Rau-N/DomainSentinel"
          version: "v1.0.0"
    

    Ensure to use the current version tag.

Step 2: Configure Dynamic Configuration

  1. Create a new or use an already existing dynamic configuration file (e.g., dynamic.yml) that defines how the plugin should behave:

    Example dynamic.yml:

    http:
      middlewares:
        domain-sentinel:
          plugin:
            domainSentinel:
              domainPathRules:
                "www3.example.com":
                  sourceIPs:
                    - "192.168.1.0/24"
                    - "78.6.34.123"
                    - "10.10.3.111"
                "www4.example.com":
                  sourceIPs:
                    - "0.0.0.0/0"
                  pathRules:
                    - path: "/admin/*"
                      sourceIPs:
                        - "10.10.4.0/24"
                        - "192.168.1.2"
                    - path: "/oai/*"
                      sourceIPs:
                        - "76.5.98.123"
                "www5.example.com":
                  sourceIPs:
                    - "10.10.3.0/24"
                    - "64.2.120.12"
    
    • This configuration defines the global rules for the domain-sentinel middleware, consisting of any combination of domain names, requested paths, and source IP addresses.

Step 3: Associate the middleware plugin to the entrypoint

  1. Edit your Traefik static configuration file traefik.yml:

    Example traefik.yml:

    entryPoints:
      webinsecure:
        address: ":80"
        http:
          middlewares:
            - domain-sentinel@file
    
    • This configuration ensures that the domain-sentinel middleware can analyze and intervene in the whole network traffic passing through the traefik proxy.

Step 4: Restart Traefik

  1. Start or restart traefik to load the plugin and apply the new configuration

    docker compose down && docker compose up -d
    

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error)

New creates a new DomainSentinel middleware.

Types

type Config

type Config struct {
	DomainPathRules map[string]DomainConfig `json:"domainPathRules" yaml:"domainPathRules" mapstructure:"domainPathRules"`
}

Config holds the plugin configuration.

func CreateConfig

func CreateConfig() *Config

CreateConfig creates the default plugin configuration.

type DenyResponse added in v1.1.0

type DenyResponse struct {
	StatusCode  int               `json:"statusCode"  yaml:"statusCode"  mapstructure:"statusCode"`
	ContentType string            `json:"contentType" yaml:"contentType" mapstructure:"contentType"`
	Body        string            `json:"body"        yaml:"body"        mapstructure:"body"`
	Headers     map[string]string `json:"headers"     yaml:"headers"     mapstructure:"headers"`
}

type DomainConfig

type DomainConfig struct {
	SourceIPs []string      `json:"sourceIPs"   yaml:"sourceIPs"   mapstructure:"sourceIPs"`
	PathRules []PathConfig  `json:"pathRules"   yaml:"pathRules"   mapstructure:"pathRules"`
	Deny      *DenyResponse `json:"denyResponse" yaml:"denyResponse" mapstructure:"denyResponse"`
}

DomainConfig holds domain-wide source IPs and path-specific configurations.

type DomainSentinel

type DomainSentinel struct {
	// contains filtered or unexported fields
}

DomainSentinel middleware struct

func (*DomainSentinel) ServeHTTP

func (ds *DomainSentinel) ServeHTTP(rw http.ResponseWriter, req *http.Request)

type PathConfig

type PathConfig struct {
	Path      string        `json:"path"        yaml:"path"        mapstructure:"path"`
	SourceIPs []string      `json:"sourceIPs"   yaml:"sourceIPs"   mapstructure:"sourceIPs"`
	Deny      *DenyResponse `json:"denyResponse" yaml:"denyResponse" mapstructure:"denyResponse"`
}

PathConfig holds the path and source IPs for a specific path under a domain.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL