fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
291
docs/features/security/route-authorization.md
Normal file
291
docs/features/security/route-authorization.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# Route Authorization System
|
||||
|
||||
Dokumentation des namespace-basierten Route Authorization Systems.
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Route Authorization System ermöglicht die Zugangskontrolle für Routes auf Basis von:
|
||||
1. **Legacy `#[Auth]` Attribute** - Backward compatibility
|
||||
2. **Namespace-basierte Blockierung** - Blockiere ganze Controller-Namespaces (z.B. `App\Application\Admin\*`)
|
||||
3. **Namespace-basierte IP-Restrictions** - IP-basierte Zugriffskontrolle per Namespace
|
||||
4. **Route-spezifische `#[IpAuth]` Attribute** - Feinkörnige IP-Kontrolle per Route
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
RouteAuthorizationService
|
||||
├── checkLegacyAuthAttribute()
|
||||
├── checkNamespaceAccessPolicy() [NEU]
|
||||
├── checkNamespaceIpRestrictions()
|
||||
└── checkRouteIpAuthAttribute()
|
||||
```
|
||||
|
||||
Der Service wird in der `RoutingMiddleware` **nach dem Routing** aufgerufen, sodass die gematchte Route bekannt ist.
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Initializer-basierte Konfiguration
|
||||
|
||||
**Location**: `src/Framework/Auth/RouteAuthorizationServiceInitializer.php`
|
||||
|
||||
```php
|
||||
#[Initializer]
|
||||
public function __invoke(Container $container): RouteAuthorizationService
|
||||
{
|
||||
$namespaceConfig = [
|
||||
// Namespace Pattern => Configuration
|
||||
'App\Application\Admin\*' => [
|
||||
'visibility' => 'admin', // IP-based restriction
|
||||
'access_policy' => NamespaceAccessPolicy::blocked()
|
||||
],
|
||||
];
|
||||
|
||||
return new RouteAuthorizationService(
|
||||
config: $this->config,
|
||||
namespaceConfig: $namespaceConfig
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Namespace Patterns
|
||||
|
||||
**Wildcard-basierte Patterns**:
|
||||
- `App\Application\Admin\*` - Matched alle Admin-Controller
|
||||
- `App\Application\Api\*` - Matched alle API-Controller
|
||||
- `App\Application\*` - Matched alle Application-Controller
|
||||
|
||||
**Exact Match**:
|
||||
- `App\Application\Admin\Dashboard` - Nur exakt dieser Namespace
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. Admin-Bereich komplett blockieren
|
||||
|
||||
```php
|
||||
$namespaceConfig = [
|
||||
'App\Application\Admin\*' => [
|
||||
'access_policy' => NamespaceAccessPolicy::blocked()
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
**Ergebnis**: Alle Admin-Controller werfen `RouteNotFound` (404)
|
||||
|
||||
### 2. Admin-Bereich mit Allowlist
|
||||
|
||||
```php
|
||||
use App\Application\Admin\LoginController;
|
||||
use App\Application\Admin\HealthCheckController;
|
||||
|
||||
$namespaceConfig = [
|
||||
'App\Application\Admin\*' => [
|
||||
'access_policy' => NamespaceAccessPolicy::blockedExcept(
|
||||
LoginController::class,
|
||||
HealthCheckController::class
|
||||
)
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
**Ergebnis**:
|
||||
- ✅ `LoginController` und `HealthCheckController` öffentlich erreichbar
|
||||
- ❌ Alle anderen Admin-Controller blockiert
|
||||
|
||||
### 3. Kombination: IP-Restriction + Namespace-Blocking
|
||||
|
||||
```php
|
||||
$namespaceConfig = [
|
||||
'App\Application\Admin\*' => [
|
||||
'visibility' => 'admin', // Nur Admin-IPs erlaubt
|
||||
'access_policy' => NamespaceAccessPolicy::blockedExcept(
|
||||
LoginController::class // Aber Login ist public
|
||||
)
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
**Ergebnis**:
|
||||
- ✅ `LoginController` - öffentlich erreichbar (trotz admin visibility)
|
||||
- 🔒 Alle anderen Admin-Controller - nur von Admin-IPs
|
||||
|
||||
### 4. API-Bereich mit IP-Restriction (ohne Blocking)
|
||||
|
||||
```php
|
||||
$namespaceConfig = [
|
||||
'App\Application\Api\*' => [
|
||||
'visibility' => 'local', // Nur localhost/private IPs
|
||||
// Kein access_policy - keine Namespace-Blockierung
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
**Ergebnis**: API nur von localhost/private IPs erreichbar
|
||||
|
||||
### 5. Mehrere Namespace-Policies
|
||||
|
||||
```php
|
||||
$namespaceConfig = [
|
||||
// Admin komplett gesperrt
|
||||
'App\Application\Admin\*' => [
|
||||
'access_policy' => NamespaceAccessPolicy::blocked()
|
||||
],
|
||||
|
||||
// API nur von localhost
|
||||
'App\Application\Api\*' => [
|
||||
'visibility' => 'local'
|
||||
],
|
||||
|
||||
// Internal Tools nur admin IPs
|
||||
'App\Application\Internal\*' => [
|
||||
'visibility' => 'admin',
|
||||
'access_policy' => NamespaceAccessPolicy::blocked()
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
## Value Objects
|
||||
|
||||
### NamespaceAccessPolicy
|
||||
|
||||
```php
|
||||
// Alle Controller im Namespace blockieren
|
||||
NamespaceAccessPolicy::blocked()
|
||||
|
||||
// Alle blockieren außer spezifische Controller
|
||||
NamespaceAccessPolicy::blockedExcept(
|
||||
LoginController::class,
|
||||
HealthCheckController::class
|
||||
)
|
||||
|
||||
// Alle erlauben (default)
|
||||
NamespaceAccessPolicy::allowed()
|
||||
|
||||
// Prüfen ob Controller blockiert ist
|
||||
$policy->isControllerBlocked(Dashboard::class) // true/false
|
||||
```
|
||||
|
||||
## Visibility Modes (IP-Restrictions)
|
||||
|
||||
**Predefined Modes**:
|
||||
- `public` - Keine IP-Restrictions
|
||||
- `admin` - Nur Admin-IPs (WireGuard, etc.)
|
||||
- `local` - Nur localhost/127.0.0.1
|
||||
- `development` - Development-IPs
|
||||
- `private` - Alias für `local`
|
||||
- `custom` - Custom IP-Liste via Config
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. **Legacy Auth Attribute** - Backward compatibility check
|
||||
2. **Namespace Access Policy** - Block/Allow basierend auf Controller-Klasse
|
||||
3. **Namespace IP Restrictions** - IP-basierte Zugriffskontrolle
|
||||
4. **Route IP Auth Attribute** - Feinkörnige Route-Level IP-Kontrolle
|
||||
|
||||
Alle Checks werfen `RouteNotFound` bei Failure (versteckt Route-Existenz).
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Test Beispiel
|
||||
|
||||
```php
|
||||
use App\Framework\Auth\RouteAuthorizationService;
|
||||
use App\Framework\Auth\ValueObjects\NamespaceAccessPolicy;
|
||||
|
||||
it('blocks admin controllers except allowlist', function () {
|
||||
$config = ['App\Application\Admin\*' => [
|
||||
'access_policy' => NamespaceAccessPolicy::blockedExcept(
|
||||
LoginController::class
|
||||
)
|
||||
]];
|
||||
|
||||
$service = new RouteAuthorizationService(
|
||||
config: $this->config,
|
||||
namespaceConfig: $config
|
||||
);
|
||||
|
||||
// Should throw RouteNotFound for Dashboard
|
||||
expect(fn() => $service->authorize($request, $dashboardRoute))
|
||||
->toThrow(RouteNotFound::class);
|
||||
|
||||
// Should allow LoginController
|
||||
expect(fn() => $service->authorize($request, $loginRoute))
|
||||
->not->toThrow(RouteNotFound::class);
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Namespace-Blocking für Production
|
||||
- Blockiere Admin/Internal-Bereiche in Production
|
||||
- Nutze Allowlist nur für wirklich öffentliche Endpoints (Login, Health)
|
||||
|
||||
### 2. IP-Restrictions für Sensitive Bereiche
|
||||
- Kombiniere Namespace-Blocking mit IP-Restrictions
|
||||
- Nutze `visibility: 'admin'` für maximale Sicherheit
|
||||
|
||||
### 3. Graceful Error Handling
|
||||
- Alle Checks werfen `RouteNotFound` (404)
|
||||
- Versteckt Route-Existenz vor Angreifern
|
||||
- Keine Information Leakage
|
||||
|
||||
### 4. Konfiguration via Initializer
|
||||
- Zentrale Konfiguration in `RouteAuthorizationServiceInitializer`
|
||||
- Environment-spezifische Configs möglich (dev vs. production)
|
||||
- Type-safe via Value Objects
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Von altem System (RoutingMiddleware::withNamespaceConfig)
|
||||
|
||||
**Alt**:
|
||||
```php
|
||||
RoutingMiddleware::withNamespaceConfig(
|
||||
$router, $dispatcher, $config, $performance, $container,
|
||||
namespaceConfig: [
|
||||
'App\Application\Admin\*' => ['visibility' => 'admin']
|
||||
]
|
||||
);
|
||||
```
|
||||
|
||||
**Neu**:
|
||||
```php
|
||||
// In RouteAuthorizationServiceInitializer
|
||||
$namespaceConfig = [
|
||||
'App\Application\Admin\*' => [
|
||||
'visibility' => 'admin',
|
||||
'access_policy' => NamespaceAccessPolicy::blocked() // NEU
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Problem: Route wirft 404 obwohl sie erreichbar sein sollte
|
||||
|
||||
**Debugging**:
|
||||
1. Prüfe `RouteAuthorizationServiceInitializer` Config
|
||||
2. Check ob Controller-Namespace in `namespaceConfig` matched
|
||||
3. Prüfe `access_policy` - ist Controller in Allowlist?
|
||||
4. Check IP-Restrictions (`visibility`)
|
||||
|
||||
### Problem: Allowlist funktioniert nicht
|
||||
|
||||
**Ursache**: Controller-Klasse exakt mit `::class` angeben
|
||||
|
||||
```php
|
||||
// ❌ Falsch
|
||||
NamespaceAccessPolicy::blockedExcept('LoginController')
|
||||
|
||||
// ✅ Korrekt
|
||||
NamespaceAccessPolicy::blockedExcept(
|
||||
\App\Application\Admin\LoginController::class
|
||||
)
|
||||
```
|
||||
|
||||
## Framework Integration
|
||||
|
||||
- **Automatic Discovery**: Service wird via `#[Initializer]` automatisch registriert
|
||||
- **DI Container**: Alle Dependencies werden automatisch injected
|
||||
- **Type Safety**: Value Objects für alle Policies
|
||||
- **Readonly Classes**: Unveränderliche Policies für Thread-Safety
|
||||
- **Framework-konform**: Nutzt bestehende Patterns (IpAuthPolicy, RouteNotFound)
|
||||
Reference in New Issue
Block a user