849 lines
21 KiB
Markdown
849 lines
21 KiB
Markdown
# Authentication & Authorization Guide
|
|
|
|
This guide explains how to use the authentication and authorization features in the Notifier service.
|
|
|
|
## Overview
|
|
|
|
The Notifier service includes:
|
|
- **API Key Authentication**: Simple token-based authentication using Bearer tokens or API keys
|
|
- **Role-Based Access Control (RBAC)**: Fine-grained authorization for specific notifiers
|
|
- **Rate Limiting**: Per-key request rate limiting to prevent abuse
|
|
- **Audit Logging**: All auth failures and API key usage are logged
|
|
|
|
## Enabling Authentication
|
|
|
|
Authentication is **disabled by default**. To enable it, you need:
|
|
|
|
1. **PostgreSQL database** for persistent key storage
|
|
2. **Auth configuration** in your config file
|
|
|
|
### Configuration
|
|
|
|
Add to your configuration file:
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
default_rate_limit: 100 # requests per minute, 0 = unlimited
|
|
database:
|
|
url: "postgresql://user:password@localhost:5432/notifier"
|
|
```
|
|
|
|
Or via environment variables:
|
|
|
|
```bash
|
|
export NOTIFIER_AUTH_ENABLED=true
|
|
export NOTIFIER_AUTH_DEFAULT_RATE_LIMIT=100
|
|
export NOTIFIER_AUTH_DATABASE_URL="postgresql://user:password@localhost:5432/notifier"
|
|
```
|
|
|
|
### Database Setup
|
|
|
|
The database schema is automatically created on first connection. You only need to:
|
|
|
|
1. Create a PostgreSQL database (e.g., `notifier`)
|
|
2. Provide database URL in configuration
|
|
3. The service will create required tables:
|
|
- `api_keys` - Stores API key metadata
|
|
- `api_key_audit_log` - Tracks all key operations
|
|
|
|
**Example**: Creating a PostgreSQL database
|
|
```bash
|
|
createdb notifier
|
|
# Or via SQL:
|
|
# CREATE DATABASE notifier;
|
|
```
|
|
|
|
## Creating API Keys
|
|
|
|
API keys are created via the REST API once you have an admin key. The system uses a hybrid architecture with persistent PostgreSQL storage and in-memory cache for performance.
|
|
|
|
### Step 1: Bootstrap (Initial Setup)
|
|
|
|
On first deployment, create an initial admin key via environment variables:
|
|
|
|
```bash
|
|
export NOTIFIER_AUTH_ENABLED=true
|
|
export NOTIFIER_BOOTSTRAP_ADMIN_KEY=true
|
|
export NOTIFIER_AUTH_DATABASE_URL="postgresql://user:password@localhost:5432/notifier"
|
|
|
|
./notifier serve
|
|
|
|
# Output:
|
|
# ============================================================
|
|
# NOTIFIER BOOTSTRAP: ADMIN KEY CREATED
|
|
# ============================================================
|
|
# Key: nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
|
|
# Save this key in a secure location.
|
|
# ============================================================
|
|
```
|
|
|
|
**Important**: Save the admin key securely. You won't be able to see it again.
|
|
|
|
### Step 2: Create Additional Keys
|
|
|
|
Use the admin key to create keys for your services via REST API:
|
|
|
|
```bash
|
|
ADMIN_KEY="nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
|
|
|
|
# Create a key for your billing service
|
|
curl -X POST http://localhost:8080/api/v1/admin/keys \
|
|
-H "Authorization: Bearer $ADMIN_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"client_id": "billing-service",
|
|
"roles": ["notify-email", "notify-slack"],
|
|
"rate_limit": 1000,
|
|
"expires_in": "8760h"
|
|
}'
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"key": "nk_b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1",
|
|
"name": "billing-service-1698297600",
|
|
"client_id": "billing-service",
|
|
"roles": ["notify-email", "notify-slack"],
|
|
"created_at": "2024-10-26T12:00:00Z",
|
|
"rate_limit": 1000
|
|
}
|
|
```
|
|
|
|
### Step 3: Store Key Securely
|
|
|
|
Store the returned key in a secure location:
|
|
|
|
```bash
|
|
echo "$BILLING_KEY" > ~/.billing-notifier-key
|
|
chmod 600 ~/.billing-notifier-key
|
|
```
|
|
|
|
### Database Persistence
|
|
|
|
Keys are automatically persisted to PostgreSQL database specified in configuration:
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
database:
|
|
url: "postgresql://user:password@localhost:5432/notifier"
|
|
```
|
|
|
|
The system automatically creates the required schema:
|
|
- `api_keys` table - Stores key metadata
|
|
- `api_key_audit_log` table - Tracks all key operations
|
|
|
|
### Programmatic Creation (Go)
|
|
|
|
If you need to create keys programmatically in Go code:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
"github.com/igodwin/notifier/internal/auth"
|
|
)
|
|
|
|
func main() {
|
|
// Create database backend
|
|
dbStore, err := auth.NewKeyStoreDB("postgresql://user:password@localhost:5432/notifier")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer dbStore.Close()
|
|
|
|
// Create hybrid key store (memory cache + database backend)
|
|
cache := auth.NewAPIKeyStore()
|
|
keyStore := auth.NewHybridKeyStore(cache, dbStore)
|
|
|
|
// Load existing keys from database
|
|
ctx := context.Background()
|
|
if err := keyStore.InitializeFromDatabase(ctx); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Create an API key for a client
|
|
expiresIn := 30 * 24 * time.Hour // 30 days
|
|
key, err := keyStore.CreateKey(
|
|
ctx,
|
|
"billing-service", // Client ID
|
|
[]string{"notify-email", "notify-slack"}, // Roles
|
|
1000, // Rate limit: 1000 req/min
|
|
&expiresIn, // Expires in 30 days
|
|
"admin", // Who created it
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Printf("API Key: %s\n", key.Key)
|
|
fmt.Printf("Client ID: %s\n", key.ClientID)
|
|
fmt.Printf("Roles: %v\n", key.Roles)
|
|
fmt.Printf("Rate Limit: %d req/min\n", key.RateLimit)
|
|
fmt.Printf("Expires At: %v\n", key.ExpiresAt)
|
|
|
|
// Example output:
|
|
// API Key: nk_b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1
|
|
// Client ID: billing-service
|
|
// Roles: [notify-email notify-slack]
|
|
// Rate Limit: 1000 req/min
|
|
// Expires At: 2025-11-24 10:30:00 +0000 UTC
|
|
}
|
|
```
|
|
|
|
### Managing API Keys
|
|
|
|
Once created, you can list, revoke, and audit keys via REST API:
|
|
|
|
#### List Your Keys
|
|
|
|
```bash
|
|
curl -X GET http://localhost:8080/api/v1/admin/keys \
|
|
-H "Authorization: Bearer $YOUR_KEY"
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"keys": [
|
|
{
|
|
"key_preview": "nk_o5p6",
|
|
"name": "billing-service-1698297600",
|
|
"client_id": "billing-service",
|
|
"roles": ["notify-email", "notify-slack"],
|
|
"created_at": "2024-10-26T12:00:00Z",
|
|
"last_used_at": "2024-10-26T15:30:00Z",
|
|
"expires_at": "2025-10-26T12:00:00Z",
|
|
"is_active": true,
|
|
"rate_limit": 1000
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Note: Only the last 4 characters of keys are shown for security.
|
|
|
|
#### Revoke a Key
|
|
|
|
```bash
|
|
curl -X DELETE http://localhost:8080/api/v1/admin/keys/nk_key_to_revoke \
|
|
-H "Authorization: Bearer $ADMIN_KEY"
|
|
```
|
|
|
|
Returns: `204 No Content` on success.
|
|
|
|
#### View Audit Log
|
|
|
|
```bash
|
|
curl -X GET http://localhost:8080/api/v1/admin/keys/nk_key/audit \
|
|
-H "Authorization: Bearer $ADMIN_KEY"
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"key_preview": "nk_o5p6",
|
|
"audit_log": [
|
|
{
|
|
"action": "created",
|
|
"performed_by": "admin-bootstrap",
|
|
"performed_at": "2024-10-26T12:00:00Z",
|
|
"details": {
|
|
"client_id": "billing-service",
|
|
"roles": ["notify-email", "notify-slack"]
|
|
}
|
|
},
|
|
{
|
|
"action": "deactivated",
|
|
"performed_by": "admin-user",
|
|
"performed_at": "2024-10-26T14:30:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Key Naming Convention
|
|
|
|
Generated API keys follow the format: `nk_<32-hex-characters>`
|
|
|
|
- `nk_` prefix identifies it as a Notifier API key
|
|
- The hex string is cryptographically secure random
|
|
- Keys use cryptographically secure random number generation
|
|
|
|
### Key Properties
|
|
|
|
| Property | Description |
|
|
|----------|-------------|
|
|
| `Key` | The actual API key to use in requests |
|
|
| `ClientID` | Identifier for the client/service using the key |
|
|
| `Roles` | List of roles granted to this key (e.g., "notify-email", "notify-slack") |
|
|
| `RateLimit` | Requests per minute allowed (0 = unlimited) |
|
|
| `ExpiresAt` | Optional expiration date (if set, key becomes invalid after this time) |
|
|
| `CreatedAt` | Timestamp when the key was created |
|
|
| `LastUsedAt` | Timestamp of the last successful authentication |
|
|
| `IsActive` | Whether the key is currently active (can be deactivated) |
|
|
|
|
## API Key Roles
|
|
|
|
Roles control which notifiers a client can use. Common role patterns:
|
|
|
|
| Role | Purpose |
|
|
|------|---------|
|
|
| `notify-email` | Can use email (SMTP) notifiers |
|
|
| `notify-slack` | Can use Slack notifiers |
|
|
| `notify-ntfy` | Can use ntfy.sh notifiers |
|
|
| `notify-all` | Can use all notification types |
|
|
| `admin` | Full access (optional, for admin operations) |
|
|
|
|
You define your own roles based on your needs.
|
|
|
|
## Configuring Role-Based Access
|
|
|
|
Control which roles can use specific notifiers in your config:
|
|
|
|
```yaml
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
host: "smtp.example.com"
|
|
port: 587
|
|
username: "user@example.com"
|
|
password: "${SMTP_PASSWORD}"
|
|
from: "noreply@example.com"
|
|
use_tls: true
|
|
allowed_roles: # Empty list = all authenticated users can use
|
|
- "notify-email"
|
|
- "admin"
|
|
|
|
internal:
|
|
host: "smtp-internal.example.com"
|
|
port: 587
|
|
username: "internal@example.com"
|
|
password: "${SMTP_INTERNAL_PASSWORD}"
|
|
from: "internal@example.com"
|
|
use_tls: true
|
|
allowed_roles:
|
|
- "admin" # Only admins can use internal SMTP
|
|
|
|
slack:
|
|
default:
|
|
webhook_url: "${SLACK_WEBHOOK}"
|
|
username: "Notifier"
|
|
allowed_roles:
|
|
- "notify-slack"
|
|
- "notify-all"
|
|
|
|
ntfy:
|
|
default:
|
|
server_url: "https://ntfy.sh"
|
|
token: "${NTFY_TOKEN}"
|
|
allowed_roles:
|
|
- "notify-all"
|
|
```
|
|
|
|
## Using API Keys
|
|
|
|
### REST API
|
|
|
|
Include the API key in the `Authorization` header as a Bearer token:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8080/api/v1/notifications \
|
|
-H "Authorization: Bearer nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"type": "email",
|
|
"subject": "Hello",
|
|
"body": "World",
|
|
"recipients": ["user@example.com"]
|
|
}'
|
|
```
|
|
|
|
Alternatively, use the `X-API-Key` header:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8080/api/v1/notifications \
|
|
-H "X-API-Key: nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{ ... }'
|
|
```
|
|
|
|
### gRPC
|
|
|
|
Include the API key in gRPC metadata:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/metadata"
|
|
pb "github.com/igodwin/notifier/api/grpc/pb"
|
|
)
|
|
|
|
func main() {
|
|
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
|
|
defer conn.Close()
|
|
|
|
// Create context with API key
|
|
ctx := context.Background()
|
|
md := metadata.New(map[string][]string{
|
|
"authorization": {"bearer nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"},
|
|
})
|
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
|
|
|
// Use the client
|
|
client := pb.NewNotifierServiceClient(conn)
|
|
resp, err := client.SendNotification(ctx, &pb.SendNotificationRequest{
|
|
Type: pb.NotificationType_NOTIFICATION_TYPE_EMAIL,
|
|
Subject: "Hello",
|
|
Body: "World",
|
|
Recipients: []string{"user@example.com"},
|
|
})
|
|
// ...
|
|
}
|
|
```
|
|
|
|
Or use `grpcurl`:
|
|
|
|
```bash
|
|
grpcurl -plaintext \
|
|
-H "authorization: bearer nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
|
|
-d '{"type":"NOTIFICATION_TYPE_EMAIL","subject":"Hello","body":"World","recipients":["user@example.com"]}' \
|
|
localhost:50051 notifier.v1.NotifierService/SendNotification
|
|
```
|
|
|
|
## Credential Management Best Practices
|
|
|
|
### For Self-Created Clients
|
|
|
|
**DO:**
|
|
- ✅ Store API keys in environment variables
|
|
- ✅ Store API keys in secure configuration management (Vault, AWS Secrets Manager)
|
|
- ✅ Rotate keys periodically (every 90 days recommended)
|
|
- ✅ Use separate keys per environment (dev, staging, prod)
|
|
- ✅ Use separate keys per service/application
|
|
- ✅ Monitor key usage via logs and audit trails
|
|
- ✅ Set expiration times on keys
|
|
- ✅ Use appropriate rate limits
|
|
|
|
**DON'T:**
|
|
- ❌ Store API keys in code or version control
|
|
- ❌ Include API keys in Docker images or build artifacts
|
|
- ❌ Log or display API keys in error messages
|
|
- ❌ Use wildcard roles like "admin" for non-admin services
|
|
- ❌ Share API keys between services
|
|
- ❌ Use the same key for multiple environments
|
|
|
|
### Example: Storing in Environment Variables
|
|
|
|
```bash
|
|
# .env file (not committed to git)
|
|
NOTIFIER_API_KEY="nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
|
|
```
|
|
|
|
```go
|
|
// In your application
|
|
import "os"
|
|
|
|
apiKey := os.Getenv("NOTIFIER_API_KEY")
|
|
```
|
|
|
|
### Example: Using with Configuration Management (Vault)
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
vault "github.com/hashicorp/vault/api"
|
|
)
|
|
|
|
func getAPIKeyFromVault() (string, error) {
|
|
client, err := vault.NewClient(&vault.Config{
|
|
Address: os.Getenv("VAULT_ADDR"),
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
secret, err := client.Logical().Read("secret/data/notifier/api-key")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
data := secret.Data["data"].(map[string]interface{})
|
|
return data["key"].(string), nil
|
|
}
|
|
```
|
|
|
|
## Client Implementation Examples
|
|
|
|
### Go Client
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"github.com/igodwin/notifier/api/grpc/pb"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/metadata"
|
|
)
|
|
|
|
type NotifierClient struct {
|
|
client pb.NotifierServiceClient
|
|
apiKey string
|
|
}
|
|
|
|
func NewNotifierClient(addr, apiKey string) (*NotifierClient, error) {
|
|
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &NotifierClient{
|
|
client: pb.NewNotifierServiceClient(conn),
|
|
apiKey: apiKey,
|
|
}, nil
|
|
}
|
|
|
|
func (nc *NotifierClient) SendNotification(ctx context.Context, req *pb.SendNotificationRequest) (*pb.SendNotificationResponse, error) {
|
|
// Add API key to context metadata
|
|
md := metadata.New(map[string][]string{
|
|
"authorization": {fmt.Sprintf("bearer %s", nc.apiKey)},
|
|
})
|
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
|
|
|
return nc.client.SendNotification(ctx, req)
|
|
}
|
|
|
|
func main() {
|
|
apiKey := os.Getenv("NOTIFIER_API_KEY")
|
|
client, err := NewNotifierClient("localhost:50051", apiKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
resp, err := client.SendNotification(context.Background(), &pb.SendNotificationRequest{
|
|
Type: pb.NotificationType_NOTIFICATION_TYPE_EMAIL,
|
|
Subject: "Hello",
|
|
Body: "World",
|
|
Recipients: []string{"user@example.com"},
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Printf("Notification sent: %s\n", resp.Result.NotificationId)
|
|
}
|
|
```
|
|
|
|
### Python Client
|
|
|
|
```python
|
|
import os
|
|
import grpc
|
|
from notifier.api.grpc import notifier_pb2, notifier_pb2_grpc
|
|
|
|
def send_notification(subject, body, recipients):
|
|
api_key = os.getenv("NOTIFIER_API_KEY")
|
|
|
|
# Create secure channel
|
|
channel = grpc.secure_channel("localhost:50051", grpc.ssl_channel_credentials())
|
|
stub = notifier_pb2_grpc.NotifierServiceStub(channel)
|
|
|
|
# Create metadata with API key
|
|
metadata = [("authorization", f"bearer {api_key}")]
|
|
|
|
# Send notification
|
|
request = notifier_pb2.SendNotificationRequest(
|
|
type=notifier_pb2.NOTIFICATION_TYPE_EMAIL,
|
|
subject=subject,
|
|
body=body,
|
|
recipients=recipients,
|
|
)
|
|
|
|
response = stub.SendNotification(request, metadata=metadata)
|
|
return response.result.notification_id
|
|
|
|
if __name__ == "__main__":
|
|
notif_id = send_notification(
|
|
"Hello",
|
|
"World",
|
|
["user@example.com"]
|
|
)
|
|
print(f"Notification sent: {notif_id}")
|
|
```
|
|
|
|
### Node.js/TypeScript Client
|
|
|
|
```typescript
|
|
import * as grpc from "@grpc/grpc-js";
|
|
import * as protoLoader from "@grpc/proto-loader";
|
|
import * as os from "os";
|
|
|
|
const NOTIFIER_API_KEY = os.getenv("NOTIFIER_API_KEY");
|
|
|
|
const packageDef = protoLoader.loadSync("notifier.proto", {
|
|
keepCase: true,
|
|
longs: String,
|
|
enums: String,
|
|
defaults: true,
|
|
oneofs: true,
|
|
});
|
|
|
|
const notifierProto = grpc.loadPackageDefinition(packageDef);
|
|
|
|
async function sendNotification(subject: string, body: string, recipients: string[]) {
|
|
// Create metadata with API key
|
|
const metadata = new grpc.Metadata();
|
|
metadata.set("authorization", `bearer ${NOTIFIER_API_KEY}`);
|
|
|
|
// Create client
|
|
const client = new (notifierProto.notifier.v1.NotifierService as any)(
|
|
"localhost:50051",
|
|
grpc.credentials.createInsecure()
|
|
);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
client.sendNotification(
|
|
{
|
|
type: "NOTIFICATION_TYPE_EMAIL",
|
|
subject,
|
|
body,
|
|
recipients,
|
|
},
|
|
metadata,
|
|
(err: any, response: any) => {
|
|
if (err) reject(err);
|
|
else resolve(response.result.notification_id);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
// Usage
|
|
sendNotification("Hello", "World", ["user@example.com"])
|
|
.then((notifId) => console.log(`Notification sent: ${notifId}`))
|
|
.catch((err) => console.error(err));
|
|
```
|
|
|
|
### cURL Examples
|
|
|
|
```bash
|
|
# Send email notification
|
|
curl -X POST http://localhost:8080/api/v1/notifications \
|
|
-H "Authorization: Bearer nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"type": "email",
|
|
"subject": "Alert",
|
|
"body": "Something happened",
|
|
"recipients": ["admin@example.com"]
|
|
}'
|
|
|
|
# Batch notifications
|
|
curl -X POST http://localhost:8080/api/v1/notifications/batch \
|
|
-H "Authorization: Bearer nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"notifications": [
|
|
{
|
|
"type": "email",
|
|
"subject": "Alert 1",
|
|
"body": "First alert",
|
|
"recipients": ["user1@example.com"]
|
|
},
|
|
{
|
|
"type": "slack",
|
|
"subject": "Alert 2",
|
|
"body": "Second alert",
|
|
"recipients": ["#alerts"]
|
|
}
|
|
]
|
|
}'
|
|
|
|
# Get notification status
|
|
curl -X GET http://localhost:8080/api/v1/notifications/{id} \
|
|
-H "Authorization: Bearer nk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
|
|
```
|
|
|
|
## Error Responses
|
|
|
|
### Authentication Failures
|
|
|
|
**REST API:**
|
|
|
|
```
|
|
401 Unauthorized
|
|
Missing or invalid Authorization header
|
|
|
|
403 Forbidden
|
|
Rate limit exceeded
|
|
|
|
401 Unauthorized
|
|
Invalid API key
|
|
|
|
401 Unauthorized
|
|
API key has expired
|
|
```
|
|
|
|
**gRPC:**
|
|
|
|
```
|
|
UNAUTHENTICATED: Missing or invalid Authorization header
|
|
UNAUTHENTICATED: Invalid API key
|
|
UNAUTHENTICATED: API key has expired
|
|
RESOURCE_EXHAUSTED: Rate limit exceeded
|
|
PERMISSION_DENIED: Insufficient permissions for this notifier
|
|
```
|
|
|
|
## Configuration Examples
|
|
|
|
### Example 1: Multi-Tenant Setup
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
default_rate_limit: 100
|
|
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
host: "smtp.example.com"
|
|
port: 587
|
|
username: "shared@example.com"
|
|
password: "${SMTP_PASSWORD}"
|
|
from: "notifications@example.com"
|
|
allowed_roles:
|
|
- "notify-all"
|
|
|
|
tenant-a:
|
|
host: "smtp.tenant-a.com"
|
|
port: 587
|
|
username: "notifications@tenant-a.com"
|
|
password: "${TENANT_A_SMTP_PASSWORD}"
|
|
from: "notifications@tenant-a.com"
|
|
allowed_roles:
|
|
- "tenant-a-notifications"
|
|
|
|
tenant-b:
|
|
host: "smtp.tenant-b.com"
|
|
port: 587
|
|
username: "notifications@tenant-b.com"
|
|
password: "${TENANT_B_SMTP_PASSWORD}"
|
|
from: "notifications@tenant-b.com"
|
|
allowed_roles:
|
|
- "tenant-b-notifications"
|
|
```
|
|
|
|
### Example 2: Restricted Access
|
|
|
|
```yaml
|
|
auth:
|
|
enabled: true
|
|
default_rate_limit: 50
|
|
|
|
notifiers:
|
|
smtp:
|
|
default:
|
|
host: "smtp.example.com"
|
|
port: 587
|
|
username: "user@example.com"
|
|
password: "${SMTP_PASSWORD}"
|
|
from: "noreply@example.com"
|
|
allowed_roles:
|
|
- "admin" # Only admins
|
|
- "email-service"
|
|
|
|
slack:
|
|
default:
|
|
webhook_url: "${SLACK_WEBHOOK}"
|
|
allowed_roles:
|
|
- "admin"
|
|
- "alerts" # Only alert systems
|
|
```
|
|
|
|
## Monitoring & Auditing
|
|
|
|
Authentication events are logged with the following information:
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2025-10-25T10:30:00Z",
|
|
"event": "auth_success",
|
|
"client_id": "billing-service",
|
|
"roles": ["notify-email", "notify-slack"],
|
|
"rate_limit_remaining": 95,
|
|
"endpoint": "/api/v1/notifications"
|
|
}
|
|
```
|
|
|
|
Authentication failures are also logged for security auditing:
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2025-10-25T10:31:00Z",
|
|
"event": "auth_failure",
|
|
"reason": "invalid_api_key",
|
|
"remote_addr": "192.168.1.100"
|
|
}
|
|
```
|
|
|
|
Monitor these logs for:
|
|
- Brute force attempts (multiple failed authentications from same IP)
|
|
- Unusual access patterns
|
|
- Rate limit violations
|
|
- Key expiration approaching
|
|
- Inactive keys being used
|
|
|
|
## Summary
|
|
|
|
1. **Enable auth** in config: `auth.enabled: true`
|
|
2. **Bootstrap admin key** on first deployment
|
|
3. **Create API keys** via REST API with appropriate roles and rate limits
|
|
4. **Configure role-based access** for each notifier
|
|
5. **Use environment variables** or secrets manager for key storage
|
|
6. **Monitor logs** and audit trails for security events
|
|
7. **Rotate keys regularly** and set expiration dates
|
|
8. **Use separate keys** for each service/application
|
|
|
|
## Related Documentation
|
|
|
|
For more detailed information on specific topics:
|
|
|
|
- **[KEY_MANAGEMENT.md](./KEY_MANAGEMENT.md)** - Complete guide to API key management
|
|
- Bootstrap mechanism
|
|
- Key creation via REST API
|
|
- Key listing, revocation, and rotation
|
|
- Audit logging
|
|
- Database persistence
|
|
- Kubernetes deployment
|
|
|
|
- **[RBAC.md](./RBAC.md)** - Role-Based Access Control (RBAC) guide
|
|
- Configuration patterns
|
|
- Authorization flow
|
|
- Restricting notifier access by role
|
|
- Testing authorization
|
|
- Security best practices
|
|
|
|
- **[RBAC_QUICKSTART.md](./RBAC_QUICKSTART.md)** - RBAC quick reference
|
|
- 60-second overview
|
|
- Common patterns
|
|
- Troubleshooting
|
|
|
|
- **[AUTH_QUICK_START.md](./AUTH_QUICK_START.md)** - Quick start guide
|
|
- Step-by-step setup
|
|
- Basic examples
|