Hard📖Теория4 min

Zero Trust подход

Принципы Zero Trust: mTLS, identity-based access, network segmentation и микросегментация

Zero Trust подход

Что такое Zero Trust

Zero Trust — модель безопасности, основанная на принципе «никогда не доверяй, всегда проверяй». В отличие от периметровой модели (castle-and-moat), Zero Trust не различает внутренний и внешний трафик.

Периметровая модель vs Zero Trust

Периметровая Zero Trust
Доверие Внутри сети = доверенный Никто не доверенный
Проверка На границе сети На каждом запросе
Lateral movement Свободный внутри Ограниченный
Breach impact Полный доступ изнутри Минимальный blast radius

Принципы Zero Trust

Принцип Описание
Verify explicitly Аутентификация и авторизация на каждый запрос
Least privilege Минимально необходимый доступ
Assume breach Проектировать как будто уже взломаны
Micro-segmentation Изоляция на уровне сервисов
Continuous validation Постоянная проверка, не одноразовая

Компоненты Zero Trust

[User/Device] → [Identity Provider] → [Policy Engine] → [Service]
                      ↓                      ↓
              Identity verification    Access decision
              MFA, device health      Context-based policy
              Risk scoring            Continuous evaluation

Identity-based access

Вместо IP-based правил — identity-based. Доступ определяется тем, кто запрашивает, а не откуда.

Компонент Описание
Service identity Каждый сервис имеет идентичность (SPIFFE/SPIRE)
User identity SSO + MFA для пользователей
Device identity Состояние устройства (patch level, encryption)
Workload identity Идентичность для контейнеров и VM

mTLS (Mutual TLS)

В обычном TLS только сервер предъявляет сертификат. В mTLS обе стороны аутентифицируют друг друга.

Standard TLS:               Mutual TLS:
Client → Server              Client → Server
         ← Certificate                ← Server Certificate
         (verify server)              (verify server)
                              Client Certificate →
                              (server verifies client)

PHP: проверка клиентского сертификата

PHPGo
<?php

declare(strict_types=1);

namespace App\Security;

/**
 * Verify client certificate in mTLS setup.
 * Nginx/Apache forwards cert info via headers.
 */
final readonly class MtlsVerifier
{
    /**
     * @param array<string> $allowedSubjects Allowed certificate subjects (CN)
     */
    public function __construct(
        private array $allowedSubjects,
    ) {}

    /**
     * Verify client certificate from proxy headers.
     *
     * Nginx config:
     *   proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
     *   proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;
     *   proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
     */
    public function verify(array $headers): MtlsResult
    {
        $verified = $headers['X-SSL-Client-Verify'] ?? '';

        if ($verified !== 'SUCCESS') {
            return MtlsResult::failed('Client certificate not verified');
        }

        $subjectDn = $headers['X-SSL-Client-S-DN'] ?? '';

        // Extract CN from Subject DN
        if (!preg_match('/CN=([^,\/]+)/', $subjectDn, $matches)) {
            return MtlsResult::failed('Cannot extract CN from certificate');
        }

        $cn = $matches[1];

        if (!in_array($cn, $this->allowedSubjects, true)) {
            return MtlsResult::failed("Service '{$cn}' is not authorized");
        }

        return MtlsResult::success($cn);
    }
}

final readonly class MtlsResult
{
    private function __construct(
        public bool $authenticated,
        public ?string $serviceIdentity,
        public ?string $error,
    ) {}

    public static function success(string $identity): self
    {
        return new self(true, $identity, null);
    }

    public static function failed(string $error): self
    {
        return new self(false, null, $error);
    }
}
package security

import (
	"fmt"
	"net/http"
	"regexp"
)

var cnRegex = regexp.MustCompile(`CN=([^,/]+)`)

// MtlsResult holds the result of mTLS client certificate verification.
type MtlsResult struct {
	Authenticated   bool   `json:"authenticated"`
	ServiceIdentity string `json:"service_identity,omitempty"`
	Error           string `json:"error,omitempty"`
}

// MtlsVerifier verifies client certificates forwarded by a reverse proxy.
type MtlsVerifier struct {
	allowedSubjects map[string]bool
}

// NewMtlsVerifier creates a verifier with allowed certificate CNs.
func NewMtlsVerifier(subjects []string) *MtlsVerifier {
	m := make(map[string]bool, len(subjects))
	for _, s := range subjects {
		m[s] = true
	}
	return &MtlsVerifier{allowedSubjects: m}
}

// Verify checks the client certificate headers set by nginx/envoy.
func (v *MtlsVerifier) Verify(r *http.Request) MtlsResult {
	verified := r.Header.Get("X-SSL-Client-Verify")
	if verified != "SUCCESS" {
		return MtlsResult{Error: "Client certificate not verified"}
	}

	subjectDN := r.Header.Get("X-SSL-Client-S-DN")
	matches := cnRegex.FindStringSubmatch(subjectDN)
	if len(matches) < 2 {
		return MtlsResult{Error: "Cannot extract CN from certificate"}
	}

	cn := matches[1]
	if !v.allowedSubjects[cn] {
		return MtlsResult{Error: fmt.Sprintf("Service %q is not authorized", cn)}
	}

	return MtlsResult{Authenticated: true, ServiceIdentity: cn}
}
## Network Segmentation

Микросегментация

Микросегментация изолирует сервисы друг от друга. Каждый сервис может общаться только с разрешёнными сервисами.

Без сегментации:            С микросегментацией:
┌───────────────┐           ┌─────┐  ┌─────┐
│ All services  │           │ API │──│ DB  │  (API → DB: allowed)
│ can talk to   │           └─────┘  └─────┘
│ each other    │           ┌─────┐  ┌─────┐
└───────────────┘           │ Web │──│ API │  (Web → API: allowed)
                            └─────┘  └─────┘
                            Web → DB: BLOCKED

PHP: межсервисная авторизация

PHPGo
<?php

declare(strict_types=1);

namespace App\Security;

/**
 * Service-to-service authorization policy.
 * Implements Zero Trust by checking identity + allowed operations.
 */
final readonly class ServiceAuthPolicy
{
    /**
     * Service communication matrix.
     * Key: calling service, Value: allowed target services and operations.
     *
     * @var array<string, array<string, array<string>>>
     */
    private const POLICY_MATRIX = [
        'api-gateway' => [
            'order-service' => ['createOrder', 'getOrder', 'listOrders'],
            'user-service' => ['getUser', 'validateToken'],
            'notification-service' => ['sendNotification'],
        ],
        'order-service' => [
            'payment-service' => ['processPayment', 'refund'],
            'inventory-service' => ['checkStock', 'reserveStock'],
            'notification-service' => ['sendNotification'],
        ],
        'payment-service' => [
            'order-service' => ['updateOrderStatus'],
            'notification-service' => ['sendNotification'],
        ],
        // Explicitly: payment-service CANNOT call user-service
    ];

    /**
     * Check if service A is allowed to call operation on service B.
     */
    public function isAllowed(string $callerService, string $targetService, string $operation): bool
    {
        $allowed = self::POLICY_MATRIX[$callerService][$targetService] ?? [];

        return in_array($operation, $allowed, true);
    }

    /**
     * Middleware: verify service identity and check policy.
     */
    public function authorize(string $callerIdentity, string $targetService, string $operation): void
    {
        if (!$this->isAllowed($callerIdentity, $targetService, $operation)) {
            throw new UnauthorizedServiceCallException(
                sprintf(
                    'Service "%s" is not allowed to call "%s.%s"',
                    $callerIdentity,
                    $targetService,
                    $operation,
                ),
            );
        }
    }
}
package security

import "fmt"

// ServiceAuthPolicy implements Zero Trust service-to-service authorization.
type ServiceAuthPolicy struct{}

// policyMatrix defines allowed service-to-service communication.
var policyMatrix = map[string]map[string][]string{
	"api-gateway": {
		"order-service":        {"createOrder", "getOrder", "listOrders"},
		"user-service":         {"getUser", "validateToken"},
		"notification-service": {"sendNotification"},
	},
	"order-service": {
		"payment-service":      {"processPayment", "refund"},
		"inventory-service":    {"checkStock", "reserveStock"},
		"notification-service": {"sendNotification"},
	},
	"payment-service": {
		"order-service":        {"updateOrderStatus"},
		"notification-service": {"sendNotification"},
	},
}

// IsAllowed checks if caller service can invoke an operation on the target.
func (p *ServiceAuthPolicy) IsAllowed(caller, target, operation string) bool {
	ops, ok := policyMatrix[caller][target]
	if !ok {
		return false
	}
	for _, op := range ops {
		if op == operation {
			return true
		}
	}
	return false
}

// Authorize checks the policy and returns an error if denied.
func (p *ServiceAuthPolicy) Authorize(caller, target, operation string) error {
	if !p.IsAllowed(caller, target, operation) {
		return fmt.Errorf("service %q is not allowed to call %q.%s", caller, target, operation)
	}
	return nil
}
## Zero Trust Checklist
Область Проверка
Identity Каждый сервис имеет identity
Authentication mTLS между сервисами
Authorization Policy engine на каждый запрос
Encryption TLS everywhere, encryption at rest
Segmentation Микросегментация сети
Monitoring Логирование всех access-решений
Device trust Проверка состояния устройств
Least privilege Минимальные права для каждого компонента

Важно: Zero Trust — это не продукт, а архитектурный подход. Нельзя «купить Zero Trust». Можно постепенно внедрять принципы, начиная с identity и mTLS между сервисами.

Итоги

Концепция Суть
Zero Trust Никогда не доверяй, всегда проверяй
mTLS Взаимная аутентификация клиента и сервера
Service identity Каждый сервис имеет криптографическую идентичность
Micro-segmentation Изоляция на уровне сервисов
Policy engine Централизованные решения о доступе
Continuous validation Постоянная проверка, не разовая