platxa-secrets-management
Fernet encryption, Kubernetes secrets, and secure token patterns for Platxa services. Covers Python cryptography, K8s Secret objects, key rotation, and constant-time token verification.
What this skill does
# Platxa Secrets Management
Guide for secrets management patterns across Platxa services.
## Overview
| Component | Encryption | Storage | Pattern |
|-----------|------------|---------|---------|
| **Credentials** | Fernet (AES-128-CBC) | Odoo DB (encrypted) | Encrypt before store |
| **K8s Secrets** | Base64 + etcd encryption | K8s API | stringData/data fields |
| **Tokens** | N/A | Memory/Header | HMAC verification |
| **JWT Keys** | Fernet (RSA private) | DB | Rotate with grace period |
## Core Principles
1. **Defense in Depth**: Encrypt at rest, in transit, and minimize plaintext in memory
2. **Least Privilege**: Namespace-scoped secrets, RBAC for access control
3. **Key Rotation**: Regular rotation with grace periods for validation
4. **Audit Trail**: Log secret operations (never log values, only key names)
5. **No Plaintext Storage**: Never store secrets unencrypted in code, configs, or logs
## Fernet Encryption
### Key Generation
```python
from cryptography.fernet import Fernet
# Generate a new key (32 bytes, url-safe base64)
key = Fernet.generate_key() # b'...' (44 chars base64)
# Store key securely (environment, config parameter, vault)
# NEVER hardcode in source code
```
### Encryption/Decryption
```python
from cryptography.fernet import Fernet
def encrypt_value(value: str, key: bytes) -> str:
"""Encrypt a string value."""
f = Fernet(key)
encrypted = f.encrypt(value.encode())
return encrypted.decode() # Store as string
def decrypt_value(encrypted: str, key: bytes) -> str:
"""Decrypt a string value."""
f = Fernet(key)
decrypted = f.decrypt(encrypted.encode())
return decrypted.decode()
```
### Key Retrieval Pattern
```python
import base64
from odoo.tools import config
def _get_encryption_key(self) -> bytes:
"""Get encryption key from config with fallback."""
# Priority 1: Odoo config file
key = config.get('instance_manager_encryption_key')
# Priority 2: System parameter (database)
if not key:
key = self.env['ir.config_parameter'].sudo().get_param(
'instance_manager.encryption_key', ''
)
# Priority 3: Development fallback (WARNING: insecure)
if not key:
_logger.warning("No encryption key configured. Using default (INSECURE!)")
key = 'developmentonlykey32byteslong!!'
# Ensure proper Fernet format
if not key.endswith('='): # Not base64 encoded
key = base64.urlsafe_b64encode(key[:32].ljust(32).encode()).decode()
return key.encode()
```
## Kubernetes Secrets
### Create Secret with stringData
```python
from kubernetes import client
def create_secret(namespace: str, name: str, data: dict) -> None:
"""Create K8s secret using stringData (auto base64)."""
core_v1 = client.CoreV1Api()
secret = client.V1Secret(
metadata=client.V1ObjectMeta(
name=name,
namespace=namespace,
labels={'app.kubernetes.io/managed-by': 'platxa'}
),
string_data=data, # Plain text, K8s encodes automatically
type='Opaque'
)
core_v1.create_namespaced_secret(namespace, secret)
```
### Create Secret with Manual Base64
```python
import base64
def create_secret_manual(namespace: str, name: str, data: dict) -> None:
"""Create K8s secret with manual base64 encoding."""
core_v1 = client.CoreV1Api()
# Manually encode each value
encoded_data = {
k: base64.b64encode(v.encode()).decode()
for k, v in data.items()
}
secret = client.V1Secret(
metadata=client.V1ObjectMeta(name=name, namespace=namespace),
data=encoded_data, # Pre-encoded base64
type='Opaque'
)
core_v1.create_namespaced_secret(namespace, secret)
```
### Read Secret
```python
def read_secret(namespace: str, name: str) -> dict:
"""Read and decode K8s secret."""
core_v1 = client.CoreV1Api()
secret = core_v1.read_namespaced_secret(name, namespace)
# Decode base64 values
return {
k: base64.b64decode(v).decode()
for k, v in secret.data.items()
}
```
### TLS Secret (Certificate)
```python
def create_tls_secret(namespace: str, name: str, cert: str, key: str) -> None:
"""Create TLS secret for certificates."""
core_v1 = client.CoreV1Api()
secret = client.V1Secret(
metadata=client.V1ObjectMeta(name=name, namespace=namespace),
type='kubernetes.io/tls',
string_data={
'tls.crt': cert,
'tls.key': key,
}
)
core_v1.create_namespaced_secret(namespace, secret)
```
## Secure Token Generation
### Password Generation
```python
import secrets
import string
def generate_password(length: int = 32) -> str:
"""Generate cryptographically secure password."""
alphabet = string.ascii_letters + string.digits
return ''.join(secrets.choice(alphabet) for _ in range(length))
# Usage
db_password = generate_password(32)
admin_password = generate_password(24)
```
### API Token Generation
```python
import secrets
def generate_token(nbytes: int = 32) -> str:
"""Generate hex token for API authentication."""
return secrets.token_hex(nbytes) # 64 char hex string
def generate_urlsafe_token(nbytes: int = 32) -> str:
"""Generate URL-safe token."""
return secrets.token_urlsafe(nbytes) # ~43 char base64
```
### DNS Verification Token
```python
def generate_dns_token() -> str:
"""Generate token for DNS verification."""
return secrets.token_hex(16) # 32 char hex
```
## Token Verification
### Constant-Time Comparison
```python
import hmac
def verify_token(provided: str, expected: str) -> bool:
"""Verify token using constant-time comparison.
IMPORTANT: Never use == for token comparison (timing attack).
"""
return hmac.compare_digest(provided, expected)
```
### Bearer Token Middleware
```python
import hmac
from flask import request
def verify_bearer_auth() -> bool:
"""Verify Bearer token from Authorization header."""
auth_header = request.headers.get('Authorization', '')
if not auth_header.startswith('Bearer '):
return False
token = auth_header[7:] # Remove 'Bearer ' prefix
expected = get_expected_token() # From secure storage
if not expected:
return False # Secure by default
return hmac.compare_digest(token, expected)
```
## Key Rotation
### Rotation Strategy
| Phase | Duration | Old Key | New Key |
|-------|----------|---------|---------|
| Generate | Immediate | Active | Created (inactive) |
| Grace Period | 24 hours | Active | Available for decrypt |
| Activate | After grace | Inactive | Active |
| Cleanup | 7 days | Deleted | Active |
### Implementation Pattern
```python
def rotate_encryption_key():
"""Rotate encryption key with grace period."""
# 1. Generate new key
new_key = Fernet.generate_key()
new_key_id = generate_key_id()
# 2. Store new key (inactive)
store_key(new_key_id, new_key, is_active=False)
# 3. Schedule activation (after grace period)
schedule_key_activation(new_key_id, delay=timedelta(hours=24))
# 4. Re-encrypt existing data (background job)
schedule_reencryption(new_key_id)
return new_key_id
```
## Go Environment Secrets
### Config from Environment
```go
func getEnvString(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
// Usage - never log default values
apiKey := getEnvString("API_KEY", "")
if apiKey == "" {
log.Fatal("API_KEY environment variable required")
}
```
### In-Cluster Authentication
```go
// K8s automatically mounts service account token
// at /var/run/secrets/kubernetes.io/serviceaccount/token
config, err := rest.InClusterConfig()
if err != nil {
// Fallback to kubeconfig for local dev
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
}
```
## Workflow
### Step 1: Generate Encryption Key
```python
from cryptography.fernRelated in Cloud & DevOps
appbuilder-action-scaffolder
IncludedCreate, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.
orchestrating-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM/session tracing/parquet telemetry (use observing-agentforce), standard CRM SOQL (use querying-soql), or Apex implementation (use generating-apex).
github-project-automation
IncludedAutomate GitHub repository setup with CI/CD workflows, issue templates, Dependabot, and CodeQL security scanning. Includes 12 production-tested workflows and prevents 18 errors: YAML syntax, action pinning, and configuration. Use when: setting up GitHub Actions CI/CD, creating issue/PR templates, enabling Dependabot or CodeQL scanning, deploying to Cloudflare Workers, implementing matrix testing, or troubleshooting YAML indentation, action version pinning, secrets syntax, runner versions, or CodeQL configuration. Keywords: github actions, github workflow, ci/cd, issue templates, pull request templates, dependabot, codeql, security scanning, yaml syntax, github automation, repository setup, workflow templates, github actions matrix, secrets management, branch protection, codeowners, github projects, continuous integration, continuous deployment, workflow syntax error, action version pinning, runner version, github context, yaml indentation error
sf-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase `sf data360` workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching sf-datacloud-* skill), the task is STDM/session tracing/parquet telemetry (use sf-ai-agentforce-observability), standard CRM SOQL (use sf-soql), or Apex implementation (use sf-apex).
fabric-cli
IncludedUse this skill for Fabric.so CLI workflows with the `fabric` terminal command: diagnose/install/login, search or browse a Fabric library, save notes/links/files, create folders, ask the Fabric AI assistant, manage tasks/workspaces, generate shell completion, check subscription usage, produce JSON output, and use Fabric as persistent agent memory. Do not use for Microsoft Fabric/Azure/Power BI `fab`, Daniel Miessler's Fabric framework, Python Fabric SSH, Fabric.js, or textile/fashion fabric.
lark
IncludedLark/Feishu CLI skills: lark-cli operations for docs, markdown, sheets, base, calendar, im, mail, task, okr, drive, wiki, slides, whiteboard, apps, approval, attendance, contact, vc, minutes, event. Use when the user needs to operate Lark/Feishu resources via lark-cli, send messages, manage documents, spreadsheets, calendars, tasks, OKRs, deploy web pages, or any Feishu/Lark workspace operations.