430 lines
12 KiB
Markdown
430 lines
12 KiB
Markdown
# Authentication & Authorization Implementation Summary
|
|
|
|
## What Was Implemented
|
|
|
|
This document summarizes the Phase 1 authentication and authorization implementation for the Notifier service.
|
|
|
|
## New Files & Modules Created
|
|
|
|
### Core Authentication Package (`internal/auth/`)
|
|
|
|
1. **auth.go** - Core API key management
|
|
- `APIKeyStore`: In-memory storage and validation of API keys
|
|
- `APIKey`: Key metadata (client_id, roles, rate_limit, expiration, etc.)
|
|
- `RateLimiter`: Per-key rate limiting with sliding window
|
|
- `AuthContext`: Request context for authenticated calls
|
|
- Key generation, validation, deactivation, and introspection
|
|
|
|
2. **rest_middleware.go** - REST API authentication
|
|
- `RESTAuthMiddleware`: Middleware for HTTP requests
|
|
- Supports `Authorization: Bearer <key>` and `X-API-Key: <key>` headers
|
|
- Rate limit checking
|
|
- Automatic audit logging
|
|
|
|
3. **grpc_middleware.go** - gRPC authentication
|
|
- `GRPCAuthMiddleware`: Unary and stream interceptors
|
|
- Extracts API key from gRPC metadata
|
|
- Rate limit enforcement
|
|
- Audit logging for all auth events
|
|
|
|
4. **authz.go** - Role-based access control
|
|
- `NotifierAuthz`: Authorization rule management
|
|
- Per-notifier type/account role restrictions
|
|
- Flexible RBAC: empty allowed_roles = any authenticated user
|
|
- Built-in role checking
|
|
|
|
### Configuration Updates
|
|
|
|
1. **internal/config/config.go**
|
|
- Added `AuthConfig` struct with:
|
|
- `enabled`: Toggle auth on/off (default: false)
|
|
- `default_rate_limit`: Default rate limit for new keys (100 req/min)
|
|
|
|
2. **Notifier Config Structs** - Added role support to all notifiers:
|
|
- `SMTPConfig.AllowedRoles`
|
|
- `SlackConfig.AllowedRoles`
|
|
- `NtfyConfig.AllowedRoles`
|
|
- Each notifier can now restrict which roles can use it
|
|
|
|
### Integration Points
|
|
|
|
1. **api/rest/router.go**
|
|
- New `NewRouterWithAuth()` function
|
|
- Backward compatible: `NewRouter()` still works without auth
|
|
- Auth middleware applied to all `/api/v1/*` routes except `/health`
|
|
|
|
2. **cmd/server/main.go**
|
|
- Auth initialization on startup (if enabled)
|
|
- Authorization rules registration from config
|
|
- Pass auth store to both gRPC and REST servers
|
|
- Graceful handling when auth is disabled
|
|
|
|
## Key Features
|
|
|
|
### 1. API Key Management
|
|
|
|
```go
|
|
// Create API keys with:
|
|
store := auth.NewAPIKeyStore()
|
|
key, _ := store.CreateKey(
|
|
"client-id",
|
|
[]string{"role1", "role2"},
|
|
100, // rate limit: 100 req/min
|
|
&expirationTime, // optional expiration
|
|
)
|
|
|
|
// Validate keys
|
|
key, err := store.ValidateKey(apiKeyString)
|
|
|
|
// Check rate limits
|
|
allowed, _ := store.CheckRateLimit(apiKeyString)
|
|
|
|
// Manage keys
|
|
store.UpdateLastUsed(apiKeyString)
|
|
store.DeactivateKey(apiKeyString)
|
|
store.ListKeys(clientID)
|
|
```
|
|
|
|
### 2. Role-Based Access Control
|
|
|
|
```yaml
|
|
# In config.yaml
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
...config...
|
|
allowed_roles:
|
|
- "notify-email"
|
|
- "admin"
|
|
|
|
slack:
|
|
default:
|
|
...config...
|
|
allowed_roles:
|
|
- "notify-slack"
|
|
- "notify-all"
|
|
```
|
|
|
|
If `allowed_roles` is empty, any authenticated user can use the notifier.
|
|
|
|
### 3. Rate Limiting
|
|
|
|
- Per-key rate limiting with sliding window
|
|
- Configurable on per-key basis (0 = unlimited)
|
|
- Automatically enforced at middleware level
|
|
- Returns `429 Too Many Requests` when exceeded
|
|
- Resets every 60 seconds
|
|
|
|
### 4. Audit Logging
|
|
|
|
All auth events are logged:
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2025-10-25T10:30:00Z",
|
|
"event": "auth_success",
|
|
"client_id": "billing-service",
|
|
"method": "SendNotification",
|
|
"remote_addr": "192.168.1.100"
|
|
}
|
|
```
|
|
|
|
## API Usage
|
|
|
|
### REST API
|
|
|
|
```bash
|
|
# Request
|
|
curl -X POST http://localhost:8080/api/v1/notifications \
|
|
-H "Authorization: Bearer nk_abc123..." \
|
|
-H "Content-Type: application/json" \
|
|
-d '{ "type": "email", ... }'
|
|
|
|
# Or use X-API-Key header
|
|
curl -X POST http://localhost:8080/api/v1/notifications \
|
|
-H "X-API-Key: nk_abc123..." \
|
|
-d '{ ... }'
|
|
|
|
# Error responses
|
|
401 Unauthorized # Missing/invalid key
|
|
403 Forbidden # Role not allowed
|
|
429 Too Many Requests # Rate limit exceeded
|
|
401 Unauthorized (API key expired) # Key expiration check
|
|
```
|
|
|
|
### gRPC API
|
|
|
|
```go
|
|
import "google.golang.org/grpc/metadata"
|
|
|
|
md := metadata.New(map[string][]string{
|
|
"authorization": {"bearer nk_abc123..."},
|
|
})
|
|
ctx := metadata.NewOutgoingContext(context.Background(), md)
|
|
|
|
client.SendNotification(ctx, &pb.SendNotificationRequest{...})
|
|
|
|
// Error codes
|
|
codes.Unauthenticated # Missing/invalid key
|
|
codes.ResourceExhausted # Rate limit exceeded
|
|
codes.PermissionDenied # Role not allowed
|
|
```
|
|
|
|
## Configuration Examples
|
|
|
|
### Minimal (Auth Disabled - Default)
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: false # Default behavior, no auth enforced
|
|
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
host: smtp.example.com
|
|
...
|
|
```
|
|
|
|
### Basic (Auth Enabled, No Role Restrictions)
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
default_rate_limit: 100
|
|
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
host: smtp.example.com
|
|
...
|
|
allowed_roles: [] # All authenticated users
|
|
|
|
slack:
|
|
default:
|
|
webhook_url: ...
|
|
allowed_roles: [] # All authenticated users
|
|
```
|
|
|
|
### Advanced (Multi-Tenant with Role Restrictions)
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
default_rate_limit: 100
|
|
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
host: smtp.example.com
|
|
...
|
|
allowed_roles: ["notify-email", "admin"]
|
|
|
|
tenant-a:
|
|
host: smtp-tenant-a.com
|
|
...
|
|
allowed_roles: ["tenant-a-admin"]
|
|
|
|
tenant-b:
|
|
host: smtp-tenant-b.com
|
|
...
|
|
allowed_roles: ["tenant-b-admin"]
|
|
|
|
slack:
|
|
default:
|
|
webhook_url: ...
|
|
allowed_roles: ["notify-all"]
|
|
```
|
|
|
|
## Backward Compatibility
|
|
|
|
- Auth is **disabled by default** - existing deployments continue to work unchanged
|
|
- `NewRouter()` function still works without auth
|
|
- All changes are additive - no existing APIs were modified
|
|
- Notifier configs are backward compatible (allowed_roles is optional)
|
|
|
|
## Security Properties
|
|
|
|
### What's Protected
|
|
|
|
- ✅ API endpoint access (all `/api/v1/*` routes)
|
|
- ✅ gRPC service calls
|
|
- ✅ Rate limit enforcement per key
|
|
- ✅ Role-based notifier access
|
|
- ✅ Expiration checking
|
|
- ✅ Deactivation support
|
|
- ✅ Audit logging
|
|
|
|
### What's Not Protected (Phase 1)
|
|
|
|
- ❌ Health check endpoint (`/health`) - intentionally open
|
|
- ❌ Key creation/management endpoints - requires external management
|
|
- ❌ Admin operations - not implemented in Phase 1
|
|
- ❌ Key rotation - manual implementation required
|
|
|
|
### Credential Security
|
|
|
|
- API keys are cryptographically random (32 bytes = 64 hex chars)
|
|
- Recommended: store in environment variables or secrets manager
|
|
- Not stored in plaintext in config files
|
|
- Supports key expiration and deactivation
|
|
- Per-key audit trail available via logs
|
|
|
|
## Testing the Implementation
|
|
|
|
### 1. Enable Auth in Config
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
default_rate_limit: 100
|
|
|
|
notifiers:
|
|
stdout: true
|
|
```
|
|
|
|
### 2. Create an API Key
|
|
|
|
```go
|
|
store := auth.NewAPIKeyStore()
|
|
key, _ := store.CreateKey("test-client", []string{"notify-all"}, 100, nil)
|
|
fmt.Println(key.Key)
|
|
```
|
|
|
|
### 3. Test REST API
|
|
|
|
```bash
|
|
# With auth
|
|
curl -H "Authorization: Bearer nk_<your-key>" \
|
|
http://localhost:8080/api/v1/notifications
|
|
|
|
# Without auth (should fail)
|
|
curl http://localhost:8080/api/v1/notifications
|
|
# 401 Unauthorized
|
|
|
|
# Invalid key (should fail)
|
|
curl -H "Authorization: Bearer invalid" \
|
|
http://localhost:8080/api/v1/notifications
|
|
# 401 Unauthorized
|
|
```
|
|
|
|
### 4. Test gRPC
|
|
|
|
```bash
|
|
grpcurl -plaintext \
|
|
-H "authorization: bearer nk_<your-key>" \
|
|
localhost:50051 notifier.v1.NotifierService/HealthCheck
|
|
|
|
# Should return 200 OK if key is valid
|
|
```
|
|
|
|
## Next Steps (Phase 2+)
|
|
|
|
Recommended future enhancements:
|
|
|
|
1. **JWT Tokens** - Replace API keys with short-lived JWTs
|
|
2. **Key Rotation** - Automatic key rotation mechanism
|
|
3. **OAuth2 Integration** - Support OAuth2 for client credentials flow
|
|
4. **Admin API** - Key creation/management via API endpoints
|
|
5. **Vault Integration** - Direct HashiCorp Vault integration
|
|
6. **Metrics** - Prometheus metrics for auth events
|
|
7. **mTLS** - Mutual TLS authentication for gRPC
|
|
8. **Scopes** - Fine-grained permission scopes
|
|
9. **WebAuthn** - Hardware key support
|
|
10. **Audit Webhooks** - Send auth events to external systems
|
|
|
|
## File Structure
|
|
|
|
```
|
|
notifier/
|
|
├── internal/
|
|
│ ├── auth/
|
|
│ │ ├── auth.go # Core API key management
|
|
│ │ ├── rest_middleware.go # REST authentication
|
|
│ │ ├── grpc_middleware.go # gRPC authentication
|
|
│ │ └── authz.go # Authorization rules
|
|
│ ├── config/
|
|
│ │ └── config.go # Updated with AuthConfig
|
|
│ └── notifier/
|
|
│ ├── smtp.go # Updated with allowed_roles
|
|
│ ├── slack.go # Updated with allowed_roles
|
|
│ └── ntfy.go # Updated with allowed_roles
|
|
├── api/
|
|
│ └── rest/
|
|
│ └── router.go # Updated with auth support
|
|
├── cmd/
|
|
│ └── server/
|
|
│ └── main.go # Updated with auth initialization
|
|
└── docs/
|
|
├── AUTH.md # Comprehensive auth documentation
|
|
├── AUTH_QUICK_START.md # Quick start guide
|
|
├── CLIENT_RECOMMENDATIONS.md # Best practices for client developers
|
|
└── IMPLEMENTATION_SUMMARY.md # This file
|
|
```
|
|
|
|
## Code Statistics
|
|
|
|
- **New files**: 4 (auth package)
|
|
- **Modified files**: 5 (config, routers, main, notifier configs)
|
|
- **Lines added**: ~700 (auth implementation)
|
|
- **Lines added**: ~300 (documentation)
|
|
- **Build status**: ✅ Compiles successfully
|
|
- **Backward compatible**: ✅ Yes (auth disabled by default)
|
|
|
|
## Known Limitations
|
|
|
|
1. **In-Memory Storage** - API keys are lost on restart
|
|
- Workaround: Re-create keys on startup or implement persistence
|
|
|
|
2. **No Key Management API** - Keys must be created programmatically
|
|
- Phase 2: Implement admin API for key management
|
|
|
|
3. **No Token Revocation** - Only deactivation available
|
|
- Keys can be deactivated but not selectively revoked
|
|
|
|
4. **Basic Rate Limiting** - Simple sliding window, not distributed
|
|
- Not suitable for multi-instance deployments
|
|
- Workaround: Use single instance or implement Redis-backed rate limiter
|
|
|
|
5. **No Metrics Export** - Auth events only logged, not exported
|
|
- Phase 2: Add Prometheus metrics
|
|
|
|
## Support & Maintenance
|
|
|
|
### Troubleshooting
|
|
|
|
1. **Auth not working?**
|
|
- Check `auth.enabled: true` in config
|
|
- Verify API key format: `nk_<32-hex>`
|
|
- Check role names match notifier `allowed_roles`
|
|
|
|
2. **Rate limit errors?**
|
|
- Increase `default_rate_limit` in config
|
|
- Create new key with higher rate limit
|
|
- Wait 60 seconds for window to reset
|
|
|
|
3. **Key expired?**
|
|
- Check `key.ExpiresAt` timestamp
|
|
- Create new key with `expiresIn` parameter or nil
|
|
|
|
## References
|
|
|
|
- **Authentication Package**: `internal/auth/`
|
|
- **REST Router**: `api/rest/router.go:NewRouterWithAuth()`
|
|
- **gRPC Server**: `cmd/server/main.go:startGRPCServer()`
|
|
- **Full Documentation**: `docs/AUTH.md`
|
|
- **Quick Start**: `docs/AUTH_QUICK_START.md`
|
|
- **Client Guide**: `docs/CLIENT_RECOMMENDATIONS.md`
|
|
|
|
## Summary
|
|
|
|
Phase 1 provides a solid foundation for authentication and authorization in the Notifier service:
|
|
|
|
✅ **Simple API Key Authentication** - Easy to implement and use
|
|
✅ **Rate Limiting** - Prevent abuse
|
|
✅ **Role-Based Access** - Fine-grained control
|
|
✅ **Audit Logging** - Security visibility
|
|
✅ **Backward Compatible** - Auth is optional
|
|
✅ **Well Documented** - Comprehensive guides for users and developers
|
|
|
|
The implementation is production-ready for single-instance deployments and can be extended to support more advanced scenarios in future phases.
|