Skip to main content

Overview

The ValidResourceAddress validation rule ensures that resource addresses are valid and do not include dangerous or blocked address ranges. This rule accepts:
  • Valid IPv4 addresses
  • Valid IPv6 addresses
  • Valid CIDR notation (IPv4 and IPv6)
  • Valid domain names (including wildcard domains like *.example.com)
This rule blocks several dangerous address ranges to prevent security issues, including private networks, loopback addresses, and ranges that would affect all traffic.

Blocked CIDR Ranges

The following CIDR ranges are explicitly blocked for security reasons:
0.0.0.0/0
string
All IPv4 traffic - blocks the entire IPv4 address space
::/0
string
All IPv6 traffic - blocks the entire IPv6 address space
0.0.0.0/1
string
Half of all IPv4 traffic (0.0.0.0 - 127.255.255.255)
128.0.0.0/1
string
Other half of all IPv4 traffic (128.0.0.0 - 255.255.255.255)
10.0.0.0/8
string
Private network (Class A) - RFC 1918
172.16.0.0/12
string
Private network (Class B) - RFC 1918
192.168.0.0/16
string
Private network (Class C) - RFC 1918
127.0.0.0/8
string
Loopback addresses
169.254.0.0/16
string
Link-local addresses
224.0.0.0/4
string
Multicast addresses
240.0.0.0/4
string
Reserved addresses
255.255.255.255/32
string
Broadcast address

Blocked Wildcard Patterns

The following wildcard patterns are also blocked:
  • *
  • *.
  • *.*
  • *.*.*
  • *.*.*.*
These wildcards would match everything and are therefore considered dangerous.

Validation Methods

isValidIpv4()

Validates IPv4 addresses using PHP’s filter_var() with FILTER_VALIDATE_IP and FILTER_FLAG_IPV4.
private function isValidIpv4(string $value): bool
{
    return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
}
Valid Examples:
  • 192.0.2.1
  • 8.8.8.8
  • 203.0.113.42

isValidIpv6()

Validates IPv6 addresses using PHP’s filter_var() with FILTER_VALIDATE_IP and FILTER_FLAG_IPV6.
private function isValidIpv6(string $value): bool
{
    return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
}
Valid Examples:
  • 2001:db8::1
  • 2001:0db8:85a3::8a2e:0370:7334
  • fe80::1

isValidCidr()

Validates CIDR notation for both IPv4 and IPv6 addresses. Ensures:
  • The value contains a / separator
  • The prefix is numeric
  • IPv4 CIDR prefixes are between 0 and 32
  • IPv6 CIDR prefixes are between 0 and 128
private function isValidCidr(string $value): bool
{
    if (!str_contains($value, '/')) {
        return false;
    }

    [$ip, $prefix] = explode('/', $value, 2);

    if (!is_numeric($prefix)) {
        return false;
    }

    $prefixInt = (int) $prefix;

    // Validate IPv4 CIDR
    if ($this->isValidIpv4($ip)) {
        return $prefixInt >= 0 && $prefixInt <= 32;
    }

    // Validate IPv6 CIDR
    if ($this->isValidIpv6($ip)) {
        return $prefixInt >= 0 && $prefixInt <= 128;
    }

    return false;
}
Valid Examples:
  • 192.0.2.0/24
  • 203.0.113.0/25
  • 2001:db8::/32

isValidDomain()

Validates domain names with the following rules:
  • Allows wildcard domains (e.g., *.example.com)
  • Must contain at least one dot
  • Only alphanumeric characters, hyphens, and dots allowed
  • No double dots (..)
  • Maximum length of 253 characters
  • Each label (part between dots) maximum 63 characters
  • Must end with a valid TLD (minimum 2 characters)
private function isValidDomain(string $value): bool
{
    // Allow wildcard domains like *.example.com
    $domainToCheck = $value;
    if (str_starts_with($value, '*.')) {
        $domainToCheck = substr($value, 2);
    }

    // Basic domain validation
    if (!preg_match('/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/', $domainToCheck)) {
        return false;
    }

    // Ensure no double dots
    if (str_contains($domainToCheck, '..')) {
        return false;
    }

    // Ensure reasonable length
    if (strlen($domainToCheck) > 253) {
        return false;
    }

    // Each label max 63 characters
    $labels = explode('.', $domainToCheck);
    foreach ($labels as $label) {
        if (strlen($label) > 63) {
            return false;
        }
    }

    return true;
}
Valid Examples:
  • example.com
  • subdomain.example.com
  • *.example.com
  • api-v2.service.example.com

isBlockedAddress()

Checks if the provided address matches any blocked CIDR range or wildcard pattern. The comparison is case-insensitive.
private function isBlockedAddress(string $value): bool
{
    $normalizedValue = strtolower($value);

    foreach ($this->blockedCidrs as $blocked) {
        if ($normalizedValue === strtolower($blocked)) {
            return true;
        }
    }

    // Block wildcards that would match everything
    if (in_array($normalizedValue, ['*', '*.', '*.*', '*.*.*', '*.*.*.*'], true)) {
        return true;
    }

    return false;
}

Usage in Laravel Validation

Basic Usage

use App\Rules\ValidResourceAddress;

public function store(Request $request)
{
    $validated = $request->validate([
        'resource_address' => ['required', new ValidResourceAddress()],
    ]);
    
    // Process the validated address...
}

Multiple Addresses

use App\Rules\ValidResourceAddress;

$validated = $request->validate([
    'addresses' => 'required|array|min:1',
    'addresses.*' => ['required', new ValidResourceAddress()],
]);

Example Validation Scenarios

Valid Addresses

The following addresses will pass validation:
AddressTypeDescription
8.8.8.8IPv4Google DNS
2001:4860:4860::8888IPv6Google DNS IPv6
203.0.113.0/24CIDRDocumentation network
example.comDomainStandard domain
*.example.comWildcard DomainWildcard subdomain

Invalid Addresses

The following addresses will fail validation:
AddressReason
0.0.0.0/0Blocked CIDR (all IPv4 traffic)
10.0.0.1Valid IPv4 but in private range
10.0.0.0/8Blocked CIDR (private network)
192.168.1.1Valid IPv4 but in private range
127.0.0.1Valid IPv4 but loopback address
*Blocked wildcard
*.*.*.*Blocked wildcard
invalid..comDouble dots in domain
256.1.1.1Invalid IPv4 (out of range)
192.0.2.0/33Invalid CIDR prefix (>32)
-example.comInvalid domain (starts with hyphen)

Error Messages

The validation rule returns the following error messages:
required
string
Message: The :attribute is required.Returned when the value is empty or contains only whitespace.
blocked
string
Message: The :attribute contains a blocked or dangerous address range.Returned when the address matches a blocked CIDR range or wildcard pattern.
invalid_format
string
Message: The :attribute must be a valid IP address, CIDR notation, or domain name.Returned when the value is not a valid IPv4, IPv6, CIDR, or domain name.

Security Rationale

Why Block Private Networks?

Private network ranges (RFC 1918) are blocked to prevent:
  • Access to internal infrastructure
  • Routing to local network resources
  • Potential network scanning or reconnaissance
  • Bypassing network security controls
  • Loopback (127.0.0.0/8): Prevents access to localhost services
  • Link-local (169.254.0.0/16): Prevents access to auto-configured network interfaces

Why Block Broadcast and Multicast?

  • Broadcast (255.255.255.255/32): Would send traffic to all devices
  • Multicast (224.0.0.0/4): Could affect multiple network hosts

Why Block Wide-Ranging CIDRs?

Blocking 0.0.0.0/0, ::/0, and other extremely broad ranges prevents:
  • Routing all traffic through a single resource
  • Unintentional network-wide impact
  • Denial of service scenarios
  • Resource exhaustion
This validation rule is specifically designed for NetBird resources where precision is critical. Only publicly routable, specific addresses should be allowed.

Implementation Details

The validation rule implements Laravel’s ValidationRule interface:
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class ValidResourceAddress implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $value = trim((string) $value);

        if ($value === '') {
            $fail('The :attribute is required.');
            return;
        }

        // Check for blocked dangerous addresses
        if ($this->isBlockedAddress($value)) {
            $fail('The :attribute contains a blocked or dangerous address range.');
            return;
        }

        // Try to validate as IP, CIDR, or domain
        if (!$this->isValidIpv4($value)
            && !$this->isValidIpv6($value)
            && !$this->isValidCidr($value)
            && !$this->isValidDomain($value)) {
            $fail('The :attribute must be a valid IP address, CIDR notation, or domain name.');
        }
    }
}
The validation flow is optimized to check for blocked addresses first before attempting format validation, providing better security and performance.

Build docs developers (and LLMs) love